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

如何在Reanimated 2中检测动画对象与其他对象或边界的碰撞?

可缩放、拖拽、旋转动画对象的碰撞检测方案建议

核心思路:基于动画共享值实时计算边界框

由于Reanimated动画运行在UI线程,无法依赖JS线程的measure、onLayout这类异步方法,核心方案是直接通过动画控制的共享值(平移x/y、缩放scale、旋转rotate)实时计算对象的实际边界框,再与目标对象或边界的边界框做碰撞判断。

具体实现步骤

  1. 预存原始尺寸
    在组件初始化时用onLayout获取对象未做任何变换的原始宽高,存入普通变量或状态中(原始尺寸不会随动画变化):

    const [originalSize, setOriginalSize] = useState({ width: 0, height: 0 });
    
    const handleLayout = (e) => {
      const { width, height } = e.nativeEvent.layout;
      setOriginalSize({ width, height });
    };
    
  2. 实时计算变换后的边界框
    用useDerivedValue基于动画共享值,计算旋转+缩放+平移后的对象四个顶点坐标,再推导边界框的最小/最大x、y值:

    import { useDerivedValue } from 'react-native-reanimated';
    
    // 假设已定义这些动画共享值
    const translateX = useSharedValue(0);
    const translateY = useSharedValue(0);
    const scale = useSharedValue(1);
    const rotate = useSharedValue(0); // 单位:弧度
    
    const objectBounds = useDerivedValue(() => {
      const rad = rotate.value;
      const scaledWidth = originalSize.width * scale.value;
      const scaledHeight = originalSize.height * scale.value;
      // 以对象中心为旋转中心,计算中心坐标
      const centerX = translateX.value + originalSize.width / 2;
      const centerY = translateY.value + originalSize.height / 2;
    
      // 旋转前的四个顶点相对中心坐标
      const halfW = scaledWidth / 2;
      const halfH = scaledHeight / 2;
      const points = [
        { x: -halfW, y: -halfH },
        { x: halfW, y: -halfH },
        { x: halfW, y: halfH },
        { x: -halfW, y: halfH },
      ];
    
      // 对顶点应用旋转变换后,平移到实际位置
      const transformedPoints = points.map(point => {
        const rotatedX = point.x * Math.cos(rad) - point.y * Math.sin(rad);
        const rotatedY = point.x * Math.sin(rad) + point.y * Math.cos(rad);
        return {
          x: rotatedX + centerX,
          y: rotatedY + centerY,
        };
      });
    
      // 推导边界框的最小/最大坐标
      const minX = Math.min(...transformedPoints.map(p => p.x));
      const maxX = Math.max(...transformedPoints.map(p => p.x));
      const minY = Math.min(...transformedPoints.map(p => p.y));
      const maxY = Math.max(...transformedPoints.map(p => p.y));
    
      return { minX, maxX, minY, maxY };
    });
    
  3. 实时碰撞检测
    在useAnimatedReaction里监听objectBounds的变化,实时与目标边界或其他对象的边界框做碰撞判断:

    import { useAnimatedReaction } from 'react-native-reanimated';
    
    useAnimatedReaction(
      () => objectBounds.value,
      (currentBounds) => {
        // 示例1:检测与屏幕边界碰撞(需提前获取screenWidth/screenHeight)
        const hitLeft = currentBounds.minX < 0;
        const hitRight = currentBounds.maxX > screenWidth;
        const hitTop = currentBounds.minY < 0;
        const hitBottom = currentBounds.maxY > screenHeight;
    
        // 示例2:检测与另一个对象的边界框碰撞(otherBounds为同方式计算的目标对象边界)
        const hitOther = !(
          currentBounds.maxX < otherBounds.minX ||
          currentBounds.minX > otherBounds.maxX ||
          currentBounds.maxY < otherBounds.minY ||
          currentBounds.minY > otherBounds.maxY
        );
    
        // 根据碰撞结果执行逻辑,比如限制对象移动
        if (hitLeft) {
          translateX.value = 0 - currentBounds.minX;
        }
      },
      [screenWidth, screenHeight, otherBounds]
    );
    

关键注意事项

  • 避免UI线程过载:若需检测多个对象碰撞,优先用轴对齐包围盒(AABB)简化计算,或仅在手势活跃时触发检测。
  • 确保原始尺寸准确:必须用初始化时的onLayout获取原始宽高,不要用动画过程中的measure,否则会导致计算偏差。
  • 屏幕边界获取:用Dimensions.get('window')或根组件onLayout获取准确的屏幕可用区域尺寸。

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 06:06:19