キーフレームとカスタム
Keyframeクラスで複数の中間状態を持つ複雑な出現・消滅アニメーションを定義します。さらに、完全に制御できるカスタムアニメーション関数(EntryExitAnimationFunctionなど)の構造も扱います。
前々章のプリセットは、出発点から目標点までを1本の曲線で結ぶものでした。この章では、その途中にいくつも中継点を置いて、より込み入った動きを組み立てる方法を扱います。
道具は2つです。0から100までの時点ごとにスタイルを並べるKeyframeと、アニメーションの中身をworkletで丸ごと自分で書くカスタムアニメーションです。前者はwebでも動き、後者はネイティブ専用です。
キーフレーム
Keyframeは、アニメーションの進行度(0から100)ごとにスタイルを指定するクラスです。プリセットより自由度が高く、途中で向きを変えたり、いったん行き過ぎてから戻ったりといった動きを組めます。
import { Keyframe } from "react-native-reanimated";
const keyframe = new Keyframe({
0: {
transform: [{ rotate: "0deg" }],
},
45: {
transform: [{ rotate: "100deg" }],
easing: Easing.exp,
},
100: {
transform: [{ rotate: "45deg" }],
},
});
function App() {
return <Animated.View entering={keyframe} />;
}
キーは0から100の範囲で、アニメーションの進行度に対応します。0(またはfrom)が初期状態、100(またはto)が最終状態です。間の数値(この例では45)が中間状態にあたります。
各キーフレームにはスタイルに加えてイージング関数を書けます。イージングは「そのキーフレームに向かう区間」に適用されるので、区間の始点ではなく終点側に指定します。省略した場合はEasing.linearが使われます。
下のデモは、回転・スケール・透明度を組み合わせたキーフレームです。ボタンで要素を出し入れすると、いったん大きく回りながら現れて、最後に落ち着くまでの動きが再生されます。
モディファイア
Keyframeにも、これまでと同じ系統のモディファイアがあります。
keyframe
.duration(1000)
.delay(500)
.reduceMotion(ReduceMotion.Never)
.withCallback((finished) => {
console.log(`中断なく終了: ${finished}`);
});
.duration()の既定は500msです。ここで指定した時間が、0から100までの全体にかかる長さになります。個々のキーフレームの間隔は、キーの数値の差で決まります。
注意点
キーフレームを組むときに、いくつか守るべき点があります。
初期状態を表すキーフレーム0(またはfrom)は必須です。ここにアニメーションさせたいすべてのスタイルプロパティの初期値を書いておきます。他のキーフレームだけに登場して0にないプロパティがあると、初期値が定まらず正しく動きません。
0とfrom、あるいは100とtoを同時に指定するとパースが衝突するので、どちらか一方にします。イージングを0に書いてはいけないのも、区間の考え方から来る決まりです。
transformをアニメーションさせる場合は、すべてのキーフレームでtransform配列のプロパティの順序をそろえてください。順序がずれると補間が破綻します。
カスタムアニメーション
プリセットやキーフレームで足りないときは、アニメーションの中身を関数として丸ごと自分で書けます。出現・消滅・レイアウトトランジションのそれぞれにカスタム関数の形があります。ただし、公式も認めるとおり理解と保守が難しくなりがちなので、まずはプリセットやキーフレームで表現できないか検討してから使うのがよいでしょう。
カスタムアニメーション関数は、次の3つを返すworkletです。
function CustomAnimation(values) {
"worklet";
const animations = {
// 各プロパティをどうアニメーションさせるか
};
const initialValues = {
// アニメーションの初期値
};
const callback = (finished: boolean) => {
// アニメーション終了時のコールバック(省略可)
};
return {
initialValues,
animations,
callback,
};
}
関数はvalues引数を受け取ります。ここには、要素がどこにどんな大きさで表示されるか(あるいは表示されていたか)の情報が入っています。
出現アニメーション(EntryAnimationsValues)では、要素がこれから表示される先の情報としてtargetOriginX / targetOriginY / targetWidth / targetHeightなどが渡されます。消滅アニメーション(ExitAnimationsValues)では、要素が表示されていた場所の情報としてcurrentOriginX / currentOriginY / currentWidth / currentHeightなどが渡されます。レイアウトトランジション(LayoutAnimationsValues)では、その両方、つまり変化前(current系)と変化後(target系)の情報がまとめて渡されます。
これらの座標や寸法を使えば、たとえば「画面の左上から現在位置まで飛んでくる」といった、プリセットにない動きを自分で組み立てられます。animationsの各プロパティにはwithTimingやwithSpringをそのまま書けるので、コアAPIの知識がそのまま生きます。
カスタムアニメーションには1つ覚えておきたい挙動があります。あるプロパティに初期値を渡さずに新しいアニメーションを始めると、初期値はそのコンポーネントに最後に割り当てられたアニメーションの値から引き継がれます。例外は出現アニメーションで、こちらは前のアニメーションが存在しないため引き継ぎが効きません。
なおカスタムアニメーションは、公式のプラットフォーム対応表ではiOSとAndroidのみとされています。webでは動作しないので、この本ではデモを置かず、構造の説明にとどめています。実際に動かして確かめるときはネイティブで試してください。