React Native中如何在Gesture Handler的onStart内调用函数?
问题:手势处理器onStart回调调用函数导致应用崩溃
每次在手势处理器的onStart回调内调用函数时,应用都会崩溃。我已经尝试使用runOnJS、runOnUI,添加'worklet'标识,但都无效。请帮忙修改代码解决这个问题。
const pullingToMatchGestureHandler = useAnimatedGestureHandler({ onStart: () => { animateShakeValue(); // <----- 这里导致应用崩溃 }, onActive: (event) => { const direction = 0; const distance = Math.sqrt( Math.pow(event.translationX, 2) + Math.pow(event.translationY, 2) ); if (event.translationX > 0 && -event.translationY > 0) { direction = Math.atan(-event.translationY / event.translationX); } else if (event.translationX < 0 && -event.translationY > 0) { direction = Math.PI + Math.atan(-event.translationY / event.translationX); } else if (event.translationX < 0 && -event.translationY < 0) { direction = Math.PI + Math.atan(-event.translationY / event.translationX); } else if (event.translationX > 0 && -event.translationY < 0) { direction = 2 * Math.PI + Math.atan(-event.translationY / event.translationX); } let rotationInRad = (rotation.value * Math.PI) / 180; rotationInRad = -rotationInRad + Math.PI / 2; while (rotationInRad < 0) { rotationInRad = rotationInRad + 2 * Math.PI; } const differenceAngle = rotationInRad - direction; if ( -distance * Math.cos(differenceAngle) > 0 && -distance * Math.cos(differenceAngle) < window.width / 5 ) pullDistance.value = -distance * Math.cos(differenceAngle) * 2.5; }, onEnd: (event) => { pullDistance.value = 0; }, }); const animateShakeValue = () => { const EASING = Easing.elastic(1.5); runOnJS(console.log)("animate"); rotation.value = withSequence( // deviate left to start from -ANGLE withTiming(-10, { duration: 100 / 2, easing: EASING }), // wobble between -ANGLE and ANGLE 7 times withRepeat( withTiming(10, { duration: 100, easing: EASING, }), 7, true ), // go back to 0 at the end withTiming(0, { duration: 100 / 2, easing: EASING }) ); };
解决方案
核心问题
animateShakeValue未标记为worklet函数:useAnimatedGestureHandler的回调运行在UI线程,调用的函数必须是worklet,否则会触发跨线程调用错误。direction变量声明错误:原代码用const声明后又重新赋值,属于语法错误,也是崩溃的潜在诱因。
修改后的完整代码
import { Easing, runOnJS, useAnimatedGestureHandler, useSharedValue } from 'react-native-reanimated'; // 确保共享变量已正确初始化 const rotation = useSharedValue(0); const pullDistance = useSharedValue(0); const pullingToMatchGestureHandler = useAnimatedGestureHandler({ onStart: () => { animateShakeValue(); }, onActive: (event) => { let direction = 0; // 改为let允许重新赋值 const distance = Math.sqrt( Math.pow(event.translationX, 2) + Math.pow(event.translationY, 2) ); if (event.translationX > 0 && -event.translationY > 0) { direction = Math.atan(-event.translationY / event.translationX); } else if (event.translationX < 0 && -event.translationY > 0) { direction = Math.PI + Math.atan(-event.translationY / event.translationX); } else if (event.translationX < 0 && -event.translationY < 0) { direction = Math.PI + Math.atan(-event.translationY / event.translationX); } else if (event.translationX > 0 && -event.translationY < 0) { direction = 2 * Math.PI + Math.atan(-event.translationY / event.translationX); } let rotationInRad = (rotation.value * Math.PI) / 180; rotationInRad = -rotationInRad + Math.PI / 2; while (rotationInRad < 0) { rotationInRad += 2 * Math.PI; } const differenceAngle = rotationInRad - direction; const calculatedDistance = -distance * Math.cos(differenceAngle); if (calculatedDistance > 0 && calculatedDistance < window.width / 5) { pullDistance.value = calculatedDistance * 2.5; } }, onEnd: () => { pullDistance.value = 0; }, }); // 添加worklet标记,确保函数运行在UI线程 const animateShakeValue = () => { 'worklet'; const EASING = Easing.elastic(1.5); runOnJS(console.log)("animate"); rotation.value = withSequence( withTiming(-10, { duration: 50, easing: EASING }), withRepeat( withTiming(10, { duration: 100, easing: EASING, }), 7, true ), withTiming(0, { duration: 50, easing: EASING }) ); };
关键修改点
- 给
animateShakeValue添加'worklet'指令,让Reanimated识别它为UI线程函数,避免跨线程调用崩溃。 - 将
const direction = 0改为let direction = 0,修复重新赋值的语法错误。 - 提取
calculatedDistance变量,减少重复计算,提升代码可读性。 - 补充了共享变量的初始化代码,确保动画状态正确绑定。
内容的提问来源于stack exchange,提问作者Rémi-Antoine Joron
相关产品推荐
相关产品推荐

