Motion(旧 Framer Motion)入門:React アニメーションを最速で始める

React でアニメーションを実装する際、CSS だけでは表現しきれない複雑な動きに悩まれたことはありませんか。また、コンポーネントの状態変化に応じて滑らかなアニメーションを作りたいけれど、実装が複雑すぎて諦めてしまった経験もあるでしょう。
そんな課題を解決してくれるのが Motion です。Motion は React のためのアニメーションライブラリで、簡単な記述で美しく滑らかなアニメーションを実装できます。この記事では、Motion の基本から実践的な使い方まで、初心者の方にもわかりやすく解説していきます。
Motion とは何か
Motion は React 専用に設計されたアニメーションライブラリです。以前は Framer Motion という名前で親しまれていましたが、現在は Motion として新しくリブランドされています。React コンポーネントに直感的で美しいアニメーションを追加できる強力なツールとして、多くの開発者に愛用されています。
Framer Motion から Motion への変更点
2024 年、Framer Motion は Motion として新しく生まれ変わりました。名前の変更だけでなく、パッケージの構造や API も改良されています。
主な変更点は以下の通りです:
# | 変更項目 | Framer Motion | Motion |
---|---|---|---|
1 | パッケージ名 | framer-motion | motion |
2 | バンドルサイズ | やや大きめ | 最適化済み |
3 | TypeScript サポート | 標準対応 | より強化 |
4 | パフォーマンス | 高性能 | さらに最適化 |
従来の Framer Motion をお使いの方も、移行は比較的簡単です。API の大部分は互換性があり、主にインポート文の変更程度で済みます。
React アニメーションライブラリとしての位置づけ
Motion は React エコシステムにおいて、アニメーションライブラリのデファクトスタンダードと言える存在です。
React アニメーションライブラリの比較を見てみましょう:
# | ライブラリ | 特徴 | 適用場面 |
---|---|---|---|
1 | Motion | 宣言的、高機能 | 複雑なアニメーション |
2 | React Transition Group | 基本的、軽量 | シンプルな遷移 |
3 | React Spring | 物理ベース | リアルな動き |
4 | Lottie React | After Effects 連携 | 複雑なイラストアニメーション |
Motion が選ばれる理由は、React の思想に沿った宣言的な記述方法と、豊富な機能性のバランスにあります。
基本的な課題
React でアニメーションを実装する際、開発者が直面する課題について詳しく見ていきましょう。
従来の CSS アニメーションの限界
CSS アニメーションは手軽に使える反面、React アプリケーションでは以下のような制約があります。
CSS だけでは困難な制御
typescript// React コンポーネントの状態に応じた動的なアニメーション
const [isExpanded, setIsExpanded] = useState(false);
// CSS だけでは以下のような複雑な制御が難しい
// - 状態に応じた異なるアニメーション
// - 動的な値(props)を使ったアニメーション
// - アニメーション完了時のコールバック
この例では、単純な状態変更でも CSS だけでは表現が困難な場面を示しています。
タイミング制御の複雑さ
css/* CSS でのアニメーション定義は静的 */
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* 動的な値やタイミングの変更が困難 */
CSS アニメーションは事前定義された動きしか実現できません。ユーザーの操作や API レスポンスに応じて動的に変化するアニメーションには不向きです。
React での状態変化に伴うアニメーションの難しさ
React の状態管理とアニメーションの組み合わせには独特の困難さがあります。
以下の図は、React での状態変化とアニメーションのタイミング問題を示しています:
mermaidsequenceDiagram
participant User
participant Component
participant DOM
User->>Component: クリック
Component->>Component: setState
Component->>DOM: 再レンダリング
Note over DOM: アニメーション途中で<br/>DOM が更新される
DOM->>User: ちらつき発生
この図が示すように、状態変更と同時に DOM が更新されるため、アニメーションが中断されたり不自然な動きになることがあります。
コンポーネントのマウント・アンマウント問題
typescriptconst ConditionalComponent = ({
show,
}: {
show: boolean;
}) => {
return (
<>
{show && (
<div className='fade-in'>
{/* show が false になると即座に DOM から削除される */}
{/* アニメーション途中でも容赦なく削除される */}
</div>
)}
</>
);
};
React の条件付きレンダリングでは、コンポーネントが即座にマウント・アンマウントされるため、フェードアウトアニメーションを自然に実装するのが困難です。
Motion で解決できること
Motion は上記の課題を包括的に解決する、React に最適化されたアニメーションライブラリです。
宣言的アニメーション
Motion の最大の特徴は、アニメーションを宣言的に記述できることです。
以下は Motion を使った基本的なアニメーションの例です:
typescriptimport { motion } from 'motion/react';
const AnimatedComponent = () => {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3 }}
>
Hello Motion!
</motion.div>
);
};
この短いコードで、フェードインとスケールアニメーションを同時に実現できます。CSS のように別ファイルでキーフレームを定義する必要もありません。
動的な値との連携
typescriptconst DynamicAnimation = ({ color, size }: Props) => {
return (
<motion.div
animate={{
backgroundColor: color, // props から動的に設定
width: size, // 値が変わると自動でアニメーション
height: size,
}}
transition={{ duration: 0.5 }}
>
Dynamic content
</motion.div>
);
};
props や state の変更に応じて、自動的にアニメーションが適用されます。これにより、複雑な状態管理アプリケーションでも自然なアニメーションを実現できます。
React との完璧な統合
Motion は React のライフサイクルと完全に統合されており、以下のような問題を解決します:
条件付きレンダリングでのアニメーション制御
typescriptimport { AnimatePresence } from 'motion/react';
const ConditionalAnimation = ({
show,
}: {
show: boolean;
}) => {
return (
<AnimatePresence>
{show && (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }} // アンマウント時のアニメーション
transition={{ duration: 0.2 }}
>
アンマウント時もアニメーション!
</motion.div>
)}
</AnimatePresence>
);
};
AnimatePresence
コンポーネントを使うことで、コンポーネントがアンマウントされる際も美しいアニメーションを実現できます。
豊富な機能とパフォーマンス
Motion は高度なアニメーション機能を提供しながら、パフォーマンスも最適化されています。
Motion の主要機能一覧:
# | 機能カテゴリ | 具体的な機能 | 用途例 |
---|---|---|---|
1 | 基本アニメーション | opacity, scale, position | フェード、拡大縮小 |
2 | レイアウトアニメーション | layout プロパティ | サイズ変更の滑らかな遷移 |
3 | ジェスチャー | drag, hover, tap | インタラクティブ UI |
4 | SVG アニメーション | path, stroke | アイコンアニメーション |
5 | スクロール連動 | useScroll, useTransform | パララックス効果 |
以下の図は Motion のアニメーション処理フローを示しています:
mermaidflowchart TD
A[React State 変更] --> B{Motion コンポーネント}
B --> C[アニメーション計算]
C --> D[60fps での更新]
D --> E[GPU 最適化された描画]
E --> F[滑らかな視覚効果]
B --> G[イベントハンドラ]
G --> H[ジェスチャー認識]
H --> A
Motion は内部で高度な最適化を行っており、開発者が複雑な計算を意識することなく、滑らかなアニメーションを実現できます。
環境構築と基本セットアップ
Motion を使い始めるための環境構築から説明していきます。既存の React プロジェクトに簡単に導入できる点も Motion の魅力の一つです。
インストール方法
Motion のインストールは Yarn を使って行います。
bash# Motion パッケージのインストール
yarn add motion
このコマンドで Motion の最新版がプロジェクトに追加されます。依存関係も自動的に解決され、すぐに使い始めることができます。
TypeScript プロジェクトでの追加設定
TypeScript を使用している場合、型定義は既に含まれているため、追加の設定は不要です。
typescript// tsconfig.json の設定例(必要に応じて)
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
}
}
Motion は TypeScript ファーストで設計されており、強力な型サポートを提供しています。
基本的な設定
Motion を使うための基本的な設定とインポート方法を確認しましょう。
基本的なインポート
typescriptimport { motion } from 'motion/react';
// 必要に応じて追加のインポート
import {
AnimatePresence,
useAnimation,
} from 'motion/react';
Motion の主要な機能は motion/react
からインポートできます。基本的なアニメーションなら motion
だけで十分です。
最初のアニメーション実装
最も簡単な Motion アニメーションを実装してみましょう。
typescriptimport React from 'react';
import { motion } from 'motion/react';
const FirstAnimation = () => {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
>
Welcome to Motion!
</motion.div>
);
};
export default FirstAnimation;
この例では、ページ読み込み時にテキストがフェードインします。motion.div
は通常の div
要素にアニメーション機能を追加したものです。
基本的なアニメーション実装
Motion の基本的なアニメーションパターンを実際に実装してみましょう。実用的な例から学ぶことで、すぐに応用できる知識が身につきます。
フェードイン・フェードアウト
最も基本的なアニメーションであるフェード効果の実装方法です。
基本的なフェードイン
typescriptimport React, { useState } from 'react';
import { motion } from 'motion/react';
const FadeAnimation = () => {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
トグル
</button>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
style={{
marginTop: '20px',
padding: '20px',
background: '#f0f0f0',
}}
>
フェードイン・アウトする要素
</motion.div>
)}
</div>
);
};
この実装では、ボタンクリックで要素の表示・非表示が切り替わり、その際にフェードアニメーションが適用されます。
AnimatePresence を使った改良版
typescriptimport { AnimatePresence, motion } from 'motion/react';
const ImprovedFadeAnimation = () => {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? '非表示' : '表示'}
</button>
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
AnimatePresence で制御されたフェード
</motion.div>
)}
</AnimatePresence>
</div>
);
};
AnimatePresence
を使うことで、コンポーネントがアンマウントされる際のアニメーションも適切に制御できます。
スケールアニメーション
要素の拡大・縮小アニメーションは、ユーザーの注意を引くのに効果的です。
ホバー時のスケール効果
typescriptconst ScaleOnHover = () => {
return (
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 300 }}
style={{
padding: '12px 24px',
backgroundColor: '#007acc',
color: 'white',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
}}
>
ホバーでスケール
</motion.button>
);
};
whileHover
と whileTap
を使うことで、マウスの状態に応じたインタラクティブなアニメーションを簡単に実装できます。
カードのエントランスアニメーション
typescriptconst CardEntrance = ({ children, delay = 0 }) => {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{
duration: 0.4,
delay,
type: 'spring',
stiffness: 100,
}}
style={{
padding: '20px',
margin: '10px',
backgroundColor: 'white',
borderRadius: '12px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
}}
>
{children}
</motion.div>
);
};
この例では、カードが下から上に浮き上がりながらフェードインするエレガントなエントランスアニメーションを実現しています。
位置移動アニメーション
要素の移動アニメーションは、UI の流れを明確にし、ユーザーの理解を助けます。
スライドイン効果
typescriptconst SlideInAnimation = ({ direction = 'left' }) => {
const getInitialPosition = () => {
switch (direction) {
case 'left':
return { x: -100, opacity: 0 };
case 'right':
return { x: 100, opacity: 0 };
case 'top':
return { y: -50, opacity: 0 };
case 'bottom':
return { y: 50, opacity: 0 };
default:
return { x: -100, opacity: 0 };
}
};
return (
<motion.div
initial={getInitialPosition()}
animate={{ x: 0, y: 0, opacity: 1 }}
transition={{ duration: 0.4, ease: 'easeOut' }}
>
{direction} からスライドイン
</motion.div>
);
};
方向を指定してスライドインアニメーションを実装できる再利用可能なコンポーネントです。
ドラッグ可能な要素
typescriptconst DraggableElement = () => {
return (
<motion.div
drag
dragConstraints={{
left: -100,
right: 100,
top: -100,
bottom: 100,
}}
dragElastic={0.2}
whileDrag={{ scale: 1.1, cursor: 'grabbing' }}
style={{
width: '100px',
height: '100px',
backgroundColor: '#ff6b6b',
borderRadius: '12px',
cursor: 'grab',
}}
>
ドラッグできます
</motion.div>
);
};
drag
プロパティを追加するだけで、要素をドラッグ可能にできます。dragConstraints
で移動範囲を制限し、whileDrag
でドラッグ中の見た目を変更できます。
Motion のアニメーション実装パターンを整理すると以下のようになります:
# | アニメーション種類 | 主な用途 | 実装の難易度 |
---|---|---|---|
1 | フェード | 要素の表示・非表示 | 易 |
2 | スケール | ボタンのフィードバック | 易 |
3 | 位置移動 | ページ遷移、スライド | 中 |
4 | レイアウトアニメーション | 動的サイズ変更 | 中 |
5 | ジェスチャー | ドラッグ、スワイプ | やや難 |
レスポンシブ対応とベストプラクティス
Motion を実際のプロジェクトで使用する際の実践的な考慮事項とベストプラクティスについて説明します。
デバイス別最適化
モバイルデバイスでのパフォーマンスを考慮した実装方法です。
reduced-motion への配慮
typescriptimport { motion, useReducedMotion } from 'motion/react';
const AccessibleAnimation = ({ children }) => {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
animate={{
opacity: 1,
x: shouldReduceMotion ? 0 : 100, // アニメーション無効時は移動なし
}}
transition={{
duration: shouldReduceMotion ? 0 : 0.5, // アニメーション無効時は即座に
}}
>
{children}
</motion.div>
);
};
ユーザーのシステム設定でアニメーションを減らす設定にしている場合、適切に対応することでアクセシビリティが向上します。
デバイス別のアニメーション調整
typescriptconst ResponsiveAnimation = () => {
const isMobile = window.innerWidth < 768;
const animationVariants = {
hidden: {
opacity: 0,
y: isMobile ? 20 : 50, // モバイルでは移動量を控えめに
},
visible: {
opacity: 1,
y: 0,
},
};
return (
<motion.div
initial='hidden'
animate='visible'
variants={animationVariants}
transition={{
duration: isMobile ? 0.2 : 0.4, // モバイルでは高速化
}}
>
レスポンシブアニメーション
</motion.div>
);
};
デバイスの特性に応じてアニメーションの強度や速度を調整することで、最適な体験を提供できます。
パフォーマンス考慮事項
Motion を使用する際の重要なパフォーマンス最適化ポイントです。
will-change プロパティの自動最適化
typescriptconst OptimizedAnimation = () => {
return (
<motion.div
animate={{ x: 100 }}
transition={{ duration: 2 }}
// Motion が自動的に will-change を適用
// GPU レイヤーでの描画が最適化される
>
最適化されたアニメーション
</motion.div>
);
};
Motion は自動的に will-change
CSS プロパティを適用し、GPU での描画を最適化します。開発者が手動で設定する必要はありません。
大量要素のアニメーション最適化
typescriptconst ListAnimation = ({ items }: { items: string[] }) => {
return (
<div>
{items.map((item, index) => (
<motion.div
key={item}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{
delay: index * 0.1, // 段階的なアニメーション
duration: 0.3,
}}
layout // レイアウト変更時の自動アニメーション
>
{item}
</motion.div>
))}
</div>
);
};
大量の要素をアニメーションする際は、遅延を使った段階的な表示で視覚的なインパクトを演出しながら、パフォーマンスも保てます。
Motion のパフォーマンス最適化における処理の流れを図で示すと:
mermaidflowchart LR
A[アニメーション開始] --> B{GPU 対応確認}
B -->|対応済み| C[GPU レイヤー利用]
B -->|未対応| D[CPU フォールバック]
C --> E[60fps での描画]
D --> F[最適化された CPU 描画]
E --> G[滑らかなアニメーション]
F --> G
Motion は環境に応じて最適な描画方法を自動選択し、常に最高のパフォーマンスを維持します。
メモリリークの防止
typescriptimport { useEffect } from 'react';
import { motion, useAnimation } from 'motion/react';
const SafeAnimation = () => {
const controls = useAnimation();
useEffect(() => {
const animation = controls.start({ rotate: 360 });
// クリーンアップでアニメーションを停止
return () => {
animation.then((anim) => anim.stop());
};
}, [controls]);
return (
<motion.div animate={controls}>
安全なアニメーション
</motion.div>
);
};
アニメーション制御オブジェクトを適切にクリーンアップすることで、メモリリークを防げます。
まとめ
Motion は React でのアニメーション実装を革新的に簡単にしてくれる素晴らしいライブラリです。従来の CSS アニメーションでは困難だった複雑な制御も、宣言的で直感的な記述で実現できます。
この記事でご紹介した内容をおさらいしましょう:
- Motion の概要: Framer Motion から進化した React 専用アニメーションライブラリ
- 課題解決: CSS の限界や React での状態管理の複雑さを解消
- 基本実装: フェード、スケール、位置移動の基本パターン
- 最適化: レスポンシブ対応とパフォーマンス考慮
Motion を使うことで、ユーザー体験を大幅に向上させる美しいアニメーションを、少ないコードで実現できます。まずは簡単なフェードアニメーションから始めて、徐々に高度な機能に挑戦してみてください。
モダンな Web アプリケーション開発において、Motion は必須のツールと言えるでしょう。ぜひ皆様のプロジェクトにも導入して、ユーザーに愛されるインターフェースを作ってみてください。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来