animation
reactnative

ReactNativeでシーケンスアニメーションを使う

複数のアニメーションから成る連続的な動き、
例えばA地点とB地点があり、両地点を移動するA->B->A->B….と移動し続けるアニメーションを実装した際の記録です。

やりたいこと

ユーザーに対して、この画面に来たらまずここをタップして欲しいんだよ!という意図を汲み取ってもらうため、矢印の画像が上下にふにふにと移動し続けるようなアニメーションを作りたいです。

実装方法

アニメーションを作成する際はまず動きを一方方向への動きに分割します。
今回の矢印が上下に移動し続けるアニメーションはこの2つに分割します。
1)下方向へ移動
2)上方向へ移動

この2つの動きを順番に、連続的に実行すれば目的の挙動を実現することができます。

ReactNativeではアニメーションを実装する際にはAnimatedライブラリを使用します。

ReactNative Animated

初期値

アニメーションで使用する値の初期値はAnimated.Valueをインスタンス化すます。
moveValue: new Animated.Value(0)

座標(x軸,y軸)で値を作りたい場合はAnimated.ValueXY()を使います。
これによりアニメーションのタイプなどの情報を持ち、interpolate()関数*などに対応することができます。

*interpolate():入力範囲をさまざまな出力範囲にマッピングできる

下のコードでは座標の値をstateで持ち、
Animated.Viewのpositionのtop属性の値としています。

constructor(props: any) {
  super(props);
  this.state = {
    moveValue: new Animated.Value(0),
  };
}
<Animated.Image style={[styles.emphasisArrow, { top:this.state.moveValue }]} source={require('../../../img/triangleArrow.png')} />

連続するアニメーション

Animated.sequenceを使います。
Animated.timingでthis.state.moveValueをどの値に、どのくらいの時間をかけて変化させるかを記述し、それを下方向に移動するものと、上方向に移動するものを用意し、sequence内に実行したい順番で記述します。
start関数によりこの一連のアニメーションが順番に問題なく実行されると、アニメーション実行結果が戻り値として返ってきます。
この結果を見て、正常に完了(event.finished:true)した場合にアニメーション関数を回帰的に実行しています。

cycleAnimation() {
  Animated.sequence([
    // 矢印がy軸に対してプラス(画面上方向)へ移動
    Animated.timing(this.state.moveValue, {
      toValue: 0,
      duration: 150,
    }),
    // 矢印が初期値からy軸に対してマイナス(画面下方向)へ移動
    Animated.timing(this.state.moveValue, {
      delay: 1000,
      toValue: -20,
      duration: 150,
    }),
  ]).start((event) => {
    if (event.finished) {
      this.cycleAnimation();
    }
  });
}

アニメーションの後処理

アニメーションはComponentWillUnmountでアニメーション用のstateに対してstopAnimation関数を実行します。
これをしないとアニメーションを実装した画面を離れてもずっとアニメーションの関数は裏側で走り続けることになります。

componentWillUnmount() {
  this.state.moveValue.stopAnimation();
}

初期値と後処理はシーケンスアニメーションを実装する際にひっかかるポイントだなと思ったのでまとめてみました。
特に後処理は実装しなくても挙動に問題はなかったので(気持ち悪いので気がついた時点で修正しました)、気がつかないで自分も後処理抜けてる!という方もいらっしゃるかもしれません。

ReactNativeでアニメーションを実装する際の参考になれば幸いです。