Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

react-scroll で、 iOS の特定バージョン以降でも正しくアニメーションさせたい

こんにちは、 id:yunagi_n です。
本日の記事は React のお話です。

React で良い感じにスクロールしてくれるライブラリで、有名なものに react-scroll というものがあります。

これは、 JS からライブラリのメソッドを呼び出すことで、もしくは組み込みコンポーネントを使うことで、アニメーションさせながら自動的にその場所に行ってくれて便利なのですが、 iOS Safari でのみ発生するバグがあるので、そのワークアラウンドを紹介します。

バグの内容

以下のように、メソッド経由で ID 要素をつかってスクロールさせる場合、 iOS Safari の 15.4 以降でアニメーションされません (参照: fisshy/react-scroll#502)。
ただし、 Android や PC Chrome などでは正常に動作します。厄介ですね。

import React, { useCallback } from "react"
import { scroller } from "react-scroll"

const SomeComponent: React.FC = () => {
  const onClickScrollToItem = useCallback(() => {
    scroller.scrollTo("item", true)
  }, [])

  return (
    <div>
      // ... とても縦長な要素
      <div id="item">なにか</div>
    </div>
  )
}

ワークアラウンド

さすがに iOS 限定で動かない、というのも困るので、ワークアラウンドで回避しましょう。 といってもやり方は簡単で、単純に自前でスクロール位置を計算してあげれば良いのです。

import React, { useCallback, useRef } from "react"
import { animateScroll as scroller } from "react-scroll"

const SomeComponent: React.FC = () => {
  const elem = useRef<HTMLDivElement>(null)
  const onClickScrollToItem = useCallback(() => {
    if (elem.current) {
      const toY = elem.current.getBoundingClientRect().top + window.scrollY
      scroller.scrollTo(toY)
    }
  }, [])

  return (
    <div>
      // ... とても縦長な要素
      <div ref={elem}>なにか</div>
    </div>
  )
}

これで、 iOS Safari 15.4 以降はもちろん、その他のプラットフォームでも正常に動作するようになりました。 お疲れ様でした。