framer-motion-layout について
framer-motion-layoutは、Framer Motionでレイアウトや共有要素のアニメーションを実装するスキルです。自動レイアウト変更、layoutIdによる遷移、AnimatePresenceの退出、並べ替え可能なリストを扱います。
主な機能
- 01位置とサイズの自動アニメーション
- 02layoutIdによる共有要素の遷移
- 03AnimatePresenceの退出アニメーション
- 04並べ替え可能なリストのパターン
- 05マウントとアンマウントの連携アニメーション
活用シーン
- 01リストの順序変更に合わせたアニメーション
- 02ページ間でのカード遷移
- 03元の項目からモーダルへの遷移
- 04要素の退出と表示の順序制御
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ c-jeril/ framer-motion-skills --skill framer-motion-layoutインストール後、Claude Code で/framer-motion-layoutを入力して読み込まれたか確認します。
詳細
Framer Motion のレイアウトアニメーション
このスキルを使う場面
共有要素のトランジション、並べ替えのレイアウトアニメーション、または連動するマウント/アンマウントアニメーションを実装するときに適用します。ユーザーから Framer Motion のレイアウトアニメーション、layoutId、または AnimatePresence について尋ねられた場合にも使います。
関連スキル: 基本的なアニメーションには framer-motion-core、バリアントには framer-motion-variants、React との連携には framer-motion-react を使います。
Layout Prop
layout プロパティを指定すると、レイアウトの変更時に位置のアニメーションが自動で有効になります。
<motion.div layout>
{items.map(item => (
<motion.div key={item.id} layout />
))}
</motion.div>
レイアウトモード
| モード | 動作 |
|---|---|
true | 位置とサイズをアニメーション化します |
"position" | 位置のみをアニメーション化します |
"size" | サイズのみをアニメーション化します |
<motion.div layout="position" />
<motion.div layout="size" />
共有要素のトランジションに使う layoutId
layoutId を指定すると、異なるコンポーネント間で要素を滑らかにトランジションさせられます。
ページ遷移
// Page A
function CardA() {
return <motion.div layoutId="card" />;
}
// Page B
function CardB() {
return <motion.div layoutId="card" />;
}
同じ layoutId を持つ CardA がアンマウントし、CardB がマウントされると、Framer Motion が要素を滑らかに位置間でアニメーション化します。
モーダルオーバーレイ
function ListItem({ item, onClick }) {
return (
<motion.div layoutId={`item-${item.id}`} onClick={onClick}>
{item.name}
</motion.div>
);
}
function Modal({ item }) {
return (
<motion.div layoutId={`item-${item.id}`}>
<h2>{item.name}</h2>
<p>{item.description}</p>
</motion.div>
);
}
レイアウトで使う AnimatePresence
AnimatePresence を使うと、終了アニメーションが有効になります。
import { AnimatePresence, motion } from "framer-motion";
function TodoList({ todos }) {
return (
<motion.ul>
<AnimatePresence>
{todos.map(todo => (
<motion.li
key={todo.id}
layout
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
/>
))}
</AnimatePresence>
</motion.ul>
);
}
AnimatePresence のモード
<AnimatePresence mode="wait">
{isOpen && <Modal key="modal" />}
</AnimatePresence>
| モード | 説明 |
|---|---|
"sync" | すべてのアニメーションが同時に実行されます(デフォルト) |
"wait" | 終了アニメーションが完了してから開始アニメーションが始まります |
"popLayout" | 終了する要素がすぐにレイアウトから削除されます |
並べ替え可能なリスト
layout とドラッグを組み合わせると、並べ替え可能なリストを作成できます。
function ReorderableList({ items, setItems }) {
return (
<AnimatePresence>
{items.map(item => (
<motion.div
key={item.id}
layout
drag
dragConstraints={{ top: 0, bottom: 0 }}
onDragEnd={({ info, target }) => {
// Calculate new index and update
}}
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
/>
))}
</AnimatePresence>
);
}
グリッドでの共有レイアウト
function Grid({ items }) {
return (
<motion.div
layout
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))",
gap: 10
}}
>
{items.map(item => (
<motion.div
key={item.id}
layout
style={{
width: "100%",
aspectRatio: 1,
backgroundColor: item.color
}}
/>
))}
</motion.div>
);
}
終了アニメーション
AnimatePresence を使うには、要素に終了状態を定義する必要があります。
<motion.div
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0 }}
/>
layout を使った終了アニメーション
<motion.div
layout
exit={{ opacity: 0, x: -100, transition: { duration: 0.3 } }}
/>
layoutId を使ったクロスフェード
同じ階層の複数の要素で layoutId を共有する場合:
function Toggle() {
const [showA, setShowA] = useState(true);
return (
<AnimatePresence mode="popLayout">
{showA ? (
<motion.div
key="a"
layoutId="shape"
style={{ backgroundColor: "red" }}
/>
) : (
<motion.div
key="b"
layoutId="shape"
style={{ backgroundColor: "blue", borderRadius: "50%" }}
/>
)}
</AnimatePresence>
);
}
要素は、角の丸みを含む状態間で滑らかに変形します。
ベストプラクティス
- ✅ ルート/ページ間の共有要素のトランジションには layoutId を使います。
- ✅ 終了アニメーションを適用するには、アニメーション化するリストを AnimatePresence でラップします。
- ✅ 位置のアニメーションを自動化するには layout プロパティを使います。
- ✅ 並べ替えを滑らかにするには AnimatePresence mode="popLayout" を使います。
- ✅ AnimatePresence を使うコンポーネントには exit 状態を定義します。
- ✅ 子要素の位置をアニメーション化する必要がある場合は、親コンテナに layout を指定します。
避けるべきこと
- ❌ 条件付きでレンダリングされるアニメーションコンポーネントを AnimatePresence でラップし忘れること。
- ❌ 同じ階層の複数の要素に同じ layoutId を使うこと。
- ❌ アンマウントするコンポーネントの終了状態を定義し忘れること。
- ❌ 子要素に適切なキーを設定せずにレイアウトアニメーションを使うこと。
- ❌ 同時に多くのレイアウト要素をアニメーション化すること。グループ化するか、順番にずらして実行します。
詳細を見る
https://www.framer.com/motion/layout-animations/ https://www.framer.com/motion/animate-presence/