Skia 帧驱动动画
概述
React Native 中高帧率多对象动画(如游戏循环)不适合用 useState 驱动——每帧触发 React reconciliation 会成为性能瓶颈。Skia Canvas 提供了绕过 React View 树的渲染路径:内部图元通过 JSI 交给 Skia C++ 引擎,再由 Metal/Vulkan 渲染,不经过 Fabric Shadow Tree。
工作方式
渲染路径对比
| 方案 | 渲染路径 | 适用场景 |
|---|---|---|
| Animated.View | React → Fabric → Native View → GPU | UI 元素动画(按钮、卡片) |
| Reanimated worklet | UI thread 的 JS runtime → Native View | 手势驱动、弹簧动画 |
| Skia Canvas | JSI → Skia C++ → Metal/Vulkan | 多对象、粒子、数据可视化 |
useFrameCallback
类似 Web 的 requestAnimationFrame,但运行在 UI runtime 而非 JS thread。回调中读写 mutable ref 中的球体位置,不触发 React re-render。
SharedValue vs mutable ref
- SharedValue:适合少量动画参数跨线程驱动样式
- mutable ref:适合 write-heavy game state(每帧改 x/y/vx/vy),只有一个消费者(画布)
MOT 选择 mutable ref——球体坐标不需要响应式订阅,帧循环直接读写即可。
dt 钳位
高刷新率设备(120Hz)帧间隔仅 8.33ms,必须用 frameInfo.timestamp 算真实 dt。切后台恢复时 dt 可能很大,需要 Math.min(dt, MAX_DT_MS) 防止瞬移。
参见
- Reanimated Worklets — worklet 执行模型
- RN 新架构 — JSI 和 Fabric 基础
- 项目文档:
docs/learning/20-skia-frame-driven-animation.md