mcpField
F
framer-motion-layout

framer-motion-layout

github.com/c-jerilデザイン・UI10788 回インストール30 週間前に更新
リポジトリ

framer-motion-layout について

framer-motion-layoutは、Framer Motionでレイアウトや共有要素のアニメーションを実装するスキルです。自動レイアウト変更、layoutIdによる遷移、AnimatePresenceの退出、並べ替え可能なリストを扱います。

主な機能

  1. 01位置とサイズの自動アニメーション
  2. 02layoutIdによる共有要素の遷移
  3. 03AnimatePresenceの退出アニメーション
  4. 04並べ替え可能なリストのパターン
  5. 05マウントとアンマウントの連携アニメーション

活用シーン

  1. 01リストの順序変更に合わせたアニメーション
  2. 02ページ間でのカード遷移
  3. 03元の項目からモーダルへの遷移
  4. 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/

コメント