使用 useAnimatedSensor 打造基于设备传感器的 React Native 交互动画
2026/9/15 22:18:35 网站建设 项目流程

使用 useAnimatedSensor 打造基于设备传感器的 React Native 交互动画

【免费下载链接】react-native-reanimatedReact Native's Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated

导读useAnimatedSensor是 React Native Reanimated(v2.5.0 起提供)内置的传感器 Hook,让开发者可以基于陀螺仪、加速度计、重力、磁场与旋转矢量等设备传感器数据,直接在 UI 线程上驱动流畅的交互动画。本文将系统讲解它的完整 API、传感器类型与配置项、返回值的数据结构、底层实现原理与实战示例,帮助你快速掌握从传感器读数到动画渲染的完整链路。注意:本文基于当前仓库version-2.x的文档快照编写,不同大版本 API 可能存在差异。

一、useAnimatedSensor 是什么

在 React Native 应用中,设备传感器(如陀螺仪 Gyroscope、加速度计 Accelerometer)持续产生高频原始数据。传统做法是把这些数据桥接回 JS 线程再做处理,而 Reanimated 的思路是:传感器数据以 SharedValue 的形式直接写入 UI 线程,动画工作区(worklet)可以直接读取并驱动withTimingwithSpring等动画,省去了跨线程通信开销。

useAnimatedSensor的签名如下(见 Hook 源码):

useAnimatedSensor(sensorType: [SensorType], config?: [UserConfig]) -> [AnimatedSensor]
  • sensorType:必填,从SensorType枚举中选择要使用的传感器;
  • config:可选,用于定制传感器行为的配置对象(UserConfig);
  • 返回值:一个AnimatedSensor实例,包含传感器读数(SharedValue)、注销函数、可用性标志与配置。

从源码看,该 Hook 内部通过useMemo把用户配置与默认值合并,然后在useEffect中调用registerSensor完成注册,并在组件卸载时自动调用unregister清理(见 useAnimatedSensor.ts)。这意味着你无需手动管理传感器生命周期——组件卸载即自动释放。

二、传感器类型 SensorType 枚举

SensorType定义于 commonTypes.ts,当前支持 5 种传感器,前四种输出3DVectorROTATION输出RotationVector

枚举值输出类型单位/含义
ACCELEROMETER3DVector加速度,m/s²(不含重力)
GYROSCOPE3DVector角速度,rad/s
GRAVITY3DVector重力矢量,m/s²
MAGNETIC_FIELD3DVector磁场强度,μT(微特斯拉)
ROTATIONRotationVector旋转矢量(四元数 + 欧拉角)

ROTATION 传感器的特殊输出

ROTATION是功能最丰富的传感器,其RotationVector同时包含归一化四元数欧拉角两组表示:

  • [qx, qy, qz, qw]:归一化四元数(quaternion);
  • [yaw, pitch, roll]:绕各轴的旋转角,单位为弧度;iOS 上遵循 Apple 的 Core Motion 约定(参考文档),即 yaw 对应绕 z 轴(水平朝向角)、pitch 对应绕 x 轴(俯仰角)、roll 对应绕 y 轴(侧倾角)。

从 useAnimatedSensor.ts 可以看到,内部还通过eulerToQuaternionworklet 函数(参考 three.js 的 Quaternion 实现,欧拉角顺序为 ZXY)把欧拉角换算为四元数,保证两种表示始终一致。

三、配置项 UserConfig 详解

config参数的类型为UserConfig(源码中称SensorConfig,见 commonTypes.ts),包含三个字段:

配置项类型默认值说明
intervalnumber \| 'auto''auto'SharedValue 更新间隔(毫秒);传'auto'时按设备帧率选择间隔
iosReferenceFrameIOSReferenceFrameAutoiOS 上使用的参考坐标系
adjustToInterfaceOrientationbooleantrue是否根据当前屏幕方向校正测量值(例如横屏时 x/y 轴需要交换/取反才能在屏幕上正确呈现)

默认值合并逻辑见 useAnimatedSensor.ts。另外,Hook 会通过useRef对比新旧配置(intervaliosReferenceFrameadjustToInterfaceOrientation任一变化即视为配置变更),并在配置变化后重新注册传感器。

interval 的底层处理

在 Sensor.ts 中,interval会被透传给原生模块ReanimatedModule.registerSensor;当值为'auto'时实际传入-1,由原生层决定采样频率(通常对齐设备帧率),从而在流畅度与功耗之间取得平衡:

ReanimatedModule.registerSensor( sensorType, config.interval === 'auto' ? -1 : config.interval, config.iosReferenceFrame, eventHandler );

iosReferenceFrame 详解

