在使用 Gesture 手势库的时候,你需要注意区分 UI 线程和 JavaScript 线程。Gesture 手势库和 Reanimated 动画库搭配使用时,Gesture 的手势回调函数是在 Reanimated 动画库创建的 UI 线程的 JavaScript 虚拟机中执行的。
但 UI 线程的 JavaScript 虚拟机和 JavaScript 线程的 JavaScript 虚拟机是两个不同的虚拟机。UI 线程的 JavaScript 虚拟机只有执行相关动画、手势回调函数的上下文,而 JavaScript 线程的 JavaScript 虚拟机拥有的是执行页面渲染的上下文,因此手势回调函数必须调用 runOnJS 回到 JavaScript 线程的 JavaScript 虚拟机中执行 setState 渲染页面。
我把使用 Gesture 手势库实现轻按手势的完整示例代码放在这里了,你可以仔细看下:
import React, {useState} from 'react';
import { Text, View, SafeAreaView} from 'react-native';
import {runOnJS} from 'react-native-reanimated';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
export default function App() {
const [logs, setLogs] = useState<string[]>([]);
const singleTap = Gesture.Tap()
// 渲染
.onStart(() => runOnJS(setLogs)(logs.concat('开始触发轻按事件')));
// 动画或日志
// .onStart(() => console.log('开始触发轻按事件'));
return (
<SafeAreaView>
<GestureDetector gesture={singleTap}>
<View style={[{width: 100,height: 100,backgroundColor: 'red'}]} />
</GestureDetector>
{logs.map((log, index) => (
<Text key={index}>{log}</Text>
))}
</SafeAreaView>
);
}
拖拽动效
要实现的拖拽动效是这样的:你可以拖动屏幕上的一个圆形视图,让跟随你的手指一起移动。并且,在你触碰到该圆形视图时,圆形视图是蓝色的,当你手指离开该圆形视图时,圆形视图是灰色的。
代码如下:
function Ball() {
const isPressed = useSharedValue(false);
const offset = useSharedValue({ x: 0, y: 0 });
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [
{ translateX: offset.value.x },
{ translateY: offset.value.y },
],
backgroundColor: isPressed.value ? 'blue' : '#ccc',
};
});
const dragGesture = Gesture.Pan()
.onBegin(() => {
isPressed.value = true;
})
.onChange((e) => {
offset.value = {
x: e.changeX + offset.value.x,
y: e.changeY + offset.value.y,
};
})
.onFinalize(() => {
isPressed.value = false;
});
return (
<GestureDetector gesture={dragGesture}>
<Animated.View style={[styles.ball, animatedStyles]} />
</GestureDetector>
);
}
export default function Example() {
return (
<View style={styles.container}>
<Ball />
</View>
);
}