如何在Reanimated 2中检测动画对象与其他对象或边界的碰撞?
可缩放、拖拽、旋转动画对象的碰撞检测方案建议
核心思路:基于动画共享值实时计算边界框
由于Reanimated动画运行在UI线程,无法依赖JS线程的measure、onLayout这类异步方法,核心方案是直接通过动画控制的共享值(平移x/y、缩放scale、旋转rotate)实时计算对象的实际边界框,再与目标对象或边界的边界框做碰撞判断。
具体实现步骤
预存原始尺寸
在组件初始化时用onLayout获取对象未做任何变换的原始宽高,存入普通变量或状态中(原始尺寸不会随动画变化):const [originalSize, setOriginalSize] = useState({ width: 0, height: 0 }); const handleLayout = (e) => { const { width, height } = e.nativeEvent.layout; setOriginalSize({ width, height }); };实时计算变换后的边界框
用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 }; });实时碰撞检测
在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
相关产品推荐
相关产品推荐

