animating-react-native-expo

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Native (Expo) animations — Reanimated v4 + Gesture Handler

React Native(Expo)动画开发——Reanimated v4 + Gesture Handler

Defaults (pick these unless there’s a reason not to)

默认方案(无特殊理由请选择这些)

  1. Simple state change (hover/pressed/toggled, small style changes): use Reanimated CSS Transitions.
  2. Mount/unmount + layout changes (lists, accordions, reflow): use Reanimated Layout Animations.
  3. Interactive / per-frame (gestures, scroll, physics, drag): use Shared Values + worklets (UI thread).
If an existing codebase already uses a different pattern, stay consistent and only migrate when necessary.
  1. 简单状态变更(悬停/按压/切换、小型样式变化):使用Reanimated CSS 过渡
  2. 挂载/卸载 + 布局变化(列表、折叠面板、重排):使用Reanimated 布局动画
  3. 交互式/逐帧(手势、滚动、物理效果、拖拽):使用共享值 + worklets(UI线程)。
如果现有代码库已使用其他模式,请保持一致性,仅在必要时进行迁移。

Quick start

快速开始

Install (Expo)

安装(Expo)

bash
npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler
Run the setup check (optional):
bash
node {baseDir}/scripts/check-setup.mjs
bash
npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler
运行安装检查(可选):
bash
node {baseDir}/scripts/check-setup.mjs

1) Shared value +
withTiming

1) 共享值 +
withTiming

tsx
import { Pressable } from 'react-native';
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';

export function FadeInBox() {
  const opacity = useSharedValue(0);

  const style = useAnimatedStyle(() => ({ opacity: opacity.value }));

  return (
    <Pressable onPress={() => (opacity.value = withTiming(opacity.value ? 0 : 1, { duration: 200 }))}>
      <Animated.View style={[{ width: 80, height: 80 }, style]} />
    </Pressable>
  );
}
tsx
import { Pressable } from 'react-native';
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';

export function FadeInBox() {
  const opacity = useSharedValue(0);

  const style = useAnimatedStyle(() => ({ opacity: opacity.value }));

  return (
    <Pressable onPress={() => (opacity.value = withTiming(opacity.value ? 0 : 1, { duration: 200 }))}>
      <Animated.View style={[{ width: 80, height: 80 }, style]} />
    </Pressable>
  );
}

2) Pan gesture driving translation (UI thread)

2) 平移手势驱动位移(UI线程)

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

export function Draggable() {
  const x = useSharedValue(0);
  const y = useSharedValue(0);

  const pan = usePanGesture({
    onUpdate: (e) => {
      x.value = e.translationX;
      y.value = e.translationY;
    },
    onDeactivate: () => {
      x.value = withSpring(0);
      y.value = withSpring(0);
    },
  });

  const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.value }, { translateY: y.value }] }));

  return (
    <GestureDetector gesture={pan}>
      <Animated.View style={[{ width: 100, height: 100 }, style]} />
    </GestureDetector>
  );
}
tsx
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
import { GestureDetector, usePanGesture } from 'react-native-gesture-handler';

export function Draggable() {
  const x = useSharedValue(0);
  const y = useSharedValue(0);

  const pan = usePanGesture({
    onUpdate: (e) => {
      x.value = e.translationX;
      y.value = e.translationY;
    },
    onDeactivate: () => {
      x.value = withSpring(0);
      y.value = withSpring(0);
    },
  });

  const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.value }, { translateY: y.value }] }));

  return (
    <GestureDetector gesture={pan}>
      <Animated.View style={[{ width: 100, height: 100 }, style]} />
    </GestureDetector>
  );
}

3) CSS-style transition (best for “style changes when state changes”)

3) 类CSS过渡(最适合“状态变更时的样式变化”场景)

tsx
import Animated from 'react-native-reanimated';

export function ExpandingCard({ expanded }: { expanded: boolean }) {
  return (
    <Animated.View
      style={{
        width: expanded ? 260 : 180,
        transitionProperty: 'width',
        transitionDuration: 220,
      }}
    />
  );
}
tsx
import Animated from 'react-native-reanimated';

export function ExpandingCard({ expanded }: { expanded: boolean }) {
  return (
    <Animated.View
      style={{
        width: expanded ? 260 : 180,
        transitionProperty: 'width',
        transitionDuration: 220,
      }}
    />
  );
}

