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

CSS Animations

キーフレームで定義したタイムラインを再生するCSSアニメーションAPIを扱います。animation-*系8プロパティの値と使いどころを、動かせるプレイグラウンドとともにまとめます。

CSSアニメーションは、スタイルのキーフレーム列を時間に沿って再生するAPIです。次章で扱うトランジションが「ある値からある値への1回の変化」をアニメーションさせるのに対して、アニメーションは自分で定義したタイムラインに従います。複数のステップを経由でき、繰り返しができ、マウントと同時に勝手に動きはじめます。

Webで@keyframesを書いたことがあれば、考え方はそのままです。第1部3章では、このAPIとHooksベースのコアAPIの使い分けを紹介しました。この章はその詳細編として、アニメーションを形づくるanimation-*系の8プロパティをまとめて扱います。

複数のキーフレームを繰り返すCSS Animationと状態間をつなぐCSS Transitionの比較図

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に、オフセット(fromto、あるいは50%のようなパーセント)と、その時点でのスタイルを対応づけたオブジェクトを渡します。もう1つはタイミングで、animationDurationanimationIterationCountanimationDirectionといったプロパティが、そのタイムラインの再生のしかたを決めます。あとはReanimatedがキーフレームの間を補間して再生してくれます。

animationNameという名前のプロパティにオブジェクトを渡すのは、最初は奇妙に見えるかもしれません。これはWebのCSSに由来する名前です。Webでは@keyframesルールに名前をつけてグローバルなスタイルシートに宣言し、animation-nameプロパティからその名前を参照します。React Nativeには名前つきルールを置くグローバルなスタイルシートがないので、名前の代わりにキーフレームのオブジェクトそのものを渡す設計になっています。

すべてのスタイルプロパティがアニメーションできるわけではありません。対応の全リストは公式のSupported style propertiesにまとまっています。

プレイグラウンド

主要なプロパティを操作しながら、挙動の違いを確かめてみてください。左端から中央でふくらんで右端へ抜ける3キーフレームのアニメーションに対して、時間・方向・繰り返し回数・イージングを切り替えられます。ボタンでanimationPlayStatepausedにすると、その場でぴたりと止まり、runningに戻すと続きから再開します。

デモを読み込み中…

一時停止以外の設定を変えたときは、デモの実装上、要素を作り直して最初から再生し直しています。directioniterationCountの違いは、再生の頭から観察するのが分かりやすいためです。

プロパティ別リファレンス

ここからは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つ増える)と、その別名startendから選び、デフォルトはjump-endです。

イージングはアニメーション全体だけでなく、キーフレームごとにも指定できます。キーフレーム内に書いたイージングは「そのキーフレームから次のキーフレームへ向かう区間」に適用され、そのプロパティを指定する後続のキーフレームがなければ最後まで効き続けます。したがって最後のキーフレーム(100%to1)に書いたイージングは、続く区間がないため無視されます。

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: 最初と最後の両方のキーフレームのスタイルを維持する

実際にどのスタイルが残るかはanimationDirectionanimationIterationCountにも依存します。たとえばreverseで終わった場合、最後に通るキーフレームはfrom側です。

animationPlayState

アニメーションの再生と一時停止を切り替えます。デフォルトはrunningです。

  • running: アニメーションを再生する
  • paused: アニメーションを一時停止する

値をstateで持って切り替えれば、プレイグラウンドのボタンのように「その場で止めて、続きから再開する」動きになります。停止位置はReanimatedが覚えているので、自分で進行度を管理する必要はありません。

Hooks APIとの関係

CSSアニメーションが向いているのは、それ自体で完結する宣言的な動きです。ローディングスピナー、脈打つバッジ、注意を引くループなど、外からの入力なしに自分から動き続けるものは、この章のAPIで素直に書けます。公式ドキュメントも、こうしたアニメーションの多くでCSS APIを最初の選択肢として勧めています。

一方、ジェスチャーやスクロールに追従する動きや、生きた値からフレーム単位で組み立てる動きには、第2部で学んだuseAnimatedStyleと共有値が向いています。使い分けの全体像は第1部3章で扱ったとおりです。

stateの変化に反応して1回だけ動かしたい場合は、キーフレームよりもトランジションが適しています。次章で扱います。

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