资讯详情

useAnimatedProps 完全指南:用 React Native Reanimated 驱动第三方组件的原生属性动画

📅 2026/9/15 13:41:36 | 华诺云谱 👁 阅读
useAnimatedProps 完全指南:用 React Native Reanimated 驱动第三方组件的原生属性动画
useAnimatedProps 完全指南用 React Native Reanimated 驱动第三方组件的原生属性动画【免费下载链接】react-native-reanimatedReact Natives Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimateduseAnimatedProps是 Reanimated 提供的用于非样式类视图属性动画的 Hook它能在 UI 线程上响应 Shared Value 的变化直接更新原生视图的属性是动画化react-native-svg等第三方原生组件属性的核心手段。读完本文你将掌握useAnimatedProps的完整用法、白名单与 Prop Adapter 机制、底层属性更新流水线以及如何在 Jest 中验证动画结果。一、useAnimatedProps 是什么与 useAnimatedStyle 的定位差异Reanimated 为样式与属性提供了两条并行的动画通道useAnimatedStyle返回一个样式对象style作用于Animated组件的style属性useAnimatedProps返回一个属性对象props作用于Animated组件的animatedProps属性可更新视图上除样式以外的其他原生属性。正如原文档所述This hook is a counterpart ofuseAnimatedStylehook, but works for a non-style view properties。两者的使用姿势几乎一致传入一个 worklet 函数不必手动添加worklet指令Hook 内部会自动完成 worklet 转换该 worklet 返回一个包含目标视图属性的对象只要 worklet 内部引用了某个 Shared Value那么当该 Shared Value 更新时worklet 就会在 UI 线程重新执行并同步更新所连接的视图。唯一能通过useAnimatedProps设置的是原生视图的原生属性。对于 React Native 核心组件绝大多数值得动画化的属性本就属于样式可以直接用useAnimatedStyle因此useAnimatedProps最常见的应用场景是动画化第三方原生组件的属性——例如用react-native-svg绘制图形时动画化路径的d属性、填充色fill等。二、核心机制在 UI 线程响应 Shared Value 更新原生属性useAnimatedProps的运行机制可以概括为三个环节注册调用 Hook 时Reanimated 将传入的 worklet 注册为 mapper映射器并收集其闭包中引用的 Shared Value 作为依赖inputs触发任一被引用的 Shared Value 发生写入时mapper 被调度到 UI 线程执行回写worklet 返回的新属性对象通过原生通道直接下发到对应的原生视图无需经过 React 渲染周期。If the animated props worklet uses any shared values, it will be executed upon these values updates and the connected view will be updated.——这正是原文档对触发机制的描述属性更新完全绕开了 JS 侧的 React 重渲染动画过程中的每一帧都在 UI 线程完成从而获得稳定的 60fps 性能。从当前仓库源码看这一机制的实现非常直接useAnimatedProps内部本质上是useAnimatedStyle的一个特化调用。useAnimatedProps.ts 中useAnimatedPropsInternal将updater、deps、adapters原样透传给useAnimatedStyle并传入第四个标志位isAnimatedProps truefunction useAnimatedPropsInternalProps extends object( updater: () Props, deps?: DependencyList | null, adapters?: | AnimatedPropsAdapterFunction | AnimatedPropsAdapterFunction[] | null ) { return (useAnimatedStyle as UseAnimatedStyleInternalProps)( updater, deps, adapters, true ); }而 useAnimatedStyle.ts 在isAnimatedProps为真时会以属性模式启动styleUpdater把 worklet 返回值作为属性而非样式来处理。这正是属性动画与样式动画共享同一套 mapper 基础设施、只在最终下发阶段分道扬镳的架构事实。三、连接视图animatedProps 属性与 createAnimatedComponentuseAnimatedProps的返回值本身只是一个普通对象要让它生效必须把它传给Animated 版本组件的animatedProps属性Animated.View animatedProps{animatedProps} /这里的animatedProps属性不是 React Native 内置的而是在组件被Animated.createAnimatedComponent包裹时由 Reanimated 注入的。TheanimatedPropsproperty is added when a native component is wrapped withAnimated.createAnimatedComponent.——也就是说Animated.View、Animated.Text这些核心组件以及你手动用createAnimatedComponent包装的第三方组件都会自动支持animatedProps。从源码看animatedProps的合并发生在 PropsFilter.tsx。过滤器采用两轮处理策略第一轮跳过animatedProps键第二轮把animatedProps中的初始值展开合并进 props从而保证animatedProps提供的属性始终优先于 JSX 中同名的内联属性且与 JSX 书写顺序无关。// Second pass: apply animatedProps last so it always wins over inline // props that share a key. This makes the precedence deterministic and // independent of the order in which attributes were written in JSX. const animatedPropsProp inputProps.animatedProps; if (animatedPropsProp) { // ...展开 animatedProps 中的属性并合并到 props }在使用第三方原生组件如react-native-svg的Path、Circle等时需要先用createAnimatedComponent包装出 Animated 版本const AnimatedPath Animated.createAnimatedComponent(Path);四、白名单机制addWhitelistedNativeProps 与 addWhitelistedUIPropsReanimated 默认只允许动画化一部分已知属性其他属性需要登记进白名单才能被 UI 线程更新。原文档针对 2.x 版本给出了两个入口addWhitelistedNativeProps()用于登记会触发布局重算的属性例如 SVG 的路径d等在 2.3.0 版本的 ConfigHelper.ts 中维护addWhitelistedUIProps()用于登记直接在 UI 线程更新的属性即在 ConfigHelper.ts 顶部维护的允许列表。调用方式类似import { addWhitelistedNativeProps } from react-native-reanimated; addWhitelistedNativeProps({ d: true });需要特别提醒的是在**当前仓库主版本Reanimated 4**中这两个函数已经被标记为 deprecated 并成为空操作。见 deprecated.ts/** deprecated This function is a no-op in Reanimated 4. */ export function addWhitelistedNativeProps( _props: Recordstring, boolean ): void { // Do nothing. This is just for backward compatibility. } /** deprecated This function is a no-op in Reanimated 4. */ export function addWhitelistedUIProps(_props: Recordstring, boolean): void { // Do nothing. This is just for backward compatibility. }它们仍从 Animated.ts 中导出以保持向后兼容但在 Reanimated 4 中不再需要手动调用——属性白名单的判定已被更完整的属性处理管线取代。如果你正在从 2.x 升级可以放心移除这两处调用mock.ts中也以NOOP形式保留了对应的 Jest mock 实现。五、实战示例用 react-native-svg 动画化 SVG 路径原文档给出了一个经典示例用useAnimatedProps驱动 SVGPath的d属性画出一个随 Shared Value 变化而放大缩小的圆。以下是完整可运行的版本import React from react; import { StyleSheet } from react-native; import Animated, { useSharedValue, useAnimatedProps, } from react-native-reanimated; import Svg, { Path } from react-native-svg; const AnimatedPath Animated.createAnimatedComponent(Path); function App() { const radius useSharedValue(50); const animatedProps useAnimatedProps(() { // draw a circle const path M 100, 100 m -${radius.value}, 0 a ${radius.value},${radius.value} 0 1,0 ${radius.value * 2},0 a ${radius.value},${radius.value} 0 1,0 ${-radius.value * 2},0 ; return { d: path, }; }); // attach animated props to an SVG path using animatedProps return ( Svg AnimatedPath animatedProps{animatedProps} fillblack / /Svg ); }要点拆解路径字符串在 worklet 内动态拼接radius.value的每次变化都会让 worklet 重新执行重新生成d字符串d属于 SVG 原生属性而非样式这正是useAnimatedProps的用武之地useAnimatedStyle无法处理它fillblack作为静态属性照常写在 JSX 上与animatedProps互不冲突。如果你需要真正让半径动起来可以在外部用withTiming等动画函数驱动radiusconst radius useSharedValue(50); // 任意事件或 useEffect 中触发 radius.value withTiming(120, { duration: 800 });Shared Value 携带动画对象时useAnimatedProps的 mapper 会逐帧读取动画插值结果于是d属性会被每一帧的新值更新形成平滑的圆形放大动画。六、createAnimatedPropAdapter弥合 API 属性名与原生属性名6.1 它解决什么问题部分第三方库以及用户自定义组件存在一个常见问题组件在 API 层暴露的属性名与其底层真正接收的原生属性名不一致。createAnimatedPropAdapter正是为此而生——它允许你定义一个转换函数把useAnimatedPropsworklet 返回的属性映射为组件真正识别的属性。注意事项原文档明确强调Adapter 应在组件外部创建。createAnimatedPropAdapter不是 Hook不应在组件每次重渲染时被调用Adapter 函数接收一个待更新到 UI 线程的属性对象不必返回值直接修改传入的对象即可。6.2 参数说明参数类型必填说明adapterFunction是接收待更新属性对象的函数就地修改该对象完成属性名转换nativePropsArray否需要加入NATIVE_THREAD_PROPS_WHITELIST白名单的属性名列表6.3 完整示例helloSize → fontSize原文档给出的示例中自定义组件Hello在 API 层接收helloSize属性但底层真正消费的是fontSizeclass Hello extends React.Component { render() { return Text style{{ fontSize: this.props.helloSize }}Hello/Text; } } const AnimatedHello Animated.createAnimatedComponent(Hello); const adapter createAnimatedPropAdapter( (props) { if (Object.keys(props).includes(helloSize)) { props.fontSize props.helloSize; delete props.helloSize; } }, [fontSize] ); export default function Component() { const sv useSharedValue(14); const helloProps useAnimatedProps( () ({ helloSize: sv.value }), null, adapter ); return AnimatedHello animatedProps{helloProps} /; }流程说明worklet 每次执行返回{ helloSize: sv.value }属性对象在发往原生侧之前先经过adapter被改写成{ fontSize: 14 }并删掉helloSize第二个参数[fontSize]把fontSize登记进原生属性白名单确保该属性可以被 UI 线程直接更新。6.4 当前版本Reanimated 4的写法变化在升级到 Reanimated 4 后createAnimatedPropAdapter已被标记为不再必要。见 PropAdapters.ts它现在只会打印一条弃用警告并原样返回 adapterexport function createAnimatedPropAdapter( adapter: AnimatedPropsAdapterWorklet, _nativeProps?: string[] ): AnimatedPropsAdapterWorklet { logger.warn( createAnimatedPropAdapter is no longer necessary in Reanimated 4 and will be removed in next version. Please remove this call from your code and pass the adapter function directly. ); return adapter; }因此在新版本中推荐直接以 worklet 形式定义 adapter 并传给useAnimatedProps的第三个参数无需再套一层工厂函数。新版文档 useAnimatedProps.mdx 给出的推荐写法是const adapter (props) { worklet; // reshape props here }; const animatedProps useAnimatedProps(updater, [], adapter);adapter 参数同时支持单个函数或函数数组多个 adapter 会按顺序依次执行见 useAnimatedProps.ts 的类型签名与 useAnimatedStyle.ts 中Array.isArray(adapters) ? adapters : [adapters]的归一化处理。七、源码级深入属性更新流水线useAnimatedProps的值最终如何到达原生视图当前仓库的实现揭示了这条关键链路7.1 属性更新入口updateProps.native.ts 中的updatePropsworklet 会根据isAnimatedProps标志分流处理const updateProps (viewDescriptors, updates, isAnimatedProps) { worklet; if (isAnimatedProps) { processColorsInProps(updates); if (transformOrigin in updates) { updates.transformOrigin processTransformOrigin(updates.transformOrigin); } if (transform in updates) { updates.transform processTransform(updates.transform); } } global.UpdatePropsManager.update( viewDescriptors, // Use props builder only for style updaters, since animated props // can contain any properties of different types, depending on the // component, which we cannot process properly with the props builder. isAnimatedProps ? updates : stylePropsBuilder.build(updates) ); };这里有两点关键设计颜色自动处理isAnimatedProps为真时属性对象会先经过processColorsInProps把颜色字符串统一转换为原生可识别的数值格式跳过样式构建器因为animatedProps可以包含任意类型、任意数量的组件特有属性取决于具体组件无法用统一的stylePropsBuilder处理所以直接原样下发。7.2 属性分类与批量下发UpdatePropsManager.update同一文件的 createUpdatePropsManager会把更新按目标拆分为原生属性与JS 属性两类凡是在_tagToJSPropNamesMapping中登记过的属性走 JS 通道scheduleOnRN调度回 React 线程其余走原生通道global._updateProps。两类更新先压入队列再通过__requestMapperRunFinalizer在 mapper 执行批次结束时统一 flush避免逐帧逐属性多次跨线程调用。八、颜色属性的特殊处理用useAnimatedProps动画化自定义组件时颜色类属性是最容易踩坑的地方原生侧无法直接理解red、#FF0000这类字符串必须转换为数值形式。Reanimated 内置了一个自动处理的颜色属性清单ColorProperties定义在 Colors.ts其中既包括常规样式颜色属性也专门覆盖了 SVG 颜色属性export const ColorProperties [ backgroundColor, borderBottomColor, borderColor, borderLeftColor, borderRightColor, borderTopColor, borderStartColor, borderEndColor, borderBlockColor, borderBlockEndColor, borderBlockStartColor, color, outlineColor, placeholderTextColor, shadowColor, textDecorationColor, tintColor, textShadowColor, overlayColor, // SVG color properties fill, floodColor, lightingColor, stopColor, stroke, ];对应的处理逻辑processColorsInProps位于 colors.ts遍历属性对象凡键名命中ColorProperties就把值交给processColor转换数组值则逐项转换export function processColorsInProps(props: StyleProps) { worklet; for (const key in props) { if (!ColorProperties.includes(key)) continue; const value props[key]; props[key] Array.isArray(value) ? value.map((c) processColor(c)) : processColor(value); } }如果你的目标颜色属性不在上述清单中例如某些 SVG 库特有的颜色属性就需要手动用processColor包裹后再返回确保原生侧能正确解析import { processColor, interpolateColor } from react-native-reanimated; function App() { const animatedProps useAnimatedProps(() { const mainColor interpolateColor(colorProgress.value, [0, 1], [red, blue]); return { // colors 不在自动处理清单中必须手动处理 colors: processColor([mainColor, green]), }; }); }该行为在 colors.android.test.ts 与 colors.ios.test.ts 中有对应的测试覆盖例如验证DynamicColorIOS会抛错、PlatformColor被保留等边界行为。九、测试用 Jest 验证 animatedPropsReanimated 的 Jest 测试工具为animatedProps提供了专门的匹配器toHaveAnimatedProps。仓库测试 animatedProps.test.tsx 给出了完整范式——它用useAnimatedProps动画化TextInput的text属性然后验证点击按钮后属性确实更新function TextInputTestComponent() { const width useSharedValue(20); const animatedProps useAnimatedProps(() ({ text: Box width: ${width.value}, defaultValue: Box width: ${width.value}, })); const handlePress () { width.value 10; }; return ( BaseTextInputComponent animatedProps{animatedProps} onPress{handlePress} / ); } test(updates text on button press, () { const { getByTestId } render(TextInputTestComponent /); const textInput getByTestId(text); const button getByTestId(button); expect(textInput).toHaveAnimatedProps({ text: Box width: 20 }); fireEvent.press(button); jest.advanceTimersByTime(animationDuration); expect(textInput).toHaveAnimatedProps({ text: Box width: 30 }); });该测试文件还覆盖了卸载清理场景L90-L109通过 spyviewDescriptors.remove验证组件卸载时动画属性对象会被正确移除避免内存泄漏。这提示我们在自己的项目中也应依赖 Reanimated 自动的卸载清理不要手动持有animatedProps对象。十、平台兼容性与最佳实践10.1 平台支持根据当前新版文档 useAnimatedProps.mdx 的PlatformCompatibility声明useAnimatedProps支持Android、iOS 与 Web三大平台。10.2 最佳实践清单优先useAnimatedStyle需要动画化的是样式属性时一律使用useAnimatedStyleuseAnimatedProps只用于非样式原生属性Adapter 定义在组件外部避免每次重渲染重复创建也符合 Hook 规范静态属性留在 JSX与动画无关的属性如示例中的fillblack直接写在 JSX 上让animatedProps只承载真正需要动画的部分颜色属性确认清单目标颜色属性不在ColorProperties中时手动包一层processColor升级 3.x/4.x 的用户移除addWhitelistedNativeProps/addWhitelistedUIProps调用并把createAnimatedPropAdapter(...)写法改为直接传 adapter workletanimatedProps 可在多个组件间共享以减少重复代码见新版文档 Remarks。10.3 快速参考参数签名function useAnimatedPropsT extends {}( updater: () PartialT, // 必填返回待动画属性对象的 worklet dependencies?: DependencyList | null, // 可选Web 无 Babel 插件时需显式传入 adapters?: PropsAdapterFunction | PropsAdapterFunction[] | null // 可选属性名转换器 ): PartialT;其中dependencies仅在 Web 端不使用 Babel 插件时有意义在原生端worklet 的闭包与 worklet hash 会被自动纳入依赖计算见 useAnimatedStyle.ts 的依赖构建逻辑。结语useAnimatedProps是 Reanimated 属性动画体系的基石它以 worklet Shared Value 的既有基础设施把原生组件任意属性纳入了 UI 线程动画的范畴配合createAnimatedComponent、Prop Adapter 与自动颜色处理让react-native-svg等第三方原生组件也能获得与核心组件一致的高性能动画体验。结合 useAnimatedProps.ts 的源码与 animatedProps.test.tsx 的测试你可以进一步探索它在自定义组件上的应用边界。【免费下载链接】react-native-reanimatedReact Natives Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。