framer-motion-layout

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Framer Motion Layout Animations

Framer Motion 布局动画

When to Use This Skill

何时使用此技能

Apply when implementing shared element transitions, layout animations for reordering, or coordinated mount/unmount animations. When the user asks about Framer Motion layout animations, layoutId, or AnimatePresence.
Related skills: For core animation use framer-motion-core; for variants use framer-motion-variants; for React integration use framer-motion-react.
适用于实现共享元素过渡、用于重新排序的布局动画,或协调的挂载/卸载动画。当用户询问Framer Motion布局动画、layoutId或AnimatePresence相关问题时也可使用。
相关技能: 核心动画使用 framer-motion-core;变体使用 framer-motion-variants;React集成使用 framer-motion-react

Layout Prop

Layout 属性

The
layout
prop enables automatic position animations when layout changes:
jsx
<motion.div layout>
  {items.map(item => (
    <motion.div key={item.id} layout />
  ))}
</motion.div>
layout
属性可在布局变化时启用自动位置动画:
jsx
<motion.div layout>
  {items.map(item => (
    <motion.div key={item.id} layout />
  ))}
</motion.div>

Layout Modes

布局模式

ModeBehavior
true
Animate position and size
"position"
Animate only position
"size"
Animate only size
jsx
<motion.div layout="position" />
<motion.div layout="size" />
模式行为
true
动画位置和尺寸
"position"
仅动画位置
"size"
仅动画尺寸
jsx
<motion.div layout="position" />
<motion.div layout="size" />

layoutId for Shared Element Transitions

layoutId 用于共享元素过渡

layoutId
enables smooth transitions between elements in different components:
layoutId
可实现不同组件间元素的平滑过渡:

Page Transitions

页面过渡

jsx
// Page A
function CardA() {
  return <motion.div layoutId="card" />;
}

// Page B
function CardB() {
  return <motion.div layoutId="card" />;
}
When CardA unmounts and CardB mounts with the same
layoutId
, Framer Motion animates the element smoothly between positions.
jsx
// 页面A
function CardA() {
  return <motion.div layoutId="card" />;
}

// 页面B
function CardB() {
  return <motion.div layoutId="card" />;
}
当CardA卸载且CardB以相同的
layoutId
挂载时,Framer Motion会将元素在两个位置间平滑动画过渡。

Modal Overlays

模态弹窗

jsx
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>
  );
}
jsx
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 for Layout

AnimatePresence 用于布局

AnimatePresence enables exit animations:
jsx
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可启用退出动画:
jsx
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 Modes

AnimatePresence 模式

jsx
<AnimatePresence mode="wait">
  {isOpen && <Modal key="modal" />}
</AnimatePresence>
ModeDescription
"sync"
All animations run simultaneously (default)
"wait"
Exit completes before enter starts
"popLayout"
Exiting element removed from layout immediately
jsx
<AnimatePresence mode="wait">
  {isOpen && <Modal key="modal" />}
</AnimatePresence>
模式描述
"sync"
所有动画同时运行(默认)
"wait"
退出动画完成后再开始进入动画
"popLayout"
退出元素立即从布局中移除

Reorderable Lists

可重新排序列表

Combine layout with drag for reorderable lists:
jsx
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>
  );
}
结合layout与拖拽功能实现可重新排序列表:
jsx
function ReorderableList({ items, setItems }) {
  return (
    <AnimatePresence>
      {items.map(item => (
        <motion.div
          key={item.id}
          layout
          drag
          dragConstraints={{ top: 0, bottom: 0 }}
          onDragEnd={({ info, target }) => {
            // 计算新索引并更新
          }}
          initial={{ opacity: 0, scale: 0.8 }}
          animate={{ opacity: 1, scale: 1 }}
          exit={{ opacity: 0, scale: 0.8 }}
        />
      ))}
    </AnimatePresence>
  );
}

Shared Layout with Grid

网格布局中的共享布局

jsx
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>
  );
}
jsx
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>
  );
}

Exit Animations

退出动画

Elements must have exit states for AnimatePresence:
jsx
<motion.div
  initial={{ opacity: 0, scale: 0 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0, scale: 0 }}
/>
使用AnimatePresence的元素必须设置退出状态:
jsx
<motion.div
  initial={{ opacity: 0, scale: 0 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0, scale: 0 }}
/>

Exit with layout

结合layout的退出动画

jsx
<motion.div
  layout
  exit={{ opacity: 0, x: -100, transition: { duration: 0.3 } }}
/>
jsx
<motion.div
  layout
  exit={{ opacity: 0, x: -100, transition: { duration: 0.3 } }}
/>

Crossfade with layoutId

使用layoutId实现淡入淡出过渡

When multiple elements share a layoutId at the same level:
jsx
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>
  );
}
The element smoothly morphs between states including border radius.
当同一层级有多个元素共享一个layoutId时:
jsx
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>
  );
}
元素会在包括边框半径在内的状态间平滑变形。

Best practices

最佳实践

  • ✅ Use layoutId for shared element transitions between routes/pages.
  • ✅ Wrap animated lists with AnimatePresence for exit animations.
  • ✅ Use layout prop for automatic position animations.
  • ✅ Use AnimatePresence mode="popLayout" for smooth reordering.
  • ✅ Define exit states for components using AnimatePresence.
  • ✅ Use layout on parent containers when children need to animate position.
  • ✅ 使用 layoutId 实现路由/页面间的共享元素过渡。
  • ✅ 用 AnimatePresence 包裹动画列表以实现退出动画。
  • ✅ 使用 layout 属性实现自动位置动画。
  • ✅ 使用 AnimatePresence mode="popLayout" 实现流畅的重新排序。
  • ✅ 为使用AnimatePresence的组件定义 exit 状态。
  • ✅ 当子元素需要位置动画时,在父容器上使用 layout

Do Not

请勿操作

  • ❌ Forget to wrap conditionally rendered animated components with AnimatePresence.
  • ❌ Use the same layoutId on multiple elements at the same level.
  • ❌ Forget to define exit states for components that unmount.
  • ❌ Use layout animations without proper keys on children.
  • ❌ Animate too many layout elements simultaneously — group or stagger.
  • ❌ 忘记用AnimatePresence包裹条件渲染的动画组件。
  • ❌ 在同一层级的多个元素上使用相同的layoutId。
  • ❌ 不为卸载的组件定义退出状态。
  • ❌ 在没有为子元素设置正确key的情况下使用布局动画。
  • ❌ 同时为过多布局元素设置动画——应分组或错开动画时间。

Learn More

了解更多