React Native Reanimated 2动画结束触发导航崩溃如何解决
崩溃根因
react-native-reanimated 搭配 react-native-gesture-handler 使用时,Gesture.Pan() 下的 onBegin/onUpdate/onEnd/onFinalize 回调默认运行在UI工作线程(Worklet),和React Native常规JS逻辑线程是隔离的。你左右滑动逻辑只修改Reanimated的sharedValue值,这类操作原生支持跨线程访问所以不会崩溃;但你在回调里直接调用React Navigation的navigate方法属于JS线程的API,直接跨线程调用就会触发应用崩溃。你之前尝试的清缓存、重装App都无法解决问题,因为这是API使用规则不符导致的逻辑错误,和环境无关。
修复方案
- 从
react-native-reanimated导入runOnJS工具方法,所有需要在Worklet线程调用JS侧逻辑(导航、console打印、状态更新等)的场景,都要用runOnJS包裹后再执行 - 修正你当前写反的上下滑判断逻辑:
y.value为正数代表手指向下滑动,为负数代表手指向上滑动,你当前代码判断y.value > windowHeight/4的分支注释写的是swiped up,逻辑完全颠倒 - 移除Worklet回调里直接写的
console.log,避免不必要的跨线程通信开销
核心修改代码
首先修改导入部分:
// 原来的Animated导入追加runOnJS import Animated, { useSharedValue, useAnimatedStyle, withSpring, runOnJS } from "react-native-reanimated";
然后修改Pan手势的onEnd回调部分:
.onEnd((e) => { const verticalSwipe = Math.abs(y.value) - Math.abs(x.value); // 原代码声明的horizontalSwipe变量未被使用,可以直接删除 if (verticalSwipe >= 0) { // 向下滑动超过屏幕1/4高度触发导航,可根据需求调整阈值 if (y.value > windowHeight / 4) { // 用runOnJS包裹JS侧的导航方法再调用 runOnJS(navigateToHome)(); } else { x.value = withSpring(startPosition); y.value = withSpring(startPosition); } } else { if (x.value > windowWidth / 4) { x.value = withSpring(windowWidth * 2); y.value = withSpring(startPosition) } else if (x.value < -windowWidth / 4) { x.value = withSpring(-windowWidth * 2); y.value = withSpring(startPosition); } else { x.value = withSpring(startPosition); y.value = withSpring(startPosition); } } })
额外优化建议
- 你代码里声明了
offset、start两个sharedValue但是完全没用到,可以删掉减少不必要的性能开销 useWindowDimensions不需要每次render重复调用三次,只需要调用一次解构出width和height即可- 如果后续要做下滑打开详情的卡片跟手动画,可以在onUpdate里判断垂直滑动占优时,给卡片加缩放、圆角变化的过渡效果,体验会更接近原生Tinder的交互
内容的提问来源于stack exchange,提问作者Damon Guzman
相关产品推荐
相关产品推荐