Workflow (copy this and tick it off)

工作流程(复制并勾选完成)

  • Identify the driver: state, layout, gesture, or scroll.
  • Choose the primitive:
    • state → CSS transition / CSS animation
    • layout/mount → entering/exiting/layout transitions
    • gesture/scroll → shared values + worklets
  • Keep per-frame work on the UI thread (worklets); avoid React state updates every frame.
  • If a JS-side effect is required (navigation, analytics, state set), call it via
    scheduleOnRN
    .
  • Verify on-device (Hermes inspector), not “Remote JS Debugging”.
  • 确定驱动源:状态布局手势滚动
  • 选择对应的基础方案:
    • 状态 → CSS过渡 / CSS动画
    • 布局/挂载 → 进入/退出/布局过渡
    • 手势/滚动 → 共享值 + worklets
  • 将逐帧操作保持在UI线程(worklets);避免每帧更新React状态。
  • 如果需要JS端副作用(导航、埋点、状态设置),通过
    scheduleOnRN
    调用。
  • 在真机上验证(Hermes调试器),不要使用“远程JS调试”。

Core patterns

核心模式

Shared values are the “wire format” between runtimes

共享值是跨运行时的“通信格式”

  • Use
    useSharedValue
    for numbers/strings/objects that must be read/written from both UI and JS.
  • Derive styles with
    useAnimatedStyle
    .
  • Prefer
    withTiming
    for UI tweens;
    withSpring
    for physics.
  • 对于需要在UI和JS线程间读写的数字/字符串/对象,使用
    useSharedValue
  • 通过
    useAnimatedStyle
    派生样式。
  • UI补间动画优先使用
    withTiming
    ;物理动效优先使用
    withSpring

UI thread vs JS thread: the only rule that matters

UI线程 vs JS线程:唯一重要的规则

  • Gesture callbacks and animated styles should stay workletized (UI runtime).
  • Only bridge to JS when you must (via
    scheduleOnRN
    ).
See: references/worklets-and-threading.md
  • 手势回调和动画样式应保持worklet化(UI运行时)。
  • 仅在必要时(通过
    scheduleOnRN
    )桥接到JS线程。
参考:references/worklets-and-threading.md

Gesture Handler: use one API style per subtree

Gesture Handler:每个子树使用统一的API风格

  • Default to hook API (
    usePanGesture
    ,
    useTapGesture
    , etc.).
  • Do not nest GestureDetectors that use different API styles (hook vs builder).
  • Do not reuse the same gesture instance across multiple detectors.
See: references/gestures.md
  • 默认使用Hook API
    usePanGesture
    useTapGesture
    等)。
  • 不要嵌套使用不同API风格(Hook vs 构建器)的GestureDetector。
  • 不要在多个检测器间复用同一个手势实例。
参考:references/gestures.md

CSS Transitions (Reanimated 4)

CSS过渡(Reanimated 4)

Use when a style value changes due to React state/props and you just want it to animate.
Rules of thumb:
  • Always set
    transitionProperty
    +
    transitionDuration
    .
  • Avoid
    transitionProperty: 'all'
    (perf + surprise animations).
  • Discrete properties (e.g.
    flexDirection
    ) won’t transition smoothly; use Layout Animations instead.
See: references/css-transitions-and-animations.md
适用于因React状态/Props变化导致样式值变更,且仅需实现动效的场景。
经验法则:
  • 始终设置
    transitionProperty
    +
    transitionDuration
  • 避免使用
    transitionProperty: 'all'
    (性能问题 + 意外动画)。
  • 离散属性(如
    flexDirection
    )无法平滑过渡;改用布局动画。
参考:references/css-transitions-and-animations.md

Layout animations

布局动画

Use when elements enter/exit, or when layout changes due to conditional rendering/reflow.
Prefer presets first (entering/exiting, keyframes, layout transitions). Only reach for fully custom layout animations when presets can’t express the motion.
See: references/layout-animations.md
适用于元素进入/退出,或因条件渲染/重排导致布局变化的场景。
优先使用预设(进入/退出、关键帧、布局过渡)。仅当预设无法满足动效需求时,才使用完全自定义的布局动画。
参考:references/layout-animations.md

