Skip to content
React Native Reanimated ガイドブック
Esc
navigateopen⌘Jpreview
On this page

スクロール連動アニメーション

useAnimatedScrollHandler・useScrollOffset・scrollToを使い、スクロール位置に連動する動きと、プログラムからのスクロールを組み立てます。

前章のジェスチャーと並んで、もう一つよく使う「生きた値」がスクロール位置です。リストをどこまでスクロールしたかに応じてヘッダーを縮めたり、プログレスバーを伸ばしたり。こうした表現は、スクロール量を共有値として受け取り、そこからスタイルを組み立てることで実現します。

Reanimatedはスクロールを扱う3つの道具を用意しています。スクロールイベントを受け取るuseAnimatedScrollHandler、現在のオフセットを共有値として取り出すuseScrollOffset、そしてコードからスクロールを動かすscrollToです。順に見ていきます。

ScrollViewのonScrollからscrollYを取り出して複数の見た目へ反映する図

スクロール位置は共有値として見た目へ流れ、scrollToは反対向きにスクロール位置を操作します。

スクロールイベントを受け取る

useAnimatedScrollHandlerは、スクロールイベント用のハンドラーを返すフックです。React Nativeのスクロール可能なコンポーネントに接続して使います。

const offsetY = useSharedValue(0);

const scrollHandler = useAnimatedScrollHandler((event) => {
  offsetY.value = event.contentOffset.y;
});

返ってきたハンドラーは、onScrollプロパティに渡します。このとき、接続する側はAnimatedでラップされたコンテナでなければなりません。ただのScrollViewではなくAnimated.ScrollViewを使う、という点に注意してください。

<Animated.ScrollView onScroll={scrollHandler}>{/* ... */}</Animated.ScrollView>

引数のhandlersには、ネイティブのイベント名に対応するキーを持つオブジェクトを渡せます。使えるキーはonScrollonBeginDragonEndDragonMomentumBeginonMomentumEndの5つです。それぞれの値は個別のworkletで、対応するイベントが発火したときに呼ばれます。

上のコードのように、オブジェクトではなく単一のworklet((event) => void)を渡した場合は、それがonScrollのハンドラーとして扱われます。

各workletはeventcontextを受け取ります。eventはスクロール情報を持つオブジェクトで、そのcontentOffset.yから縦方向のスクロール量が読めます。contextはイベントをまたいで状態を保持できるプレーンなJSオブジェクトで、複数のハンドラーを登録したときは、それらの間で共有されます。

第2引数のdependenciesは省略可能です。これが関係するのは、Babelプラグインを使わないWeb環境だけです。本書のデモもこの構成なので、workletが読む共有値を依存配列に明示しています。

次のデモは、受け取ったスクロール量をuseAnimatedStyleに流し込み、スクロールに合わせてヘッダーの高さと色を変えています。下へスクロールするとヘッダーが縮みます。

デモを読み込み中…

スクロール量を共有値として取り出す

イベントを自分で受け取らず、現在のオフセットだけがほしいこともあります。そのときはuseScrollOffsetが便利です。スクロール可能なコンポーネントのオフセットを、そのまま共有値として返してくれます。

const animatedRef = useAnimatedRef<ScrollView>();
const scrollOffset = useScrollOffset(animatedRef);

引数には、スクロール要素に接続したアニメーテッドrefを渡します。このrefは、AnimatedコンポーネントかReact Nativeの組み込みコンポーネントに渡す必要があります。フックはスクロールが縦か横かを自動で判別するので、向きを気にせず使えます。

第2引数のprovidedOffsetは省略可能で、更新先の共有値を自分で渡したいときに使います。省略すると、内部で作られた共有値が返ります。戻り値は、現在のオフセットを保持する共有値です。

次のデモは、取り出したオフセットをinterpolateでプログレスバーの幅に変換しています。スクロールに合わせてバーが伸び縮みします。

デモを読み込み中…

useScrollOffsetScrollViewだけでなくFlatListFlashListでも使えます。また、渡すrefはあとから差し替えても、フックは接続先に応じた値を正しく返します。

プログラムからスクロールする

ここまではユーザーのスクロールに反応する側でしたが、逆にコードからスクロールを動かしたい場面もあります。scrollToは、指定したXまたはYのオフセットへ同期的にスクロールします。

const animatedRef = useAnimatedRef();
const scroll = useSharedValue(0);

useDerivedValue(() => {
  scrollTo(animatedRef, 0, scroll.value, true);
});

引数は4つです。animatedRefはスクロール要素に接続したアニメーテッドref、xは横方向、yは縦方向のスクロール先(いずれもピクセル)、そしてanimatedはスクロールをなめらかにするか(true)、一瞬で移動するか(false)を決めます。戻り値はありません。

次のデモは、ボタンを押すと共有値scrollが変わり、それを監視するuseDerivedValueの中でscrollToが呼ばれ、対応する位置までスクロールします。

デモを読み込み中…

scrollToAnimated.FlatListにも対応します。内部でScrollViewを使っていてAnimated化されたコンポーネントであれば、たいてい動きます。

この章のまとめ

  • useAnimatedScrollHandlerはスクロールイベント用のハンドラーを返します。Animated.ScrollViewonScrollに渡して使います
  • useScrollOffsetは、スクロール要素の現在のオフセットを共有値として返します。向きは自動で判別されます
  • scrollToはコードからスクロール位置を動かします。UIスレッドから呼び出す必要があります

スクロール位置もジェスチャーと同じく、共有値を経由してスタイルへ流し込むのが基本の形です。次章では、キーボードやセンサーといったデバイス由来の値、そしてアクセシビリティ設定への対応を扱います。

この章のもとになった公式ドキュメント