レイアウトトランジションとリスト
layoutプロパティとLinearTransitionなどのトランジションで、要素の位置やサイズの変化をなめらかに補間します。リストのitemLayoutAnimationとLayoutAnimationConfigによるスキップも扱います。
前章の出現・消滅は、要素がツリーに追加・削除される瞬間の動きでした。この章で扱うのはその中間、すでにある要素の位置やサイズが変わったときの動きです。
項目を1つ削除するとまわりの要素が詰まる、並び順が変わって要素が入れ替わる。こうしたレイアウトの変化を、瞬間的なジャンプではなくなめらかな移動として見せるのがレイアウトトランジションです。
layoutプロパティ
レイアウトトランジションは、Animated.Viewのlayoutプロパティにトランジションを渡すだけで有効になります。要素のサイズや位置が変わると、Reanimatedが変化の前後を比べて、その差を自動で補間します。

追加時はentering、配置変更時はlayout、削除時はexitingが、それぞれ変化の前後をつなぎます。
import Animated, { LinearTransition } from "react-native-reanimated";
function App() {
return <Animated.View layout={LinearTransition} />;
}
自分で変化量を計算する必要はありません。Reanimatedがレイアウトの前後を測って、位置とサイズを目標値までアニメーションさせます。
下のデモは4つのタイルを並べたものです。トランジションを選び、項目を追加したりタイルをタップして削除したりすると、残ったタイルが選んだトランジションで移動します。出現と消滅には前章のFadeIn / FadeOutを組み合わせています。
トランジションの種類
Reanimatedには、動きの異なる複数のトランジションが用意されています。それぞれ位置とサイズの補間のしかたが違います。
LinearTransition— 位置とサイズを同じように補間する。もっとも素直な動きSequencedTransition— 先にX位置と幅、続いてY位置と高さの順に動かす。.reverse()で順序を逆にできるFadingTransition— 元の位置で消えて、新しい位置で現れる。透明度で切り替えるJumpingTransition— 新しい位置へ跳ねるように移動するCurvedTransition— X・Y・幅・高さのそれぞれに別々のイージングを指定でき、移動の軌跡が曲線を描く
CurvedTransitionは軸ごとにイージングを分けられるのが特徴です。次のように.easingX()と.easingY()へ別のイージングを渡すと、まっすぐではなくカーブを描いて移動します。
CurvedTransition.easingX(Easing.sin).easingY(Easing.exp);
トランジションのモディファイア
トランジションも、前章のプリセットと同じくモディファイアで調整できます。.duration()と.delay()はどのトランジションでも使え、.reduceMotion()や.withCallback()も共通です。
LinearTransitionは.springify()でバネベースにもできます。バネの指定は物理ベース(.mass()と.damping())と時間ベース(.duration()と.dampingRatio())のどちらかで、両者は混在できません。両方を渡した場合は時間ベースが優先されます。
LinearTransition.springify()
.damping(30)
.mass(5)
.stiffness(10);
前章と同じく、バネベースのモディファイアはwebでは反映されません。この本のweb環境のデモでは時間ベースのトランジションを使っています。
SequencedTransitionには順序を反転する.reverse()が、EntryExitTransitionには出現と消滅のアニメーションを指定する.entering()と.exiting()があります。トランジションごとに固有のモディファイアがあるので、詳しくは公式ドキュメントのレイアウトトランジションを参照してください。
リストに適用する
Animated.FlatListには、項目のレイアウト変化にトランジションを適用するitemLayoutAnimationプロパティがあります。リストの並びが変わったとき、各項目がそのトランジションで移動します。
import Animated, { LinearTransition } from "react-native-reanimated";
function App() {
return (
<Animated.FlatList
data={data}
renderItem={renderItem}
itemLayoutAnimation={LinearTransition}
/>
);
}
いくつか注意点があります。itemLayoutAnimationが効くのは単一列のAnimated.FlatListだけで、numColumnsを2以上にすると使えません。トランジションは実行中に差し替えたり、undefinedにして無効にしたりできます。
また、リストの項目がkeyもidも持たない場合は、keyExtractorを自分で用意して各項目に一意なキーを返す必要があります。FlatListは既定でこのキーをもとに項目を対応づけるので、キーが安定していないとトランジションが正しく走りません。
アニメーションをスキップする
出現・消滅アニメーションは、要素がマウント・アンマウントされるたびに走ります。ただ、画面を最初に開いたときにまで全項目が一斉にフェードインすると、かえってうるさく感じることがあります。こうした場面ではLayoutAnimationConfigで特定のタイミングのアニメーションをスキップできます。
import { LayoutAnimationConfig } from "react-native-reanimated";
function App() {
const [show, setShow] = useState(true);
return (
<LayoutAnimationConfig skipEntering>
<View>
{show && <Animated.View entering={PinwheelIn} exiting={PinwheelOut} />}
</View>
</LayoutAnimationConfig>
);
}
skipEnteringを付けると、LayoutAnimationConfigがマウントされた時点での子要素の出現アニメーションがスキップされます。同じようにskipExitingは、アンマウント時の消滅アニメーションをスキップします。最初の表示だけ静かにして、その後の追加・削除ではアニメーションを見せる、といった制御ができます。
LayoutAnimationConfigは入れ子にでき、一部の要素だけスキップ対象にできます。FlatListの場合は、リスト全体のマウント・アンマウント時のアニメーションをまとめてスキップするskipEnteringExitingAnimationsというショートカットもあります。これは内部で<LayoutAnimationConfig skipEntering skipExiting>でリストを包むのと同じです。
なおLayoutAnimationConfigは、公式のプラットフォーム対応表ではiOSとAndroidのみとされています。webでの動作は保証されていないので、スキップ制御が必要な場面ではネイティブで確認してください。