Scroll-linked animations

滚动关联动画

Prefer Reanimated scroll handlers/shared values; keep worklet bodies tiny. For full recipes, see:
  • references/recipes.md
优先使用Reanimated滚动处理器/共享值;保持worklet代码简洁。完整示例请参考:
  • references/recipes.md

Troubleshooting checklist

故障排查清单

  1. “Failed to create a worklet” / worklet not running
  • Ensure the correct Babel plugin is configured for your environment.
    • Expo: handled by
      babel-preset-expo
      when installed via
      expo install
      .
    • Bare RN: Reanimated 4 uses
      react-native-worklets/plugin
      .
  1. Gesture callbacks not firing / weird conflicts
  • Ensure the app root is wrapped with
    GestureHandlerRootView
    .
  • Don’t reuse gestures across detectors; don’t mix hook and builder API in nested detectors.
  1. Needing to call JS from a worklet
  • Use
    scheduleOnRN(fn, ...args)
    .
  • fn
    must be defined in JS scope (component body or module scope), not created inside a worklet.
  1. Jank / dropped frames
  • Check for large objects captured into worklets; capture primitives instead.
  • Avoid
    transitionProperty: 'all'
    .
  • Don’t set React state every frame.
See: references/debugging-and-performance.md
  1. “Failed to create a worklet” / worklet未运行
  • 确保为你的环境配置了正确的Babel插件。
    • Expo:通过
      expo install
      安装时,由
      babel-preset-expo
      自动处理。
    • 原生RN:Reanimated 4使用
      react-native-worklets/plugin
  1. 手势回调未触发 / 出现异常冲突
  • 确保应用根组件被
    GestureHandlerRootView
    包裹。
  • 不要在多个检测器间复用手势;不要在嵌套检测器中混合使用Hook和构建器API。
  1. 需要从worklet中调用JS代码
  • 使用
    scheduleOnRN(fn, ...args)
  • fn
    必须定义在JS作用域(组件内部或模块作用域),而非worklet内部。
  1. 卡顿 / 丢帧
  • 检查worklet中是否捕获了大型对象;优先捕获基本类型。
  • 避免使用
    transitionProperty: 'all'
  • 不要每帧更新React状态。
参考:references/debugging-and-performance.md

Bundled references (open only when needed)

内置参考文档(必要时查看)

  • references/setup-and-compat.md: Expo vs bare setup, New Architecture requirement, common incompatibilities.
  • references/worklets-and-threading.md: UI runtime,
    scheduleOnUI
    /
    scheduleOnRN
    , closure capture, migration notes.
  • references/gestures.md: GestureDetector, hook API patterns, composition, gotchas.
  • references/css-transitions-and-animations.md: Reanimated 4 CSS transitions/animations quick reference.
  • references/layout-animations.md: entering/exiting/layout transitions and how to pick.
  • references/recipes.md: copy-paste components (drag, swipe, pinch, bottom sheet-ish, scroll effects).
  • references/debugging-and-performance.md: diagnosing worklet issues, profiling, common perf traps.
  • references/setup-and-compat.md:Expo vs 原生项目配置、新架构要求、常见兼容性问题。
  • references/worklets-and-threading.md:UI运行时、
    scheduleOnUI
    /
    scheduleOnRN
    、闭包捕获、迁移说明。
  • references/gestures.md:GestureDetector、Hook API模式、组合、常见陷阱。
  • references/css-transitions-and-animations.md:Reanimated 4 CSS过渡/动画速查。
  • references/layout-animations.md:进入/退出/布局过渡及方案选择。
  • references/recipes.md:可直接复用的组件(拖拽、滑动、捏合、类底部弹窗、滚动效果)。
  • references/debugging-and-performance.md:worklet问题诊断、性能分析、常见性能陷阱。

Quick search

快速搜索

bash
grep -Rni "scheduleOnRN" {baseDir}/references
grep -Rni "transitionProperty" {baseDir}/references
grep -Rni "usePanGesture" {baseDir}/references
bash
grep -Rni "scheduleOnRN" {baseDir}/references
grep -Rni "transitionProperty" {baseDir}/references
grep -Rni "usePanGesture" {baseDir}/references

Primary docs

官方文档