アニメーション

RN のアニメーションは「JS スレッドを通さず、UI スレッドで動かす」のが品質の決め手。 標準の Animated もあるが、2026 現在は Reanimated が事実上の標準。 ジェスチャと組み合わせるなら Gesture Handler

3 つのレイヤー

  1. 標準 Animated — どの RN にも入っている。シンプルなフェード・スライドはこれで足りる。
  2. Reanimated — JS スレッドを介さずUI スレッドで実行される。複雑なジェスチャや滑らかな表現に。
  3. LayoutAnimation — レイアウト変化を自動で animate(地味に便利)。

標準 Animated

Animated.Value でアニメ可能な値を作り、Animated.timing 等で動かす。 useNativeDriver: true を必ず付ける(transform / opacity 限定だが UI スレッドで動く)。

import { Animated, Pressable, Text, View } from "react-native"
import { useRef } from "react"

function FadeIn() {
  const opacity = useRef(new Animated.Value(0)).current

  const fade = () => {
    Animated.timing(opacity, {
      toValue: 1,
      duration: 600,
      useNativeDriver: true,
    }).start()
  }

  return (
    <>
      <Animated.View style={{ opacity, padding: 20, backgroundColor: "#7dd3fc" }}>
        <Text>表示</Text>
      </Animated.View>
      <Pressable onPress={fade}><Text>フェードイン</Text></Pressable>
    </>
  )
}

ただし、新規プロジェクトでは Reanimated を選ぶのが今の主流。標準 Animated は制約と落とし穴が多い

Reanimated(推奨)

react-native-reanimated(v4 系)。
- JS スレッドを介さず UI スレッドで値を動かす
- worklet("worklet" ディレクティブ)で UI スレッドで実行されるコードを書ける
- Web もサポート(v4 から CSS animation を生成)

npx expo install react-native-reanimated

セットアップ

Expo Router の最新テンプレートでは標準で入っている。手で入れる場合は babel.config.jsreact-native-reanimated/plugin を最後に追加。

useSharedValue + useAnimatedStyle

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  withSpring,
  Easing,
} from "react-native-reanimated"

function Toggle() {
  const offset = useSharedValue(0)

  const animatedStyles = useAnimatedStyle(() => ({
    transform: [{ translateX: offset.value }],
  }))

  return (
    <>
      <Animated.View style={[{ width: 80, height: 80, backgroundColor: "aqua" }, animatedStyles]} />
      <Pressable onPress={() => { offset.value = withSpring(offset.value + 100) }}>
        <Text>動かす</Text>
      </Pressable>
    </>
  )
}

典型: スプリング

offset.value = withSpring(100, {
  mass: 1,
  damping: 10,
  stiffness: 100,
})

連鎖

import { withSequence, withDelay } from "react-native-reanimated"

scale.value = withSequence(
  withTiming(1.2, { duration: 200 }),
  withTiming(1.0, { duration: 200 }),
  withDelay(500, withTiming(0.9, { duration: 200 })),
)

useDerivedValue

他の SharedValue から派生した値を作る。

const opacity = useDerivedValue(() => {
  return offset.value > 50 ? 1 : 0.5
})

worklet を直接書く

UI スレッドで実行する関数は最初に "worklet" ディレクティブを付ける。

function calc(x: number) {
  "worklet"
  return x * x
}

const result = useDerivedValue(() => calc(offset.value))

Gesture Handler(ジェスチャ)

react-native-gesture-handler。タップ・パン・ピンチ・回転・長押しなど、 OS のネイティブジェスチャ認識を使う。Reanimated と組み合わせると滑らかなドラッグが作れる。

npx expo install react-native-gesture-handler

ルートで GestureHandlerRootView

Expo の最近のテンプレートでは自動で入る。手動なら app/_layout.tsx で:

import { GestureHandlerRootView } from "react-native-gesture-handler"

<GestureHandlerRootView style={{ flex: 1 }}>
  <Slot />
</GestureHandlerRootView>

パン(ドラッグ)

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

