CSS Animations
キーフレームで定義したタイムラインを再生するCSSアニメーションAPIを扱います。animation-*系8プロパティの値と使いどころを、動かせるプレイグラウンドとともにまとめます。
CSSアニメーションは、スタイルのキーフレーム列を時間に沿って再生するAPIです。次章で扱うトランジションが「ある値からある値への1回の変化」をアニメーションさせるのに対して、アニメーションは自分で定義したタイムラインに従います。複数のステップを経由でき、繰り返しができ、マウントと同時に勝手に動きはじめます。
Webで@keyframesを書いたことがあれば、考え方はそのままです。第1部3章では、このAPIとHooksベースのコアAPIの使い分けを紹介しました。この章はその詳細編として、アニメーションを形づくるanimation-*系の8プロパティをまとめて扱います。

CSS Animationは定義したタイムラインを再生し、CSS Transitionは状態が変わったときに前後を補間します。
なお、この章と次章のデモはすべて本書のweb環境で実際に動作します。手元のスマートフォンで動かすときも同じコードが使えます。
最初のアニメーション
ずっと脈打ち続ける箱を作ってみます。各時点での見た目をキーフレームとして定義し、1周期の長さをReanimatedに伝えるだけです。
import Animated from "react-native-reanimated";
const pulse = {
from: {
transform: [{ scale: 0.8 }, { rotateZ: "-15deg" }],
},
to: {
transform: [{ scale: 1.2 }, { rotateZ: "15deg" }],
},
};
function App() {
return (
<Animated.View
style={{
...styles.box,
animationName: pulse,
animationDuration: "1s",
animationIterationCount: "infinite",
animationTimingFunction: "ease-in-out",
animationDirection: "alternate",
}}
/>
);
}
ボタンもstateもありません。コンポーネントがマウントされた瞬間にアニメーションが始まり、animationIterationCountが'infinite'なので動き続けます。
しくみ
アニメーションの記述は2つの部分に分かれます。
1つはキーフレームです。animationNameに、オフセット(from、to、あるいは50%のようなパーセント)と、その時点でのスタイルを対応づけたオブジェクトを渡します。もう1つはタイミングで、animationDurationやanimationIterationCount、animationDirectionといったプロパティが、そのタイムラインの再生のしかたを決めます。あとはReanimatedがキーフレームの間を補間して再生してくれます。
animationNameという名前のプロパティにオブジェクトを渡すのは、最初は奇妙に見えるかもしれません。これはWebのCSSに由来する名前です。Webでは@keyframesルールに名前をつけてグローバルなスタイルシートに宣言し、animation-nameプロパティからその名前を参照します。React Nativeには名前つきルールを置くグローバルなスタイルシートがないので、名前の代わりにキーフレームのオブジェクトそのものを渡す設計になっています。
すべてのスタイルプロパティがアニメーションできるわけではありません。対応の全リストは公式のSupported style propertiesにまとまっています。
プレイグラウンド
主要なプロパティを操作しながら、挙動の違いを確かめてみてください。左端から中央でふくらんで右端へ抜ける3キーフレームのアニメーションに対して、時間・方向・繰り返し回数・イージングを切り替えられます。ボタンでanimationPlayStateをpausedにすると、その場でぴたりと止まり、runningに戻すと続きから再開します。
一時停止以外の設定を変えたときは、デモの実装上、要素を作り直して最初から再生し直しています。directionやiterationCountの違いは、再生の頭から観察するのが分かりやすいためです。
プロパティ別リファレンス
ここからは8つのプロパティを順に見ていきます。いずれもiOS・Android・webのすべてで動作します。
なおAnimated.Viewは複数のアニメーションを同時に持てます。animationNameにキーフレームオブジェクトの配列を渡すと、以降の各プロパティも同じ並び順の配列で個別に指定できます。
animationName: [bounceIn, move, slide],
animationDuration: ['3s', '150ms', 500],
この例ではbounceInに3秒、moveに150ミリ秒、slideに500ミリ秒のdurationが対応します。以降の各プロパティの説明では配列形式の記述は省略しますが、すべて同じ規則で配列を受け取れます。
animationName
アニメーションのキーフレームを指定します。
<Animated.View
style={{
animationName: {
"100%": {
transform: [{ translateX: 100 }],
},
},
animationDuration: "300ms",
}}
/>
キーフレームオブジェクトのキーには次の書き方が使えます。
<percentage>:0から100の数値に%をつけた文字列from:0%の別名to:100%の別名<number>:0から1の浮動小数点数
値には、その時点でのスタイルプロパティを書きます。キーフレームはインラインで直接渡しても、変数に切り出して複数のコンポーネントで使い回してもかまいません。noneを渡すとアニメーションなしになります。
いくつか覚えておきたい挙動があります。キーフレームは最低1つあれば動き、足りない側は要素の現在の状態が最初のキーフレームとして使われます。先ほどの例にfromがないのはそのためです。また複数のアニメーションが同じプロパティを対象にした場合は、配列の後ろにあるものが前の変更を上書きします。react-native-svgのコンポーネントもアニメーションできます(公式のAnimating SVG参照)。
animationDuration
アニメーション1回分の長さを指定します。デフォルトは0です。
値は3通りの書き方があり、いずれも負の値は使えません。
'3s': 秒を表す文字列'150ms': ミリ秒を表す文字列500: ミリ秒を表す数値
animationTimingFunction
進行中の中間値の計算方法、つまりイージングを指定します。デフォルトはeaseです。
文字列で指定できる定義済み関数は7つです。
linear: 最初から最後まで一定速度ease: ゆっくり始まり、速くなって、ゆっくり終わるease-in: ゆっくり始まって加速するease-out: 速く始まって減速するease-in-out: ゆっくり始まり、速くなり、またゆっくりになるstep-start: 開始時に一気に飛ぶstep-end: 終了時に一気に飛ぶ
より細かく制御したいときは、パラメータつきの関数をreact-native-reanimatedからimportして使います。
import { cubicBezier, linear, steps } from "react-native-reanimated";
animationTimingFunction: cubicBezier(0.25, 0.1, 0.5, 2),
animationTimingFunction: linear(0, [0.25, "75%"], 1),
animationTimingFunction: steps(4, "jump-end"),
cubicBezier(x1, y1, x2, y2)は2つの制御点で進行カーブを描くベジェ曲線です。linear(...points)はx=0で始まりx=1で終わる折れ線で、各点のy座標だけを並べるか、[0.25, '75%']のようにx座標を添えて指定します。steps(段数, modifier)は指定した段数の階段状に進みます。modifierはjump-start(開始時に最初のジャンプ)・jump-end(終了時に最後のジャンプ)・jump-none(両端でジャンプせず、両端で1/nずつ停止)・jump-both(両端に停止を挟み、途中の段が1つ増える)と、その別名start・endから選び、デフォルトはjump-endです。
イージングはアニメーション全体だけでなく、キーフレームごとにも指定できます。キーフレーム内に書いたイージングは「そのキーフレームから次のキーフレームへ向かう区間」に適用され、そのプロパティを指定する後続のキーフレームがなければ最後まで効き続けます。したがって最後のキーフレーム(100%、to、1)に書いたイージングは、続く区間がないため無視されます。
const square = {
"0%": {
transform: [{ translateX: 0 }],
animationTimingFunction: cubicBezier(0.25, 0.1, 0.26, 1.53),
},
"100%": {
transform: [{ translateX: -80 }],
animationTimingFunction: "linear", // 🚨 これは無視される
},
};
animationDelay
アニメーションが始まるまでの待ち時間を指定します。デフォルトは0です。
値の書き方はanimationDurationと同じ3通り('3s'・'-150ms'・500)ですが、こちらは負の値も使えます。負のdelayを指定するとアニメーションはすぐに始まり、その分だけ途中から再生されます。たとえば10秒のアニメーションにanimationDelay: '-5s'を指定すると、ちょうど半分の位置からスタートします。
animationIterationCount
アニメーションを繰り返す回数を指定します。デフォルトは1です。
'infinite'を渡すと永遠に繰り返します。数値は0以上であれば整数でなくてもよく、たとえば0.5を渡すと周期の半分だけ再生されます。
animationDirection
アニメーションを再生する向きを指定します。デフォルトはnormalです。
normal: 順方向に再生するreverse: 逆方向に再生するalternate: 繰り返しのたびに順方向と逆方向を交互に切り替えるalternate-reverse: 交互に切り替えるが、1回目を逆方向から始める
alternate系はanimationIterationCountとの組み合わせで生きるプロパティです。プレイグラウンドでinfiniteと組み合わせると、行って戻る動きの違いがよく分かります。
animationFillMode
アニメーションの開始前と終了後に、キーフレームから計算されたスタイルをどう残すかを指定します。デフォルトはnoneです。
none: 終了後、キーフレームから計算されたスタイルを破棄するforwards: 最後のキーフレームのスタイルを維持するbackwards: 最初のキーフレームのスタイルを維持するboth: 最初と最後の両方のキーフレームのスタイルを維持する
実際にどのスタイルが残るかはanimationDirectionとanimationIterationCountにも依存します。たとえばreverseで終わった場合、最後に通るキーフレームはfrom側です。
animationPlayState
アニメーションの再生と一時停止を切り替えます。デフォルトはrunningです。
running: アニメーションを再生するpaused: アニメーションを一時停止する
値をstateで持って切り替えれば、プレイグラウンドのボタンのように「その場で止めて、続きから再開する」動きになります。停止位置はReanimatedが覚えているので、自分で進行度を管理する必要はありません。
Hooks APIとの関係
CSSアニメーションが向いているのは、それ自体で完結する宣言的な動きです。ローディングスピナー、脈打つバッジ、注意を引くループなど、外からの入力なしに自分から動き続けるものは、この章のAPIで素直に書けます。公式ドキュメントも、こうしたアニメーションの多くでCSS APIを最初の選択肢として勧めています。
一方、ジェスチャーやスクロールに追従する動きや、生きた値からフレーム単位で組み立てる動きには、第2部で学んだuseAnimatedStyleと共有値が向いています。使い分けの全体像は第1部3章で扱ったとおりです。
stateの変化に反応して1回だけ動かしたい場合は、キーフレームよりもトランジションが適しています。次章で扱います。