reanimated-skia-performance

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Reanimated + Skia Performance

Reanimated + Skia 性能优化

Defaults

默认规范

  • Keep animation state on the UI thread:
    useSharedValue
    ,
    useDerivedValue
    , worklets.
  • Prefer Reanimated v4 declarative APIs; avoid legacy
    Animated
    .
  • Prefer
    shared.get()
    /
    shared.set()
    over
    shared.value
    in app code (React Compiler friendly).
  • Minimize JS↔UI crossings: avoid
    scheduleOnRN
    /
    runOnJS
    except for unavoidable side effects.
  • For Skia, avoid per-frame React renders: pass
    SharedValue
    s directly to Skia props/uniforms.
  • 将动画状态保存在UI线程:
    useSharedValue
    useDerivedValue
    、worklets。
  • 优先使用Reanimated v4声明式API;避免使用旧版
    Animated
  • 在应用代码中优先使用
    shared.get()
    /
    shared.set()
    而非
    shared.value
    (兼容React Compiler)。
  • 尽量减少JS↔UI线程交互:除非是不可避免的副作用,否则避免使用
    scheduleOnRN
    /
    runOnJS
  • 对于Skia,避免每帧触发React重渲染:直接将
    SharedValue
    传递给Skia属性/统一变量。

Workflow

工作流程

  1. Define the effect: what animates, duration/curve, interrupt rules, and gesture input.
  2. Choose the renderer:
    • Use Reanimated styles for transforms/opacity/layout.
    • Use Skia for custom drawing, particles, gradients, runtime effects/shaders.
  3. Choose the primitive:
    • Use Reanimated CSS transitions/animations for simple declarative style changes.
    • Use
      withTiming
      for tweens,
      withSpring
      for physics,
      withDecay
      for momentum.
    • Use Layout Animations for mount/unmount or layout changes.
  4. Implement a single data flow on the UI thread (no
    setState
    per frame).
  5. Do a perf pass (see
    references/perf-checklist.md
    ).
  1. 定义效果:明确动画对象、时长/曲线、中断规则以及手势输入。
  2. 选择渲染器:
    • 使用Reanimated样式处理变换/透明度/布局。
    • 使用Skia处理自定义绘图、粒子效果、渐变、运行时效果/着色器。
  3. 选择原语:
    • 使用Reanimated CSS过渡/动画处理简单的声明式样式变化。
    • 使用
      withTiming
      实现补间动画,
      withSpring
      实现物理动画,
      withDecay
      实现动量动画。
    • 使用布局动画处理挂载/卸载或布局变化。
  4. 在UI线程上实现单一数据流(避免每帧调用
    setState
    )。
  5. 执行性能检查(参见
    references/perf-checklist.md
    )。

Patterns

实践模式

Shared values (React Compiler safe)

共享值(兼容React Compiler)

  • Read:
    progress.get()
  • Write:
    progress.set(withTiming(1))
ts
import { useEffect } from 'react';
import { useSharedValue, withTiming } from 'react-native-reanimated';

const progress = useSharedValue(0);

useEffect(() => {
  progress.set(withTiming(1, { duration: 400 }));
}, [progress]);
  • 读取:
    progress.get()
  • 写入:
    progress.set(withTiming(1))
ts
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 transitions

Reanimated v4 CSS过渡

Use for state-driven style changes where you do not need bespoke worklets.
tsx
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',
  }}
/>
适用于无需自定义worklets的状态驱动式样式变化。
tsx
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 animations (keyframes)

Reanimated v4 CSS动画(关键帧)

Use for keyframe-like sequences (pulses, wiggles, repeated loops) without writing custom worklets.
Supported settings:
  • animationName
    (keyframes object)
  • animationDuration
  • animationDelay
  • animationTimingFunction
  • animationDirection
  • animationIterationCount
  • animationFillMode
  • animationPlayState
tsx
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',
  }}
/>
适用于无需编写自定义worklets的关键帧序列动画(脉冲、摆动、重复循环)。
支持的设置:
  • animationName
    (关键帧对象)
  • animationDuration
  • animationDelay
  • animationTimingFunction
  • animationDirection
  • animationIterationCount
  • animationFillMode
  • animationPlayState
tsx
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-mode tuning panel (sliders)

开发模式调优面板(滑块)

Use sliders to tune animation configs or shader uniforms in
__DEV__
.
  • Keep the tuning UI in a separate component to avoid re-rendering the animated scene.
  • Write slider values into
    SharedValue
    s via
    .set()
    .
  • For shader tuning: feed
    SharedValue
    s into uniforms via
    useDerivedValue
    .
  • For animation config tuning: store config params in
    SharedValue
    s and read them when starting/restarting the animation.
See:
references/dev-tuning.md
.
__DEV__
模式下使用滑块调优动画配置或着色器统一变量。
  • 将调优UI放在单独组件中,避免触发动画场景重渲染。
  • 通过
    .set()
    将滑块值写入
    SharedValue
  • 着色器调优:通过
    useDerivedValue
    SharedValue
    传入统一变量。
  • 动画配置调优:将配置参数存储在
    SharedValue
    中,启动/重启动画时读取。
参见:
references/dev-tuning.md

Gesture-driven animation (Gesture Builder API)

手势驱动动画(Gesture Builder API)

Update shared values in
onUpdate
and drive visuals via animated styles.
tsx
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>;
onUpdate
中更新共享值,通过动画样式驱动视觉效果。
tsx
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 shader with animated uniforms

Skia着色器与动画统一变量

  • Compile the runtime effect once (
    useMemo
    ).
  • Pass uniforms as a
    SharedValue<Uniforms>
    (Skia detects animated props by
    { value: T }
    ).
  • Do not access
    .value
    in your app code; pass the
    SharedValue
    object.
tsx
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>;
  • 仅编译一次运行时效果(使用
    useMemo
    )。
  • 将统一变量作为
    SharedValue<Uniforms>
    传递(Skia通过
    { value: T }
    检测动画属性)。
  • 不要在应用代码中访问
    .value
    ;直接传递
    SharedValue
    对象。
tsx
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>;

Common pitfalls

常见陷阱

  • Do not read shared values in React render; read them in worklets (
    useAnimatedStyle
    ,
    useDerivedValue
    ).
  • Do not call
    runOnJS
    /
    scheduleOnRN
    in
    onUpdate
    unless you must.
  • Do not allocate big arrays/paths/images per frame; memoize Skia objects and update via animated props.
  • For runtime effects, always provide every uniform declared in the shader; missing uniforms throw.
  • 不要在React渲染中读取共享值;应在worklets(
    useAnimatedStyle
    useDerivedValue
    )中读取。
  • 除非必要,否则不要在
    onUpdate
    中调用
    runOnJS
    /
    scheduleOnRN
  • 不要每帧分配大数组/路径/图像;对Skia对象进行memoize,通过动画属性更新。
  • 对于运行时效果,必须提供着色器中声明的所有统一变量;缺失统一变量会抛出错误。

References

参考资料

  • Reanimated v4 patterns and repo conventions:
    references/reanimated-v4.md
  • Skia Canvas + runtime effects/shaders patterns:
    references/skia-shaders.md
  • Dev-mode tuning panels (sliders):
    references/dev-tuning.md
  • Performance checklist + debugging:
    references/perf-checklist.md
  • Reanimated v4模式及仓库约定:
    references/reanimated-v4.md
  • Skia Canvas + 运行时效果/着色器模式:
    references/skia-shaders.md
  • 开发模式调优面板(滑块):
    references/dev-tuning.md
  • 性能检查清单 + 调试:
    references/perf-checklist.md