React Native Animation
React Nativeには2種類のAnimationがあるようです。
- Animated API
- LayoutAnimation API
今回は Animated をAPI を使ってみたいと思います。
コンポーネントに Animation を適用する手順
コンポーネントに Animation を適用する手順を大まかに5つのステップにまとめると以下になります。
- Animated.Value で変化させたい値を定義する
- 値の変化を定義する
- Animation を開始する
- View の代わりに Animated.View を使う
- style に Animated.Value を設定する
1. Animated.Value で変化させたい値を定義する
new Animated.Value で 変化させたい値(媒介変数的なもの)を定義する。
まずは変化させたい値を定義します。ここでは x を作っています。
const x = useRef(new Animated.Value(0)).current;
この x は初期値に 0 が設定されています。
後で x の値の変化に従い style を変化させてコンポーネントを Animation させます。
2. 値の変化を定義する
Animated.timing 関数でCompositeAnimation のインスタンスを生成する。
x の現在の値(最初は初期値)をどこまで変化させるのか、変化に要する時間、一様に変化させるのか等を決めます。
ここでは、x を10秒間で 0 から 1 まで変化する CompositeAnimation のインスタンスを生成します。 CompositeAnimation は1回の Animation につき1回定義する ようにします。普通にコンポーネントの中で定義するとレンダリングされる度にCompositeAnimationが生成されてしまいます。これを回避するために useEffect 等で必要な時だけ生成するようにしましょう。
以下の例では、useEffect で x が生成された時(つまり初回レンダリング時)のみ CompositeAnimation を生成するようにしています。
useEffect(() => {
const compositAnimation = Animated.timing(
x,
{
toValue: 1,
duration: 10000,
}
);
}, [x])
ここで注意したいのが、 CompositeAnimation を生成しただけでは、x の変化は開始されません。 CompositeAnimation のメソッド start 関数を呼び出すと x の変化が開始します。
Animated.timing の仕様
値をeasing function に従って変化させる CompositeAnimation を生成する関数。
timing: (value: AnimatedValue | AnimatedValueXY, config: TimingAnimationConfig) => CompositeAnimation;
TimingAnimationConfig
変数 | 型 | 説明 |
---|---|---|
toValue | number AnimatedValue { x: number; y: number } AnimatedValueXY AnimatedInterpolation | 変化後の値 |
easing | (value: number) => number | パラメータの変化曲線。Easing function. Default: Easing.inOut Easing.ease, Easing.linearなどがある。 |
duration | number | 変化する時間(ms)。Default: 500 |
delay | number | 変化開始するまでの時間(ms)。Default: 0 |
isInteraction | boolean | Default: true |
useNativeDriver | boolean | Default: false |
3. Animation を開始する
Animation の開始:
compositAnimation.start()
以下のコードは x が作成された時(つまり最初のレンダリング時)だけ Animation が開始されます。useEffect の依存変数が x なので、x が変更されたときのみ compositeAnimation.start() が呼ばれますが、x は Animated.Value のインスタンスの参照なので初回レンダリング時に生成されて以降は変化しないからです。
ちなみに一度実行された compositeAnimation はもう一度 start しても何も起きないので注意が必要です。
useEffect(() => {
const compositeAnimation = ...;
compositeAnimation.start();
}, [x])
Animation を開始するタイミングをずらしたい場合は、何らかのフラグを用意する必要があります。フラグを状態に保存しておき、そのフラグを変化させることで Animation を開始することができます。
以下のコードは flag が true になったら Animation が開始する例です。
useEffect(() => {
if (flag) { // flagがtrueになったら開始する
const compositeAnimation = ...;
compositeAnimation.start();
}
}, [x, flag])
以下のようにするとフラグが変化する度に何度も Animation が開始されます。
useEffect(() => {
const compositeAnimation = ...;
compositeAnimation.start();
}, [x, flag])
4. Viewの代わりにAnimated.Viewを使う
変化する Animated.Value を View の style に設定して Animation を実現するのですが、通常の View コンポーネントの style には Animated.Value を使用することはできません。以下のように Animated.View を使用します。
<Animated.View>
...
</Animated.View>
5. styleにAnimated.Valueを設定する
最後に Animated.Value の style に Animated.Value を設定します。以下の例では、opacity に x を設定しています。opacity が一定時間で 0 から 1 に変化します。
<Animated.View style={{ opacity: x }}>
複数のAnimationを順番に実行させたり、同時に実行させたりする
順番に実行:
Animated.sequence([
Animated.timing(...),
Animated.decay(...),
Animated.delay(...),
Animated.spring(...),
...
]);
同時に実行:
Animated.parallel([
Animated.timing(...),
Animated.decay(...),
Animated.delay(...),
Animated.spring(...),
...
]);
sequence と parallel を組み合わせる:
Animated.sequence([
...,
Animated.parallel([
...,
]),
...
]);
Animated.Valueの演算:
演算 | 説明 |
---|---|
Animated.add | 足し算 |
Animated.subtract | 引き算 |
Animated.divide | 割り算 |
Animated.multiply | 掛け算 |
Animated.modulo | 剰余 |
Interpolation
interpolateメソッドを使うと Animated.Value の変化に連動する値を作成することができます。以下の例では x が 0 から1 まですると、traslateY が 150 から0 まで変化します。
const x = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(
x,
{
toValue: 1,
duration: 10000,
}
).start();
}, [x])
style={{
opacity: x,
transform: [{
translateY: x.interpolate({
inputRange: [0, 1],
outputRange: [150, 0] // 0 : 150, 0.5 : 75, 1 : 0
}),
}],
}}
変化の範囲はいくつも指定することができます。
value.interpolate({
inputRange: [-300, -100, 0, 100, 101],
outputRange: [300, 0, 1, 0, 0]
});
数字だけではなく、角度等を表現する文字列もサポートしています。
value.interpolate({
inputRange: [0, 360],
outputRange: ['0deg', '360deg']
});
Animation の終了を検知する
CompositeAnimation の start の引数に Animation の終了に呼ばれるコールバックを渡すことができます。
Animated.timing(...).start(({finished}) => {
// 終了時に呼ばれる
})
Animation できるコンポーネント
- View, Text, Image, ScrollView, FlatList, SectionList がデフォルトで対応しています。
- Animated.createAnimatedComponent で自作できるらしいです。