Expo SDK 40でReanimatedを使ってみたよ

2020/12/20に公開

この記事は React Native アドベントカレンダーの21日目の記事です。

これはなに

Expo SDK 40の個人的な目玉機能である「Reanimated v2-rc」について触ってみた、です。デモ付きです。

Expo SDK 40

Expoは、ReactNativeにおける開発を支援するフレームワークです。先日、今年最後のアップデートであるSDK40がリリースされました。SDK40のアップデートはこちらに公式のアナウンスがあります。SDK40では、「SDK 39から大きな変更はない」とのことですが、個人的に「Reanimated v2-rcが使える」ことが気になり、触ってみました。

つくったアプリ

今回は、アニメーションの動作を確認するために、ボタンを押したら絵文字が吹き出すアプリ👇を作りました。このアプリで、ReactNative標準のAnimatedのものと、Reanimated v2-rcをいれたアプリを比較しました。

SDK40 - 通常
標準のAnimatedをつかったアプリ

アニメーションのあるアプリをつくってみる

標準のAnimated

標準のAnimatedをつかってアニメーションを描画させるコード例を、ざっくり示します。公式ドキュメントはこちらです。「標準のAnimatedをつかったアプリ」は、このようなコードで作成しました。

/* AnimationIcon.tsx */
/* 標準ライブラリのAnimatedを使う */
import { StyleSheet, Animated, Easing, Text } from "react-native";

export class AnimationIcon extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      /* 絵文字の座標と、透明度を変化させます */
      animeY: new Animated.Value(0),
      animeX: new Animated.Value(0),
      animeOpacity: new Animated.Value(1),
    };
  }
  componentDidMount() {
    this._startAnimation();
  }
  componentWillUnmount() {
    this.state.animeX.stopAnimation(); // アニメーションの後処理
  }

  _startAnimation = () => {
    const toValueY = /* どこまでY座標を変化させるか */
    Animated.timing(this.state.animeY, {
      toValue: toValueY,
      duration,
      useNativeDriver: true,
      // アニメーションをどのように変化させたいか
      // https://reactnative.dev/docs/easing
      easing: Easing.inOut(Easing.linear), 
      delay: delay,
    }).start();
    
    /* 他の値もアニメーションを定義する */
  };

  render() {
    const icon = this.props.icon || "😊";
    return (
      <>
        <Animated.View
          style={[
            styles.icon,
            /* アニメーションで変化させる値をstyleにあてる */
            { opacity: this.state.animeOpacity },
            {
              transform: [
                { translateY: this.state.animeY },
                { translateX: this.state.animeX },
              ],
            },
          ]}
        >
          <Text style={styles.iconText}>{icon}</Text>
        </Animated.View>
        </>
    );
  }
}


react-native-reanimated対応

さて、上記で示した標準のAnimatedを使ったアプリをreact-native-reanimated対応させてみましょう。

Reanimatedとは

expo SDK 40公式アナウンスより、

Now with more bugfixes and improvements, the react-native-reanimated v2 release candidate is available in SDK 40!

とありますので、expo SDK 40ではReanimated v2-rc が使えるようです。追加するライブラリとしては、 react-native-reanimatedとなります。

React Native アドベントカレンダー 3日目の記事React Native 2020年の振り返りにもあるように、2020/11にReactNativeで対応されたものが、今回Expoでも対応されたということですね。ReactNativeのアニメーションの中でも特に、Androidのアニメーションは遅いので、このライブラリを使うと早くなるかを見てみます。

Reanimatedをいれる

ドキュメントReanimatedのとおりに、まずはライブラリの追加をしてみます。なんと、これだけです![1]

expo install react-native-reanimated

# yarnを使っている人向け
yarn add react-native-reanimated@2.0.0-rc.0

# babel.config.jsを編集し、
# plugins: ["react-native-reanimated/plugin"]を追加する
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: ["react-native-reanimated/plugin"],
  };
};

Reanimatedを使う

上記のとおり追加したライブラリをつかったアニメーションを実装してみます。

/* AnimationIcon.tsx */
/* 追加したReanimatedライブラリをつかう */
import { StyleSheet, Text } from "react-native";
import Animated, {
  Easing,
  useSharedValue,
  useAnimatedStyle,
  withTiming,
} from "react-native-reanimated";

/* Hooks対応しないと使えないようでした */
export const AnimationIcon: React.FC<Props> = ({ icon = "😊" }) => {
  const valueY = useSharedValue(/* 最終値 */);
  useEffect(() => {
    valueY.value = /* 初期値 */;
  }, []);

  const transformStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {
          translateY: withTiming(valueY.value, {
            duration: FADE_DURATION,
            easing: Easing.inOut(Easing.linear),
          }),
        },
        /* 省略 */
      ],
    };
  });

  const opacityStyle = useAnimatedStyle(() => {
  /* こちらも同様に */
  });

  return (
    <>
      {/* 上記で用意したReanimatedのスタイルを当てる */}
      <Animated.View style={[styles.icon, transformStyle, opacityStyle]}>
        <Text style={styles.iconText}>{icon}</Text>
      </Animated.View>
    </>
  );
};

DEMO - Androidによるアニメーションの比較

Reanimatedでつくったアプリと、標準のAnimatedをつかったアプリを比較してみましょう。どうでしょうか。

標準のAnimated Reanimatedを使った場合
SDK40 - 通常 SDK40+reanimated

ぱっと見、スムーズになった気がしますね!!!
特にボタンを押し始めたときの挙動が滑らかになったようですね。

計測する

上記のDemoでも表示していますが、パフォーマンスモニターをみてみます。
パフォーマンスモニターは、実機で見ている場合は端末を振ると表示できます。Androidのエミュレーターでは、ターミナルから adb shell input keyevent 82を実行すると開くことができます。
パフォーマンスモニターを開くと、画面右上に表示されます。その中でも、 JS xx fpsの数値が重要です。

パフォーマンスモニター

JS xx fpsの数値は、秒間にJSフレームレートを描画できた回数を表す数値[2]です。
通常この値は60であり、この値が小さいほど描画が「カクつく」ことになります。
この値を比較すると、数値的にも違いがあること・Reanimatedの方がより滑らかなこと、がわかります。

  • 標準のAnimated: 5 前後
  • Reanimated v2: 5-25 前後

まとめ

アニメーションを使う場合は、Reanimatedをつかうと動きが滑らかになることを確認できました。
ExpoでもSDK40で、簡単に対応できるようになっていました。

お知らせ

12/26〜の技術書典10で、「Firestore Testing −なぜテストを書くのか、どう書くのかがよくわかる!−」 という本を出版予定です。
こちらの👉👉サークルページ👈👈 に追加される予定です。お楽しみに!

脚注
  1. bare workflowの場合には、追加の作業がありますので、こちらのドキュメント通りの作業が必要ですが、今回は割愛します。 ↩︎

  2. パフォーマンスの指標について、詳しくはこちらのReactNativeドキュメントに記載されています。 ↩︎

Discussion