Motion(旧 Framer Motion)基本 API 徹底解説:motion 要素・initial/animate/exit の正しい使い方

React でアニメーションを実装する際、Motion は最も強力で直感的なライブラリの一つです。従来の複雑なアニメーション実装から解放され、わずか数行のコードで美しいアニメーションが作成できる魅力があります。
この記事では、Motion の基本となる motion
要素と、initial
、animate
、exit
の 3 つの核となるプロパティの正しい使い方を、実践的なコード例とともに詳しく解説していきます。初心者の方でも段階的に理解できるよう、基礎から応用まで丁寧に説明いたします。
背景
Motion とは何か
Motion は React 専用のアニメーションライブラリで、宣言的な記述でスムーズなアニメーションを実現できます。2018 年に Framer Motion として登場し、現在は Motion として継続開発されています。
最大の特徴は、複雑な JavaScript のアニメーション処理を React のコンポーネント記法に自然に統合できる点です。従来のアニメーション実装で必要だった手動での DOM 操作や状態管理が不要になり、React の思想に沿った形でアニメーションが記述できるのです。
React アニメーションライブラリの現状
React エコシステムには様々なアニメーションライブラリが存在しています。以下に主要なライブラリの特徴をまとめます。
ライブラリ名 | 特徴 | 学習コストの高さ | パフォーマンス |
---|---|---|---|
Motion | 宣言的、豊富な機能 | 低 | 高 |
React Spring | Hook ベース、物理演算 | 中 | 高 |
React Transition Group | 軽量、基本的な遷移 | 低 | 中 |
Lottie React | After Effects 連携 | 低 | 中 |
Motion は他のライブラリと比較して、記述のシンプルさとパフォーマンスのバランスが優秀です。特に、CSS のアニメーションプロパティに近い感覚で書けるため、フロントエンド開発者にとって直感的に扱えるのが大きなメリットです。
Motion の位置づけと特徴
現代の Web 開発において、アニメーションは単なる装飾ではなく、ユーザーエクスペリエンスを向上させる重要な要素となりました。Motion はこの流れに対応し、以下の特徴を持っています。
- 宣言的記述: アニメーションの最終状態を記述するだけで、中間フレームは自動生成
- 高パフォーマンス: GPU アクセラレーションを活用したスムーズな動作
- TypeScript 完全対応: 型安全性を保ちながら開発可能
- 豊富な API: シンプルなフェードインから複雑なジェスチャーアニメーションまで対応
以下の図は、Motion がどのように React コンポーネントと統合されるかを示しています。
mermaidflowchart TB
react[React コンポーネント] --> motion[motion 要素]
motion --> props[アニメーションプロパティ]
props --> initial[初期状態定義]
props --> animate[アニメーション状態]
props --> exit[終了状態]
initial --> render[DOM レンダリング]
animate --> render
exit --> render
render --> gpu[GPU アクセラレーション]
gpu --> smooth[スムーズアニメーション]
この構成により、React の仮想 DOM と Motion のアニメーションエンジンがシームレスに連携し、最適化されたアニメーションが実現されます。
課題
従来のCSS アニメーションの限界
CSS だけでアニメーションを実装する場合、以下のような課題があります。
状態管理の複雑さ
CSS のキーフレームアニメーションは、アニメーションの開始・終了タイミングを JavaScript で制御する必要があります。これにより、React の状態管理とアニメーションの同期が困難になることがありました。
css/* 複雑なクラス管理が必要 */
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 300ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 300ms;
}
動的なアニメーションの実装困難さ
プロパティの値を動的に変更したり、条件に応じてアニメーションを切り替えたりする場合、CSS だけでは限界があります。JavaScript での細かい制御が必要になり、コードが複雑になってしまうのです。
React でのアニメーション実装の難しさ
React の仮想 DOM の仕組みは、直接的な DOM 操作を前提とした従来のアニメーションライブラリとは相性が良くありませんでした。
ライフサイクルとの同期問題
React コンポーネントのマウント・アンマウントとアニメーションのタイミングを同期させるのが複雑でした。特に、コンポーネントが削除される前にアニメーションを完了させる処理は、多くの開発者を悩ませてきました。
再レンダリングによる処理中断
React の再レンダリングによって、実行中のアニメーションが中断されたり、期待した動作にならなかったりする問題がありました。
以下の図は、従来のアニメーション実装における課題を示しています。
mermaidsequenceDiagram
participant React as React コンポーネント
participant CSS as CSS アニメーション
participant JS as JavaScript 制御
participant DOM as DOM 要素
React->>DOM: 初期レンダリング
React->>JS: アニメーション開始指示
JS->>CSS: クラス付与
CSS->>DOM: アニメーション実行
Note over React,DOM: 再レンダリング発生
React->>DOM: 強制更新
CSS-->>DOM: アニメーション中断
Note over React,DOM: 同期がとれない状態
このシーケンス図から分かるように、React の更新サイクルと CSS アニメーションの実行タイミングにずれが生じることで、アニメーションが中途半端な状態で終わってしまう問題がありました。
パフォーマンスとUX の両立
美しいアニメーションを実現したいものの、パフォーマンスを犠牲にするわけにはいきません。特に以下の点が課題でした。
60fps を維持する難しさ
スムーズなアニメーションには 60fps(16.67ms ごとの更新)が必要ですが、JavaScript の処理負荷によってフレームレートが低下しやすい問題がありました。
メモリリークのリスク
アニメーション処理が適切に終了せず、メモリリークを起こすリスクがありました。特に、タイマーやリスナーの適切な解放処理を忘れがちでした。
解決策
Motion の基本コンセプト
Motion は上記の課題を解決するために、以下のコンセプトを採用しています。
宣言的アニメーション記述
従来の命令的な記述(「これをして、次にこれをして...」)ではなく、宣言的な記述(「最終的にこの状態になる」)でアニメーションを定義できます。これにより、コードの可読性が向上し、メンテナンスが容易になります。
React ライフサイクルとの完全統合
Motion は React のコンポーネントライフサイクルと完全に統合されており、マウント・アンマウント時のアニメーションを自動的に管理してくれます。
motion 要素の仕組み
Motion の中核となるのが motion
要素です。通常の HTML 要素を motion.div
、motion.span
などの motion 要素に置き換えることで、アニメーション機能が利用できます。
motion 要素は以下のような仕組みで動作します:
mermaidflowchart LR
component[React コンポーネント] --> motion_element[motion.div]
motion_element --> props[アニメーションプロパティ]
subgraph "Motion エンジン"
props --> parser[プロパティ解析]
parser --> timeline[タイムライン生成]
timeline --> renderer[レンダラー]
end
renderer --> dom[DOM 更新]
dom --> gpu[GPU アクセラレーション]
gpu --> animation[スムーズアニメーション]
この仕組みにより、開発者は複雑なアニメーション処理を意識することなく、プロパティの設定だけでアニメーションを実現できます。
宣言的アニメーションのメリット
宣言的アニメーション記述により、以下のメリットが得られます:
コードの簡潔性
従来 50 行以上必要だったアニメーション処理が、わずか数行で記述できるようになります。
デバッグのしやすさ
アニメーションの状態がプロパティとして明確に定義されるため、問題の特定が容易です。
再利用性の向上
アニメーションパターンをコンポーネント化して、他の場所で簡単に再利用できます。
具体例
motion 要素の基本使用法
まず、Motion を使用するための基本的なセットアップから始めましょう。
インストール
bashyarn add motion
基本的なインポート
typescriptimport { motion } from 'motion/react'
Motion では、通常の HTML 要素を motion
プレフィックス付きの要素に置き換えることで、アニメーション機能を追加できます。
最も基本的な motion 要素
tsxfunction BasicExample() {
return (
<motion.div
style={{
width: 100,
height: 100,
backgroundColor: '#3b82f6'
}}
>
基本的な motion 要素
</motion.div>
)
}
この時点では通常の div
要素と同じように動作しますが、アニメーションプロパティを追加することで動きを付けられるようになります。
initial プロパティの詳細解説
initial
プロパティは、アニメーションの開始状態を定義します。コンポーネントがマウントされた瞬間の状態を指定できます。
基本的な initial の使い方
tsxfunction FadeInExample() {
return (
<motion.div
initial={{ opacity: 0 }}
style={{
width: 200,
height: 100,
backgroundColor: '#10b981'
}}
>
フェードインする要素
</motion.div>
)
}
上記のコードでは、要素は透明度 0(完全に透明)の状態で表示されます。まだアニメーションは発生しませんが、初期状態が設定されました。
複数のプロパティを同時に設定
tsxfunction MultipleInitialExample() {
return (
<motion.div
initial={{
opacity: 0,
scale: 0.8,
y: 20
}}
style={{
width: 200,
height: 100,
backgroundColor: '#8b5cf6'
}}
>
複数の初期状態を持つ要素
</motion.div>
)
}
この例では、透明度、スケール、Y 軸の位置を同時に初期状態として設定しています。要素は半透明で小さく、少し下にずれた状態で表示されます。
initial を false に設定
tsxfunction NoInitialExample() {
return (
<motion.div
initial={false}
style={{
width: 200,
height: 100,
backgroundColor: '#ef4444'
}}
>
初期アニメーションをスキップ
</motion.div>
)
}
initial={false}
を指定すると、初期アニメーションがスキップされ、要素は通常の状態で即座に表示されます。
animate プロパティの活用方法
animate
プロパティは、要素が到達すべき最終状態を定義します。Motion は自動的に initial
から animate
への遷移アニメーションを生成します。
基本的なフェードイン アニメーション
tsxfunction FadeInAnimation() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
style={{
width: 200,
height: 100,
backgroundColor: '#06b6d4'
}}
>
フェードインアニメーション
</motion.div>
)
}
この例では、透明度 0 から 1 への滑らかな遷移が自動的に生成されます。デフォルトのアニメーション時間は 0.3 秒です。
複雑な変形アニメーション
tsxfunction ComplexAnimation() {
return (
<motion.div
initial={{
opacity: 0,
scale: 0.5,
rotate: -10,
y: 50
}}
animate={{
opacity: 1,
scale: 1,
rotate: 0,
y: 0
}}
transition={{
duration: 0.6,
ease: "easeOut"
}}
style={{
width: 150,
height: 150,
backgroundColor: '#f59e0b',
borderRadius: 8
}}
>
複雑な変形アニメーション
</motion.div>
)
}
この例では、フェードイン、スケール変更、回転、位置移動を同時に実行します。transition
プロパティで、アニメーション時間とイージング関数をカスタマイズしています。
状態に応じた動的なアニメーション
tsxfunction DynamicAnimation() {
const [isHovered, setIsHovered] = useState(false)
return (
<motion.button
animate={{
scale: isHovered ? 1.1 : 1,
backgroundColor: isHovered ? '#3b82f6' : '#6b7280'
}}
onHoverStart={() => setIsHovered(true)}
onHoverEnd={() => setIsHovered(false)}
style={{
padding: '12px 24px',
border: 'none',
borderRadius: 6,
color: 'white',
cursor: 'pointer'
}}
>
ホバーでアニメーション
</motion.button>
)
}
状態が変更されるたびに、Motion は自動的に現在の状態から新しい animate
状態への遷移を生成します。
exit プロパティとアンマウント時の処理
exit
プロパティは、コンポーネントが DOM から削除される際のアニメーションを定義します。ただし、exit アニメーションを実行するには AnimatePresence
コンポーネントが必要です。
基本的なexit アニメーション
tsximport { motion, AnimatePresence } from 'motion/react'
function ExitExample() {
const [isVisible, setIsVisible] = useState(true)
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? '非表示にする' : '表示する'}
</button>
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3 }}
style={{
width: 200,
height: 100,
backgroundColor: '#ec4899',
marginTop: 20
}}
>
exit アニメーション
</motion.div>
)}
</AnimatePresence>
</div>
)
}
AnimatePresence
により、コンポーネントが削除される前に exit アニメーションが実行され、完了後に DOM から削除されます。
リストアイテムの削除アニメーション
tsxfunction TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'React を学ぶ' },
{ id: 2, text: 'Motion をマスターする' },
{ id: 3, text: 'アプリを公開する' }
])
const removeTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id))
}
return (
<ul style={{ listStyle: 'none', padding: 0 }}>
<AnimatePresence>
{todos.map((todo) => (
<motion.li
key={todo.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20, backgroundColor: '#fef2f2' }}
transition={{ duration: 0.2 }}
style={{
padding: 12,
marginBottom: 8,
backgroundColor: '#f3f4f6',
borderRadius: 6,
cursor: 'pointer'
}}
onClick={() => removeTodo(todo.id)}
>
{todo.text}
</motion.li>
))}
</AnimatePresence>
</ul>
)
}
この例では、リストアイテムがクリックされると右にスライドしながらフェードアウトし、背景色も変化して削除されることを視覚的に示します。
実践的なアニメーション例
ページ遷移アニメーション
tsxfunction PageTransition({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{
duration: 0.3,
ease: "easeInOut"
}}
>
{children}
</motion.div>
)
}
// 使用例
function App() {
const [currentPage, setCurrentPage] = useState('home')
return (
<AnimatePresence mode="wait">
{currentPage === 'home' && (
<PageTransition key="home">
<HomePage />
</PageTransition>
)}
{currentPage === 'about' && (
<PageTransition key="about">
<AboutPage />
</PageTransition>
)}
</AnimatePresence>
)
}
mode="wait"
により、前のページの exit アニメーションが完了してから、次のページの enter アニメーションが開始されます。
ステージングアニメーション(順次表示)
tsxfunction StaggeredList() {
const items = ['項目 1', '項目 2', '項目 3', '項目 4']
return (
<motion.ul
initial="hidden"
animate="visible"
variants={{
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}}
style={{ listStyle: 'none', padding: 0 }}
>
{items.map((item, index) => (
<motion.li
key={index}
variants={{
hidden: { opacity: 0, x: -20 },
visible: { opacity: 1, x: 0 }
}}
style={{
padding: 12,
marginBottom: 8,
backgroundColor: '#dbeafe',
borderRadius: 6
}}
>
{item}
</motion.li>
))}
</motion.ul>
)
}
staggerChildren
により、子要素が順次アニメーションされ、美しいカスケード効果が生まれます。
カスタムトランジション設定
tsxfunction CustomTransition() {
const [isExpanded, setIsExpanded] = useState(false)
return (
<motion.div
animate={{
height: isExpanded ? 200 : 60,
backgroundColor: isExpanded ? '#dcfce7' : '#f3f4f6'
}}
transition={{
height: {
type: "spring",
stiffness: 300,
damping: 30
},
backgroundColor: {
duration: 0.2
}
}}
style={{
width: 300,
borderRadius: 8,
padding: 16,
cursor: 'pointer',
overflow: 'hidden'
}}
onClick={() => setIsExpanded(!isExpanded)}
>
<h3 style={{ margin: 0 }}>展開可能なカード</h3>
{isExpanded && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
style={{ margin: '12px 0 0 0' }}
>
詳細な内容がここに表示されます。このテキストは展開時にフェードインします。
</motion.p>
)}
</motion.div>
)
}
プロパティごとに異なるトランジションを設定することで、より細かなアニメーション制御が可能です。高さにはバネ物理演算、背景色には線形遷移を適用しています。
以下の図は、initial・animate・exit の実行フローを示しています:
mermaidstateDiagram-v2
[*] --> Initial: コンポーネントマウント
Initial --> Animate: 自動遷移
Animate --> StateChange: プロパティ変更
StateChange --> Animate: 新しい状態へアニメーション
Animate --> Exit: アンマウント開始
Exit --> [*]: DOM から削除
note right of Initial
initial プロパティで定義
マウント時の初期状態
end note
note right of Animate
animate プロパティで定義
目標となる状態
end note
note right of Exit
exit プロパティで定義
削除時のアニメーション
end note
この状態遷移により、コンポーネントのライフサイクル全体を通してスムーズなアニメーションが実現されます。
図で理解できる要点:
- initial → animate は自動的に実行される
- animate 状態では、プロパティ変更に応じて新しいアニメーションが生成される
- exit は AnimatePresence 内でのみ実行される
まとめ
Motion(旧 Framer Motion)の基本 API である motion
要素と initial
、animate
、exit
プロパティの使い方を詳しく解説いたしました。
重要なポイントをまとめると以下の通りです:
- motion 要素: 通常の HTML 要素にアニメーション機能を追加
- initial プロパティ: マウント時の初期状態を定義
- animate プロパティ: 目標となる最終状態を宣言的に記述
- exit プロパティ: AnimatePresence と組み合わせてアンマウント時のアニメーションを制御
Motion を活用することで、React アプリケーションに美しく滑らかなアニメーションを簡単に追加できます。宣言的な記述により、コードの可読性とメンテナンス性も大幅に向上するでしょう。
次のステップとして、より高度な機能である variants
や gestures
の活用、パフォーマンス最適化のテクニックを学んでいくことをお勧めいたします。
関連リンク
- article
Gemini CLI のプロンプト設計術:高精度応答を引き出すテクニック 20 選
- article
gpt-oss と OpenAI GPT の違いを徹底比較【コスト・性能・自由度】
- article
【保存版】Git のタグ(tag)の使い方とリリース管理のベストプラクティス
- article
JavaScript の this キーワードを完全理解!初心者がつまずくポイント解説
- article
GPT-5 で作る AI アプリ:チャットボットから自動化ツールまでの開発手順
- article
Motion(旧 Framer Motion)基本 API 徹底解説:motion 要素・initial/animate/exit の正しい使い方
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来