react-animations について
Framer Motion、React Spring、GSAP、CSS/Tailwindの使い分けと実装を案内する、React開発者向けのアニメーションスキルです。アクセシビリティやパフォーマンスの指針に加え、再利用可能なコンポーネントとバリアントのテンプレートも含みます。
主な機能
- 01アニメーションライブラリの選択指針
- 02Framer Motionのジェスチャーとレイアウトパターン
- 03スクロール、ドラッグ、タイムラインのアニメーション
- 04モーション低減へのアクセシビリティ対応
- 05パフォーマンスとバンドルサイズの指針
- 06再利用可能なアニメーションコンポーネントとバリアント
活用シーン
- 01React UIのアニメーション方式を選ぶ
- 02スクロール連動や共有要素の遷移を追加する
- 03アニメーションのアクセシビリティと性能を確認する
- 04再利用可能なゲームUIアニメーションを作る
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fcsouza/ agent-skills --skill react-animationsインストール後、Claude Code で/react-animationsを入力して読み込まれたか確認します。
同梱ファイルboilerplate/motion-components.tsxtemplates/animation-variants.ts
詳細
React アニメーション
Framer Motion(主軸)、React Spring(物理演算)、GSAP(タイムライン)、CSS/Tailwind(シンプルなケース)を使った、本番品質の React アニメーションです。
ライブラリ選択表
| ユースケース | ライブラリ | 理由 |
|---|---|---|
| 入場/退場アニメーション | Framer Motion | AnimatePresence がアンマウントを処理します |
| 共有要素のトランジション | Framer Motion | layoutId |
| 物理演算ベース(スプリング、バウンス) | Framer Motion または React Spring | spring 設定 |
| 複雑なタイムライン/シーケンス | GSAP | Timeline API |
| スクロールをトリガーにする | Framer Motion(whileInView / useScroll) | スクロール用フックを標準搭載しています |
| シンプルなホバー/フォーカス状態 | CSS Tailwind | JavaScript は不要です |
| ドラッグ&ドロップ | Framer Motion | ジェスチャーを標準サポートしています |
| SVG パスアニメーション | GSAP または Framer Motion | どちらも SVG をサポートしています |
| 命令型/プログラムによる制御 | Framer Motion useAnimate | 最新の命令型 API |
基本原則
Matt Perry(Framer Motion の開発者):「アニメーションは宣言的に記述し、命令型で管理すべきではありません。目標の状態を記述すれば、あとはライブラリが処理します。」 Sarah Drasner:「アニメーションは装飾ではなく、コミュニケーションです。すべての動きには目的があるべきです。」
- シンプルな場合は CSS、複雑な場合は JS — Tailwind の
transitionで対応できるなら、それを使いましょう。ホバー状態に Framer Motion を追加する必要はありません - 合成可能なプロパティだけをアニメーションさせる —
transformとopacityを使いましょう。width、height、top、leftはリフローを引き起こすため使わないでください - 条件付きレンダーは AnimatePresence で囲む — これがないと、退場アニメーションはスキップされます
- 連携するアニメーションには Variants を使う — アニメーション状態はインライン値ではなく、コンポーネント外でオブジェクトとして定義しましょう
- 共有要素のトランジションには layoutId を使う — Framer Motion が位置間の補間を処理します
- ジェスチャー駆動には useMotionValue を使う — アニメーションを駆動する値に useState を使わないでください
- 60fps の予算を守る — アニメーションのロジックをレンダーサイクルから切り離し、transform を使いましょう
Framer Motion の主なパターン
基本的なアニメーション
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
/>
ジェスチャー状態(whileHover、whileTap)
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
/>
子要素を段階的に表示する Variants
const container = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.1 },
},
};
const item = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
<motion.ul variants={container} initial="hidden" animate="visible">
{items.map((i) => (
<motion.li key={i} variants={item} />
))}
</motion.ul>
退場アニメーションを伴う AnimatePresence
mode プロパティは、入場する要素と退場する要素の動作を制御します。
"sync"(デフォルト)— 入場と退場を同時に実行します"wait"— 入場を開始する前に退場を完了します(ページ遷移に適しています)"popLayout"— 退場する要素をレイアウトフローからすぐに取り除きます
<AnimatePresence mode="wait">
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
/>
)}
</AnimatePresence>
スクロールをトリガーにするアニメーション用の whileInView
最もシンプルな方法です。スクロール用フックは必要ありません。
<motion.div
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.5 }}
/>
スクロールして戻ったときにアニメーションが再生されないよう、viewport.once: true を設定します。
パララックス用の useScroll + useTransform
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -200]);
<motion.div style={{ y }} />
共有要素用の layoutId
// In list view
<motion.div layoutId={`card-${id}`}>
<Thumbnail />
</motion.div>
// In detail view
<motion.div layoutId={`card-${id}`}>
<FullImage />
</motion.div>
制約付きドラッグ
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
dragElastic={0.2}
whileDrag={{ scale: 1.1 }}
/>
useAnimate — 命令型アニメーション(v11+)
プログラムによるシーケンスには、useAnimation よりも useAnimate を優先してください。ref のスコープ内で動作し、そのスコープ内の任意のセレクターを使用できます。
const [scope, animate] = useAnimate();
const handleClick = async () => {
await animate(scope.current, { scale: 1.2 }, { duration: 0.2 });
await animate(scope.current, { scale: 1 }, { duration: 0.1 });
};
<div ref={scope}>
<button onClick={handleClick}>Click me</button>
</div>
MotionConfig — グローバルなアニメーション設定
アプリ(またはサブツリー)をラップして、モーション低減やスプリングのプリセットなどのデフォルトを設定します。
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>
reducedMotion="user" を指定すると、OS レベルで「視差効果を減らす」設定をしているユーザーのアニメーションが自動的に無効になります。大規模にアクセシビリティへ対応する最も簡単な方法です。
LazyMotion — バンドルサイズの最適化
本番アプリでは、motion を LazyMotion + m に置き換えて、アニメーションエンジンをコード分割しましょう。
import { LazyMotion, domAnimation, m } from 'framer-motion';
<LazyMotion features={domAnimation}>
<m.div animate={{ opacity: 1 }} />
</LazyMotion>
ドラッグやレイアウトアニメーションが必要な場合は、domAnimation の代わりに domMax を使います。
ゲーム UI のパターン
滑らかなトゥイーンによる体力バー
const motionWidth = useMotionValue(current / max);
const springWidth = useSpring(motionWidth, { stiffness: 200, damping: 30 });
useEffect(() => {
motionWidth.set(Math.max(0, Math.min(1, current / max)));
}, [current, max]);
<motion.div style={{ scaleX: springWidth, transformOrigin: 'left' }} />
浮かび上がるダメージ数値
<motion.span
initial={{ opacity: 1, y: 0 }}
animate={{ opacity: 0, y: -60 }}
transition={{ duration: 0.8, ease: 'easeOut' }}
onAnimationComplete={onComplete}
>
-{damage}
</motion.span>
カードの反転(rotateY)
<motion.div animate={{ rotateY: isFlipped ? 180 : 0 }} style={{ perspective: 1000 }}>
<div style={{ backfaceVisibility: 'hidden' }}>{front}</div>
<div style={{ backfaceVisibility: 'hidden', rotateY: 180 }}>{back}</div>
</motion.div>
画面の揺れ(useAnimate)
const [scope, animate] = useAnimate();
const shake = async () => {
await animate(scope.current, { x: [0, -10, 10, -10, 10, 0] }, { duration: 0.4 });
};
<div ref={scope}>{children}</div>
メニューのスライドイン/スライドアウト
<AnimatePresence>
{isOpen && (
<motion.nav
initial={{ x: -300 }}
animate={{ x: 0 }}
exit={{ x: -300 }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
/>
)}
</AnimatePresence>
インベントリーアイテムのドロップ(スプリング物理演算)
<motion.div
initial={{ y: -200, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ type: 'spring', stiffness: 400, damping: 15 }}
/>
アクセシビリティ
ユーザーのモーション設定を必ず尊重してください。方法は 2 つあります。
1. MotionConfig(アプリに推奨) — コンポーネントツリー全体をラップします。
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>
2. useReducedMotion フック — コンポーネント単位で制御します。
const shouldReduceMotion = useReducedMotion();
<motion.div
animate={{ opacity: 1, y: shouldReduceMotion ? 0 : -20 }}
/>
パフォーマンス
transformとopacityのみをアニメーションさせます。GPU で合成され、レイアウトやペイントが発生しません- 常にアニメーションする要素には
will-change: transformを指定します(独立したレイヤーに昇格します) motion.create(Component)を使うと、ラッパーの div を追加せずにカスタムコンポーネントをアニメーションできますlayoutprop はレイアウトアニメーションを自動的に実行します。大きな DOM ツリーでは負荷が高いため、可能な限り小さなサブツリーに適用します- アニメーションを駆動する値には
useStateよりuseMotionValueを優先します。モーション値では再レンダリングが発生しません LazyMotionとdomAnimationを使うと、フルバンドルと比べて gzip サイズを約 15kb 削減できます
セットアップ
bun add framer-motion
設定不要です。プロバイダーは必要ありません。motion.div を直接インポートして使用します。
グローバルなアクセシビリティ対応を行うには、アプリのルートをラップします。
import { MotionConfig } from 'framer-motion';
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>
ボイラープレート
boilerplate/motion-components.tsx — すぐに使えるコンポーネント:FadeIn、SlideIn、ScaleIn、StaggerList、FloatingNumber、HealthBar、AnimatedCard
templates/animation-variants.ts — 再利用可能なバリアントオブジェクトとスプリングトランジションのプリセット
関連項目
vercel-react-best-practices— React のパフォーマンスパターンui-ux-game— ゲームの HUD と UI のパターンfrontend-design— コンポーネント設計と Tailwind のパターン
落とし穴とアンチパターン
- レイアウトを発生させるプロパティ(
width、height、top、left)をアニメーションさせる — 代わりにscaleX/scaleYまたはlayoutprop を使います exitprop の使用時に AnimatePresence を忘れる — ラッパーがないと、終了アニメーションは実行されずにスキップされます- レンダー中にモーション値を作成する —
useMotionValueフックを使います。レンダー中に作成するとメモリリークが発生します - 同じ要素で CSS の
transitionと Framer Motion を併用する — 競合するため、どちらか一方を選びます - アニメーションを過剰に使う — すべての操作をアニメーションさせると感覚的な負荷が高くなります。装飾ではなく、伝達のためにアニメーションを使います
initialを指定せずにマウント時にアニメーションさせる — アニメーション前にコンポーネントが一瞬表示されます。必ずinitialを設定しますuseAnimationを使う — 非推奨です。命令型アニメーションには代わりにuseAnimateを使います- 大規模な
layoutアニメーション — 深くネストされたツリーでlayoutprop を使うと、負荷の高い再計算が発生します。可能な限り小さなサブツリーに適用します - アクセシビリティ対応を省略する — 必ず
MotionConfig reducedMotion="user"またはuseReducedMotionを使います
参考資料
- Framer Motion のドキュメント — https://motion.dev
- Matt Perry — Framer Motion の開発者。API 設計に関する講演
- Sarah Drasner — 「SVG Animations」、アニメーション設計パターン
- GSAP のドキュメント — https://gsap.com