mcpField
R
reanimated-skia-performance

reanimated-skia-performance

github.com/andreev-danila设计与 UI24956 次安装14 周前更新
代码仓库

关于 reanimated-skia-performance

reanimated-skia-performance 是一项用于实现和调优 Reanimated 与 Skia 动画的技能。它为开发者提供 UI 线程状态管理、渲染器与动画方案选择,以及性能问题规避方面的指导。

主要功能

  1. 01将动画状态保留在 UI 线程
  2. 02选择 Reanimated 或 Skia
  3. 03使用声明式过渡和动画原语
  4. 04将共享值直接传给 Skia
  5. 05避免逐帧渲染和分配
  6. 06参考性能指南与检查清单

适用场景

  1. 01规划动画效果和手势行为
  2. 02为变换或自定义绘图选择渲染器
  3. 03为 Skia 着色器统一变量制作动画
  4. 04检查动画代码中的性能问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/andreev-danila/skills --skill reanimated-skia-performance

安装后在 Claude Code 中输入/reanimated-skia-performance确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
dev-tuning.md
perf-checklist.md
reanimated-v4.md
skia-shaders.md

详细介绍

Reanimated + Skia 性能

默认做法

  • 将动画状态保留在 UI 线程:useSharedValue、useDerivedValue、worklet。
  • 优先使用 Reanimated v4 声明式 API;避免使用旧版 Animated。
  • 在应用代码中,优先使用 shared.get() / shared.set(),而非 shared.value(更适配 React Compiler)。
  • 尽量减少 JS↔UI 线程间的切换:除非副作用无法避免,否则不要使用 scheduleOnRN/runOnJS。
  • 对于 Skia,避免每帧都触发 React 渲染:将 SharedValue 直接传给 Skia 属性/统一变量。

工作流

  1. 明确效果:动画内容、时长/曲线、中断规则和手势输入。
  2. 选择渲染器:
    • 对变换/不透明度/布局使用 Reanimated 样式。
    • 对自定义绘制、粒子、渐变、运行时效果/着色器使用 Skia。
  3. 选择基本方案:
    • 对简单的声明式样式变化,使用 Reanimated CSS 过渡/动画。
    • 对补间动画使用 withTiming,对物理效果使用 withSpring,对惯性效果使用 withDecay。
    • 对挂载/卸载或布局变化使用 Layout Animations。
  4. 在 UI 线程上实现单一数据流(不要每帧调用 setState)。
  5. 检查性能(参见 references/perf-checklist.md)。

模式

共享值(兼容 React Compiler)
  • 读取:progress.get()
  • 写入:progress.set(withTiming(1))
import { useEffect } from 'react';
import { useSharedValue, withTiming } from 'react-native-reanimated';

const progress = useSharedValue(0);

useEffect(() => {
  progress.set(withTiming(1, { duration: 400 }));
}, [progress]);
Reanimated v4 CSS 过渡

用于由状态驱动的样式变化,且无需自定义 worklet 的场景。

import Animated from 'react-native-reanimated';

<Animated.View
  style={{
    width: expanded ? 240 : 160,
    opacity: enabled ? 1 : 0.6,
    transitionProperty: ['width', 'opacity'],
    transitionDuration: 220,
    transitionTimingFunction: 'ease-in-out',
  }}
/>
Reanimated v4 CSS 动画(关键帧)

用于类似关键帧的序列(脉冲、抖动、重复循环),无需编写自定义 worklet。

支持的设置:

  • animationName(关键帧对象)
  • animationDuration
  • animationDelay
  • animationTimingFunction
  • animationDirection
  • animationIterationCount
  • animationFillMode
  • animationPlayState
import Animated from 'react-native-reanimated';

const pulse = {
  from: { transform: [{ scale: 1 }], opacity: 0.9 },
  '50%': { transform: [{ scale: 1.06 }], opacity: 1 },
  to: { transform: [{ scale: 1 }], opacity: 0.9 },
};

<Animated.View
  style={{
    animationName: pulse,
    animationDuration: '900ms',
    animationDelay: '80ms',
    animationTimingFunction: 'ease-in-out',
    animationDirection: 'alternate',
    animationIterationCount: 'infinite',
    animationFillMode: 'both',
    animationPlayState: paused ? 'paused' : 'running',
  }}
/>
开发模式调节面板(滑块)

在 __DEV__ 中使用滑块调节动画配置或着色器统一变量。

  • 将调节 UI 放在单独的组件中,避免重新渲染动画场景。
  • 通过 .set() 将滑块值写入 SharedValue。
  • 调节着色器时:通过 useDerivedValue 将 SharedValue 传入统一变量。
  • 调节动画配置时:将配置参数存储在 SharedValue 中,并在启动/重新启动动画时读取。

参见:references/dev-tuning.md。

手势驱动动画(Gesture Builder API)

在 onUpdate 中更新共享值,并通过动画样式驱动视觉效果。

import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';

const x = useSharedValue(0);
const gesture = Gesture.Pan()
  .onUpdate((e) => {
    x.set(e.translationX);
  })
  .onEnd(() => {
    x.set(withSpring(0));
  });

const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.get() }] }));

<GestureDetector gesture={gesture}>
  <Animated.View style={style} />
</GestureDetector>;
带动画统一变量的 Skia 着色器
  • 使用 useMemo 编译一次运行时效果。
  • 将统一变量作为 SharedValue<Uniforms> 传入(Skia 会通过 { value: T } 检测动画属性)。
  • 不要在应用代码中访问 .value;直接传入 SharedValue 对象。
import { useMemo } from 'react';
import { Skia, Canvas, Fill, Paint, Shader, type Uniforms } from '@shopify/react-native-skia';
import { useDerivedValue } from 'react-native-reanimated';
import { useClock } from '@shopify/react-native-skia';

const sksl = `
uniform float2 u_resolution;
uniform float u_time;

half4 main(float2 xy) {
  float2 uv = xy / u_resolution;
  float v = 0.5 + 0.5 * sin(u_time * 0.002 + uv.x * 8.0);
  return half4(v, v * 0.6, 1.0 - v, 1.0);
}
`;

const effect = useMemo(() => Skia.RuntimeEffect.Make(sksl), []);
if (!effect) return null;

const clock = useClock(); // ms since first frame
const uniforms = useDerivedValue<Uniforms>(() => ({
  u_resolution: Skia.Point(width, height),
  u_time: clock.get(),
}));

<Canvas style={{ width, height }}>
  <Fill>
    <Paint>
      <Shader source={effect} uniforms={uniforms} />
    </Paint>
  </Fill>
</Canvas>;

常见问题

  • 不要在 React 渲染过程中读取共享值;应在 worklet 中读取(useAnimatedStyle、useDerivedValue)。
  • 除非确有必要,否则不要在 onUpdate 中调用 runOnJS/scheduleOnRN。
  • 不要每帧都分配大型数组/路径/图像;应对 Skia 对象进行记忆化,并通过动画属性更新。
  • 对于运行时效果,务必提供着色器中声明的所有统一变量;缺少统一变量会导致报错。

参考资料

  • Reanimated v4 模式和仓库约定:references/reanimated-v4.md
  • Skia Canvas + 运行时效果/着色器模式:references/skia-shaders.md
  • 开发模式调节面板(滑块):references/dev-tuning.md
  • 性能检查清单 + 调试:references/perf-checklist.md

评论

支持 Markdown