CSS Transitions
スタイルの変化を自動でなめらかにつなぐCSSトランジションAPIを扱います。transition-*系5プロパティの値と注意点を、スタイル切り替えのデモとともにまとめます。
CSSトランジションは、スタイルプロパティの値が変わったときに、その変化をなめらかにアニメーションさせるAPIです。アニメーションを自分で駆動する代わりに、どのプロパティを・どれくらいの時間でアニメーションさせるかを宣言しておきます。あとは普段どおりにスタイルを更新すれば、Reanimatedが古い値と新しい値の間を勝手に補間してくれます。
前章のアニメーションが「定義したタイムラインの再生」だったのに対して、トランジションは「変化1回ごとのA→B」です。WebのCSSでtransitionを使ったことがあれば、そのままの感覚で書けます。
最初のトランジション
ボタンを押すたびに、サイズと色が変わる箱を作ります。手でアニメーションさせる処理はどこにもありません。stateにもとづいてwidthとbackgroundColorを変えるだけで、トランジションが残りを引き受けます。
<Animated.View
style={{
width,
backgroundColor,
transitionProperty: ["width", "backgroundColor"],
transitionDuration: 500,
}}
/>
普通のスタイル変更をなめらかな変化に変える材料は2つだけです。transitionPropertyが監視するスタイルプロパティをReanimatedに伝え、transitionDurationが変化にかける時間を決めます。
下のデモで試してみてください。propertyのセレクタをwidthだけに絞ると、色は瞬時に切り替わり、幅だけがアニメーションします。noneにすると両方とも即座に切り替わります。
しくみ
トランジションの発火条件は「監視しているプロパティの値がレンダリング間で変わること」です。つまり次の2つがそろって初めて動きます。
- スタイルが
Animated.ViewなどのAnimatedコンポーネントに適用されている - 新しい値が再レンダリングによって届く(典型的にはstateやpropsの変化)
すべてのスタイルプロパティがトランジションできるわけではなく、flexDirectionやjustifyContentのように離散的に切り替わるものもあります。対応の全リストは公式のSupported style propertiesを、離散プロパティの扱いは後述のtransitionBehaviorを参照してください。
プロパティ別リファレンス
5つのプロパティを順に見ていきます。いずれもiOS・Android・webのすべてで動作します。
前章のアニメーションと同じく、こちらも配列で複数のプロパティを個別に設定できます。transitionPropertyに配列を渡すと、以降の各プロパティも同じ並び順の配列で対応します。
transitionProperty: ["width", "transform", "borderRadius"],
transitionDuration: ["3s", "150ms", 500],
この例ではwidthに3秒、transformに150ミリ秒、borderRadiusに500ミリ秒のdurationが対応します。
transitionProperty
トランジションさせるスタイルプロパティの名前を指定します。
<Animated.View
style={{
transitionProperty: "width",
transitionDuration: 300,
}}
/>
値は4通りです。
'backgroundColor'のような文字列: 単一のプロパティを監視する['borderWidth', 'borderColor']のような配列: 複数のプロパティを監視するall: トランジション可能なすべてのプロパティを監視するnone: 何も監視しない
注意点がいくつかあります。トランジションを起こすにはtransitionDurationの指定が必須です(デフォルトの0のままでは瞬時に切り替わります)。監視対象のプロパティは、同じコンポーネントのスタイルに適用されている必要があります。またallはパフォーマンス問題につながりうるため、公式は使用を勧めていません。
flexDirectionやjustifyContentのような離散プロパティは、なめらかには補間できません。離散プロパティを含むレイアウトの変化をアニメーションさせたいときは、第4部で扱ったレイアウトアニメーションが使えます。なおreact-native-svgのpropsもトランジションできます(公式のAnimating SVG参照)。
transitionDuration
トランジションにかける時間を指定します。デフォルトは0です。
値は3通りの書き方があり、いずれも負の値は使えません。
'3s': 秒を表す文字列'150ms': ミリ秒を表す文字列500: ミリ秒を表す数値
transitionTimingFunction
変化中の中間値の計算方法、つまりイージングを指定します。デフォルトはeaseです。
指定できる値は前章のanimationTimingFunctionと共通です。linear・ease・ease-in・ease-out・ease-in-out・step-start・step-endの定義済み関数に加えて、cubicBezier()・linear()・steps()のパラメータつき関数を使えます。それぞれの意味は前章のリファレンスを参照してください。
import { steps } from "react-native-reanimated";
<Animated.View
style={{
transitionProperty: "height",
transitionDuration: 300,
transitionTimingFunction: steps(4, "jump-end"),
}}
/>
transitionDelay
トランジションが始まるまでの待ち時間を指定します。デフォルトは0です。
値の書き方はtransitionDurationと同じ3通り('3s'・'-150ms'・500)ですが、こちらは負の値も使えます。負のdelayを指定するとトランジションはすぐに始まり、その分だけ途中から再生されます。10秒のトランジションにtransitionDelay: '-5s'なら、半分進んだ状態からスタートします。
transitionBehavior
離散プロパティにもトランジションを適用するかを指定します。デフォルトはnormalで、連続的なプロパティだけをトランジションさせ、離散プロパティの変化は即座に適用します。
<Animated.View
style={{
transitionProperty: "alignItems",
transitionDuration: 500,
transitionBehavior: "allow-discrete",
}}
/>
normal: 離散プロパティにはトランジションを適用せず、変化を即座に反映するallow-discrete: 離散プロパティにもトランジションを適用し、変化のタイミングを遅らせる
allow-discreteにしても、離散プロパティの値がなめらかに補間されるわけではありません。値はトランジションの中間点で一方からもう一方へ切り替わります。例外はdisplayで、こちらはトランジション開始の時点で即座に切り替わります。
Hooks APIとの関係
トランジションが向いているのは、stateに駆動されるアニメーションです。メニューの開閉、セクションの展開と折りたたみ、テーマの切り替え、要素の表示と非表示。日常的なUIアニメーションの多くはここに収まり、公式ドキュメントもまずトランジションから始めることを勧めています。
宣言的に書けるぶんコードは短く、フックも共有値も登場しません。Webから来た人には馴染みのあるメンタルモデルで、アニメーション自体はReanimatedのコアがJSスレッドの外で実行します。どのプロパティが動くかをReanimatedが正確に把握できるので、内部最適化の余地が大きいAPIでもあります。
一方、フレーム単位の制御が必要な場面、つまりジェスチャーやスクロールに追従する動き、画面遷移、複数のアニメーションのオーケストレーションでは、第2部で学んだ共有値とuseAnimatedStyleの出番です。使い分けの考え方は第1部3章も参照してください。