react-native-reanimated(reanimated2)とreact-native-gesture-handlerを用いたアニメーションについて、概念の理解などに苦しんだので備忘録。
素人ですので、間違えなどあればご指摘いただけると幸いです。
一番簡単なアニメーション(PanGesture)
import * as React from "react";
import { StyleSheet, View } from "react-native";
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
} from "react-native-gesture-handler";
interface ContextInterface {
translateX: number;
translateY: number;
};
export default function PanGestureScreen() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const panGestureEvent = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
ContextInterface
>({
onStart: (event, context) => {
// contextはgesture中に自在にアクセスできるobject 自分が必要な値をstart時にもたせて置くことで、他のeventの際にその値を参照できる
context.translateX = translateX.value;
context.translateY = translateY.value;
},
onActive: (event, context) => {
// gesutreスタート時の位置とtransltaion分の位置を足して現在の位置を算出する
translateX.value = event.translationX + context.translateX;
translateY.value = event.translationY + context.translateY;
},
onEnd: (event) => {}
});
const reanimatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: translateX.value,
},
{
translateY: translateY.value,
},
],
};
});
return (
<View style={styles.container}>
<PanGestureHandler onGestureEvent={panGestureEvent}>
// 以下の四角いViewがアニメーションする
<Animated.View style={[styles.square, reanimatedStyle]} />
</PanGestureHandler>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
circle: {
width: 200,
height: 200,
alignItems: "center",
justifyContent: "center",
borderRadius: 100,
borderWidth: 5,
borderColor: "rgba(0,0,256,0.5)",
},
});
動作例
#react native で gesture & animationを実装するために必要な概念
①JS thread と UI thread
react nativeでは大きく分けて上記の2つのthreadを考えることができる
UI thireadは60frames(コマ送り)/1秒 で動いており、JS threadとは非同期でやり取りしている。
このやり取りの中で、UI threadからanimationに関するframeの要求がJS threadに送られ、それをresponse するという動作が16.6(=1/60)ミリ秒以内に完結しないと、frame dropがおきてアニメーションがぎこちなくなってしまう(UXが低下してしまう)。
このような問題の対策としてreact-native-reanimatedとreact-native-gesture-handlerから提供されるapiがある。これらのapiは
全てのgestureとanimataionに関するメソッドなどをUI側で宣言して、2つのthreadでのやり取りをなくす
つまり、animationのeventが生じるだけではUI threadとJS threadは疎通しないのでスムーズにアニメーションできる
②よく出てくる用語やapiの考え方
- useSharedValue
- UI thread と JS thread でshareできる値を提供するapi js thread(react native)から由来するアニメーションを可能にする
- worklet
- 関数にこのディレクティブを指定すると その関数がコンパイルされてUI thireadに送られ、UI thiread側で実行できるようになる
- interpolate (和訳: 補間)
- shared valueをアニメーションしやすい値に補間するためのapi e.g. shared valueが0から1にアニメーションし、interpolateによって0から360に補間してrotateの角度をアニメーションさせる 他にも rgba表記のcolorな どもアニメーションせさられる。
参考
公式(reanimated2): https://docs.swmansion.com/react-native-reanimated/
公式(gesture-handler): https://docs.swmansion.com/react-native-gesture-handler/
worklet : https://www.youtube.com/watch?v=6dDpggVnZPo
william's tutorial at freeCodeCamp.org : https://www.youtube.com/watch?v=wEVjaXK4sYQ
https://chrizog.com/react-native-rotation-anchor-point