Programmer's Note

コード読み書きの備忘録。

React NativeのAnimated を試す

React Nativeのアニメーションのテスト。

Animated関連のAPIを使用するようで、この仕組みのイメージが少し分かった。

特定のカーブに従って何かしらの値を変化させて、 その変化をAnimatedなComponentで拾うという感じ。

変化させる値はAnimated.Value()関数で作る。

  percent = new Animated.Value(0);

この値を時間経過によって変化させる。これをアニメーションと言っていて、Animated.timing( ... ).start()を使う。

  startAnime = () => {
    Animated.timing(this.percent, {
      toValue: len,
      duration: 10*1000,
      useNativeDriver: true,
      easing: Easing.linear,
    }).start();
  }

toValueは最終的に持っていきたい値。 durationはどれくらいの時間をかけて変化させるか。msec単位。

iOSの場合useNativeDriver:指定は必須のようで、ないと怒られる。 trueにできない場合もあるので、そのときはfalseで。 easingは値を変化されるカーブを指定できるが、ここではリニアに設定。

あとは、アニメーションさせたい対象。 標準では、

  • Animated.Image
  • Anmated.ScrollView
  • Animated.Text
  • Animated.View
  • Animated.FlatList
  • Animated.SectionList

が用意されている。これらのComponentの任意のpropをアニメーション対象に指定できる、ということのようだ。

さて、ここではsvgライブラリを使ってCircleオブジェクトをアニメーション対象としたい。 この場合は、

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

という風にすれば、Animated 対象となる。

単なるCircleを使ってもだめだった。 Animated.Valueの変化を観察してくれない(つまり、動かない)。

最初は、Animated.Xxxがなぜ必要か分からなかったが、値の変化を拾ってくるかどうか、ということね。

逆に言えば、Animated.createAnimatedComponent()を使えてば、なんでもAnimated対象になるのなら、最強な気がする。

ここではCircle

            strokeDashoffset={this.percent}

というpropがAnimated.Valueを使っている。この値の変化を観察してくれるというわけだ。

以下、全コード。

import React, { Component } from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Button,
  Animated,
  Easing,
} from 'react-native';
import Svg, {
  Circle
} from 'react-native-svg';

const RADIUS = 45;
const len = RADIUS * 2 * Math.PI;
const AnimatedCircle = Animated.createAnimatedComponent(Circle);

class App extends Component {
  percent = new Animated.Value(0);

  startAnime = () => {
    Animated.timing(this.percent, {
      toValue: len,
      duration: 10*1000,
      useNativeDriver: true,
      easing: Easing.linear,
    }).start();
  }

  render() {
    return (
      <SafeAreaView style={styles.container}>
        <Svg height='50%' width='50%' viewBox='0 0 100 100'>
          <Circle
            cx='50'
            cy='50'
            r={`${RADIUS}`}
            stroke='blue'
            strokeWidth='1'
            strokeDasharray={[len, len]}
            strokeDashoffset={this.percent}
          />
          <AnimatedCircle
            cx='50'
            cy='50'
            r={`${RADIUS}`}
            stroke='blue'
            strokeWidth='10'
            strokeDasharray={[len, len]}
            strokeDashoffset={this.percent}
          />
        </Svg>
        <Button title='Start Anime' onPress={this.startAnime} />
      </SafeAreaView >
    );
  }
}

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default App;

補足

動作させるには、 https://github.com/react-native-community/react-native-svg が必要で、インストール方法は上記を参照。

Animated.Valueの一番簡単なサンプルは、React Nativeの標準ドキュメントにある。 https://reactnative.dev/docs/animated

参考

https://www.youtube.com/watch?v=Y50CQfyFkGI&t=402s