function Draggable() {
  const tx = useSharedValue(0)
  const ty = useSharedValue(0)
  const start = useSharedValue({ x: 0, y: 0 })

  const pan = Gesture.Pan()
    .onStart(() => {
      start.value = { x: tx.value, y: ty.value }
    })
    .onUpdate((e) => {
      tx.value = start.value.x + e.translationX
      ty.value = start.value.y + e.translationY
    })
    .onEnd(() => {
      tx.value = withSpring(0)
      ty.value = withSpring(0)
    })

  const style = useAnimatedStyle(() => ({
    transform: [{ translateX: tx.value }, { translateY: ty.value }],
  }))

  return (
    <GestureDetector gesture={pan}>
      <Animated.View style={[{ width: 80, height: 80, backgroundColor: "hotpink" }, style]} />
    </GestureDetector>
  )
}

ピンチ・回転・タップ

ナビゲーション間の共有要素遷移

Expo Router は react-native-reanimated + react-native-screens と組み合わせて shared element transitions を提供(experimental)。リスト → 詳細で画像が大きく拡大される演出。

import Animated from "react-native-reanimated"

// 一覧側
<Animated.Image
  sharedTransitionTag="hero-1"
  source={{ uri: post.thumb }}
  style={{ width: 80, height: 80 }}
/>

// 詳細側
<Animated.Image
  sharedTransitionTag="hero-1"
  source={{ uri: post.image }}
  style={{ width: "100%", height: 240 }}
/>

レイアウトアニメーション

要素の追加・削除・移動を自動で animate。FlatList のソート、リストの追加削除に強い。

標準の LayoutAnimation

import { LayoutAnimation, Platform, UIManager } from "react-native"

if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) {
  UIManager.setLayoutAnimationEnabledExperimental(true)
}

const addItem = () => {
  LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
  setItems([...items, newItem])
}

Reanimated の Layout Animation

import Animated, { FadeIn, FadeOut, SlideInRight, Layout } from "react-native-reanimated"

<Animated.View
  entering={FadeIn.delay(200)}
  exiting={FadeOut}
  layout={Layout.springify()}
>
  ...
</Animated.View>

FlatList の itemLayoutAnimation も使える:

<Animated.FlatList
  data={items}
  itemLayoutAnimation={Layout.springify()}
  ...
/>

SVG / アニメーション

react-native-svg + Reanimated でパスやストロークの長さを動かせる。 R3F が RN でも動くので、3D っぽい表現も可能。

moti(宣言的 API)

Reanimated の上に乗る、「from / animate」で書ける宣言的ライブラリ。

import { MotiView } from "moti"

<MotiView
  from={{ opacity: 0, translateY: 20 }}
  animate={{ opacity: 1, translateY: 0 }}
  transition={{ type: "spring", damping: 12 }}
/>

Lottie

Adobe After Effects から書き出したJSON ベースのアニメーションを再生できる。 ロード画面やマイクロインタラクションに強い。

npx expo install lottie-react-native
import LottieView from "lottie-react-native"

<LottieView
  source={require("../assets/loading.json")}
  autoPlay
  loop
  style={{ width: 200, height: 200 }}
/>

prefers-reduced-motion 対応

OS の視差効果を減らす設定を尊重する。a11y のページと同じ考え方。

import { useReducedMotion } from "react-native-reanimated"

function Hero() {
  const reduced = useReducedMotion()
  // reduced が true なら派手なアニメをスキップ
}

判断ガイド

やりたいこと使うもの
シンプルなフェード・スライド標準 Animated でも可、Reanimated なら確実
ジェスチャ連動Gesture Handler + Reanimated
リストの追加・削除を滑らかにReanimated の Layout / FadeIn / FadeOut
画面遷移の hero animationExpo Router + Reanimated の sharedTransitionTag
キャラ・ローディングなどの凝った演出Lottie
宣言的に短く書きたいmoti
ホットリロードでアニメが壊れる

Reanimated は worklet を別スレッドにシリアライズするため、大きく書き換えるとリロードしてもうまく反映されないことがある。
開発メニューから "Reload" するか、r キーで完全リロードすると治る。