framer-motion-layout
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFramer 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 prop enables automatic position animations when layout changes:
layoutjsx
<motion.div layout>
{items.map(item => (
<motion.div key={item.id} layout />
))}
</motion.div>layoutjsx
<motion.div layout>
{items.map(item => (
<motion.div key={item.id} layout />
))}
</motion.div>Layout Modes
布局模式
| Mode | Behavior |
|---|---|
| Animate position and size |
| Animate only position |
| Animate only size |
jsx
<motion.div layout="position" />
<motion.div layout="size" />| 模式 | 行为 |
|---|---|
| 动画位置和尺寸 |
| 仅动画位置 |
| 仅动画尺寸 |
jsx
<motion.div layout="position" />
<motion.div layout="size" />layoutId for Shared Element Transitions
layoutId 用于共享元素过渡
layoutIdlayoutIdPage 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 , Framer Motion animates the element smoothly between positions.
layoutIdjsx
// 页面A
function CardA() {
return <motion.div layoutId="card" />;
}
// 页面B
function CardB() {
return <motion.div layoutId="card" />;
}当CardA卸载且CardB以相同的挂载时,Framer Motion会将元素在两个位置间平滑动画过渡。
layoutIdModal 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>| Mode | Description |
|---|---|
| All animations run simultaneously (default) |
| Exit completes before enter starts |
| Exiting element removed from layout immediately |
jsx
<AnimatePresence mode="wait">
{isOpen && <Modal key="modal" />}
</AnimatePresence>| 模式 | 描述 |
|---|---|
| 所有动画同时运行(默认) |
| 退出动画完成后再开始进入动画 |
| 退出元素立即从布局中移除 |
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的情况下使用布局动画。
- ❌ 同时为过多布局元素设置动画——应分组或错开动画时间。