You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 })
  );
};

解决方案

核心问题

  1. animateShakeValue未标记为worklet函数:useAnimatedGestureHandler的回调运行在UI线程,调用的函数必须是worklet,否则会触发跨线程调用错误。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:44:57