关于 reanimated-skia-performance
reanimated-skia-performance 是一项用于实现和调优 Reanimated 与 Skia 动画的技能。它为开发者提供 UI 线程状态管理、渲染器与动画方案选择,以及性能问题规避方面的指导。
主要功能
- 01将动画状态保留在 UI 线程
- 02选择 Reanimated 或 Skia
- 03使用声明式过渡和动画原语
- 04将共享值直接传给 Skia
- 05避免逐帧渲染和分配
- 06参考性能指南与检查清单
适用场景
- 01规划动画效果和手势行为
- 02为变换或自定义绘图选择渲染器
- 03为 Skia 着色器统一变量制作动画
- 04检查动画代码中的性能问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ andreev-danila/ skills --skill reanimated-skia-performance安装后在 Claude Code 中输入/reanimated-skia-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/dev-tuning.mdperf-checklist.mdreanimated-v4.mdskia-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 属性/统一变量。
工作流
- 明确效果:动画内容、时长/曲线、中断规则和手势输入。
- 选择渲染器:
- 对变换/不透明度/布局使用 Reanimated 样式。
- 对自定义绘制、粒子、渐变、运行时效果/着色器使用 Skia。
- 选择基本方案:
- 对简单的声明式样式变化,使用 Reanimated CSS 过渡/动画。
- 对补间动画使用
withTiming,对物理效果使用withSpring,对惯性效果使用withDecay。 - 对挂载/卸载或布局变化使用 Layout Animations。
- 在 UI 线程上实现单一数据流(不要每帧调用
setState)。 - 检查性能(参见
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(关键帧对象)animationDurationanimationDelayanimationTimingFunctionanimationDirectionanimationIterationCountanimationFillModeanimationPlayState
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