IOSReferenceFrame枚举(见 commonTypes.ts)对齐 Apple Core Motion 的CMAttitudeReferenceFrame定义,仅对 iOS 生效:

含义
XArbitraryZVertical任意 X 轴、Z 轴垂直(不校正磁偏角)
XArbitraryCorrectedZVertical任意 X 轴(已做磁偏角校正)、Z 轴垂直
XMagneticNorthZVerticalX 轴指向磁北、Z 轴垂直
XTrueNorthZVerticalX 轴指向真北、Z 轴垂直
Auto自动选择:无磁力计的设备(如 iPod)用XArbitraryZVertical,有磁力计的设备用XArbitraryCorrectedZVertical

四、返回值 AnimatedSensor 详解

useAnimatedSensor返回一个AnimatedSensor对象(类型见 commonTypes.ts),包含四个属性:

{ sensor: SharedValue<Value3D | ValueRotation | null>, // 传感器实时读数 unregister: () => void, // 停止监听传感器更新 isAvailable: boolean, // 设备上传感器是否可用 config: UserConfig, // 用户提供的配置 }

数据结构的初始值与更新

Sensor类在初始化时会通过makeMutable创建 SharedValue,初始值取决于传感器类型(见 Sensor.ts):

  • ROTATION 传感器初始化为全 0 的RotationVector{ qw: 0, qx: 0, qy: 0, qz: 0, yaw: 0, pitch: 0, roll: 0, interfaceOrientation: 0 }
  • 其余传感器初始化为全 0 的Value3D{ x: 0, y: 0, z: 0, interfaceOrientation: 0 }

之后每当原生传感器产生新数据,就会触发注册的回调 worklet,把最新测量值写回sensorData.value(见 useAnimatedSensor.ts),从而自动驱动依赖它的动画 worklet。

三个数据类型的结构

3DVector(commonTypes.ts):

{ x: number; // X 轴分量 y: number; // Y 轴分量 z: number; // Z 轴分量 interfaceOrientation: InterfaceOrientation; // 当前设备方向 }

RotationVector(commonTypes.ts):

{ qw: number; // 四元数标量分量 qx: number; // 四元数向量分量 qy: number; qz: number; yaw: number; // 偏航角(弧度) pitch: number; // 俯仰角(弧度) roll: number; // 侧倾角(弧度) interfaceOrientation: InterfaceOrientation; // 当前设备方向 }

InterfaceOrientation枚举(commonTypes.ts)用于标记测量时的设备方向,Android 与 iOS 的对应关系如下:

AndroidiOS
ROTATION_0默认方向竖屏(portrait)
ROTATION_90旋转 90°横屏右向(landscape,Home 键在右侧)
ROTATION_180旋转 180°倒置(upside down)
ROTATION_270旋转 270°横屏左向(landscape,Home 键在左侧)

五、接口方向自动校正的原理

adjustToInterfaceOrientation: true(默认)时,测量值会在写入 SharedValue 之前按当前interfaceOrientation进行坐标系变换。以测试用例 sensors.test.ts 中验证的行为为例:

  • 3D 向量adjustVectorToInterfaceOrientation,见 useAnimatedSensor.ts):
    • ROTATION_90x = -y, y = x(x 轴与 y 轴交换并取反);
    • ROTATION_270x = y, y = -x
    • ROTATION_180xy同时取反;
    • ROTATION_0:不做变换。
  • 旋转矢量adjustRotationToInterfaceOrientation,见 useAnimatedSensor.ts):对pitch/roll/yaw做对应旋转变换后,再通过eulerToQuaternion重新计算四元数,保证欧拉角与四元数始终自洽。

该行为在 sensors.test.ts 中针对ROTATION_0 / 90 / 180 / 270四种方向都有精确断言(例如 3D 传感器在 90° 横屏下x: 1, y: 2会被校正为x: -2, y: 1)。

六、完整示例:用旋转传感器驱动视图缩放

下面是一个可直接运行的完整示例,它监听ROTATION传感器,并根据设备的 yaw(水平朝向)与 pitch(俯仰)动态改变黑色色块的高度和宽度:

import Animated, { useAnimatedSensor, useAnimatedStyle, SensorType, withTiming, } from 'react-native-reanimated'; function UseAnimatedSensorExample() { const animatedSensor = useAnimatedSensor(SensorType.ROTATION, { interval: 10, // 每 10ms 更新一次读数 }); // <- 初始化 const style = useAnimatedStyle(() => { const yaw = Math.abs(animatedSensor.sensor.value.yaw); const pitch = Math.abs(animatedSensor.sensor.value.pitch); return { height: withTiming(yaw * 200 + 20, { duration: 100 }), // <- 用法 width: withTiming(pitch * 200 + 20, { duration: 100 }), // <- 用法 }; }); return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Animated.View style={[{ backgroundColor: 'black' }, style]} /> </View> ); }

关键点说明:

  1. 读取位置animatedSensor.sensor.value必须在 worklet(如useAnimatedStyle的回调)内部读取,才能保证动画在 UI 线程上运行、不产生主线程卡顿;
  2. 可选平滑:如示例所示,可以把原始读数包一层withTiming/withSpring,避免传感器高频抖动导致的样式突变;
  3. 传感器不可用:返回的isAvailable标志可用于判断设备是否支持所选传感器,例如模拟器或缺少磁力计的低端设备上ROTATION可能不可用,此时应提供降级 UI。

七、传感器可用性、注销与多实例共享

可用性判断与注册失败

registerSensor返回-1表示传感器不可用(见 useAnimatedSensor.ts):此时isAvailablefalseunregister为 no-op,SharedValue 保持初始全 0 值。在 Web 端,JS 侧还会打印告警日志,提示传感器不可用、需要 HTTPS 安全上下文(https:)或 iOS Safari 需要额外授权(见 JSReanimated.ts)。

多实例共享:SensorContainer 的复用机制

SensorContainer(见 SensorContainer.ts)按sensorType * 100 + iosReferenceFrame * 10 + adjustToInterfaceOrientation生成传感器 ID,用于缓存和复用原生传感器实例。这意味着同一传感器配置被多个组件使用时,底层只会注册一个原生传感器,通过引用计数(listenersNumber)管理:最后一个监听者注销后才真正关闭原生传感器(见 SensorContainer.ts)。

生命周期管理

在组件卸载时,useEffect的清理函数会自动调用unregister(见 useAnimatedSensor.ts),开发者一般无需手动调用;但在特定场景(如传感器变化后想提前停止监听)可以主动调用返回的unregister函数。

八、测试验证:orientation 校正的行为基准

仓库中的 sensors.test.ts 是理解传感器行为的最佳"活文档",它用@testing-library/react-hooksrenderHook渲染 Hook、mock 掉core层的registerSensor,然后直接向事件回调注入模拟数据,验证四类行为:

  1. ROTATION 传感器原样透传(L60-L81):adjustToInterfaceOrientation: false时数据不做任何变换;
  2. 3D 传感器原样透传(L83-L100);
  3. 旋转传感器四种方向校正(L102-L187):覆盖ROTATION_0/90/180/270,并验证了校正后的四元数与欧拉角(含符号变化);
  4. 3D 传感器四种方向校正(L189-L253):验证 x/y 轴交换与取反规则。

此外,测试中还保留了旋转转换计算器的注释引用,方便你在调整自定义旋转逻辑时核对数值。

九、注意事项与平台差异

:::caution iOS 位置服务要求 在 iOS 上,若要读取传感器数据,需要先在设备上开启位置服务:设置 > 隐私 > 位置服务(Settings > Privacy > Location Services)。 :::

其余需要留意的平台差异:

  • Web 端:基于 W3C 通用传感器 API(AccelerometerGyroscope等)实现,要求页面处于安全上下文(HTTPS),iOS Safari 还要求额外的传感器权限授权(见 JSReanimated.ts);
  • 测试环境 / Jest mock:仓库在 mock.ts 中为useAnimatedSensor提供了 mock 实现,返回全 0 的传感器数据与 no-op 的unregister,方便在单元测试中稳定渲染组件;
  • interval: 'auto':按设备帧率采样,动画流畅度与电量消耗的均衡选择;需要更高采样率时显式传入毫秒数(如示例中的10)。

十、总结

useAnimatedSensor把设备传感器与 Reanimated 的 UI 线程动画体系无缝衔接:

  • 5 种传感器(加速度计、陀螺仪、重力、磁场、旋转矢量)通过SensorType枚举一键切换;
  • intervaliosReferenceFrameadjustToInterfaceOrientation三个配置项分别控制采样频率、iOS 参考坐标系与方向自动校正;
  • 返回值AnimatedSensor.sensor是 SharedValue,可在任意 worklet 中直接读取,配合useAnimatedStyle即可实现如"设备倾斜驱动元素形变/位移/透明度"的沉浸式交互;
  • 底层由SensorContainer统一管理原生传感器实例的注册、复用与引用计数释放,Hook 卸载时自动清理。

文中涉及的源码与测试均可直接在当前仓库中继续研读:Hook 实现、类型定义、原生桥接封装、实例管理、行为测试 以及 Web 实现。

【免费下载链接】react-native-reanimatedReact Native's Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询