アニメーション
RN のアニメーションは「JS スレッドを通さず、UI スレッドで動かす」のが品質の決め手。
標準の Animated もあるが、2026 現在は Reanimated が事実上の標準。
ジェスチャと組み合わせるなら Gesture Handler。
3 つのレイヤー
- 標準
Animated— どの RN にも入っている。シンプルなフェード・スライドはこれで足りる。 - Reanimated — JS スレッドを介さずUI スレッドで実行される。複雑なジェスチャや滑らかな表現に。
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.js に react-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>
</>
)
}
useSharedValue— UI スレッドにある値。書き換えで再レンダーは起きない。useAnimatedStyle— その値をスタイルにマップ。worklet 内で実行される。withTiming/withSpring/withDecay/withRepeat/withSequence/withDelay— アニメ系関数。
典型: スプリング
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>
)
}
ピンチ・回転・タップ
Gesture.Pinch()— ピンチ(拡大縮小)Gesture.Rotation()— 回転Gesture.Tap()/Gesture.LongPress()/Gesture.Fling()Gesture.Simultaneous(a, b)— 同時に認識Gesture.Race(a, b)— 早い方が勝つGesture.Exclusive(a, b)— 排他
ナビゲーション間の共有要素遷移
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 animation | Expo Router + Reanimated の sharedTransitionTag |
| キャラ・ローディングなどの凝った演出 | Lottie |
| 宣言的に短く書きたい | moti |
Reanimated は worklet を別スレッドにシリアライズするため、大きく書き換えるとリロードしてもうまく反映されないことがある。
開発メニューから "Reload" するか、r キーで完全リロードすると治る。