前言
大家好,我是 Motion Vue
的作者。作为 framer-motion 的忠实粉丝,我一直非常欣赏它优雅的 API 设计和极致的动画体验。本文想和大家简单分享下 Motion
的 layout animation
基础用法和实战技巧,希望能给你带来启发,也欢迎大家关注 Motion Vue
在 Vue
生态的实现与探索。
布局动画基础
什么是布局动画?
在 Framer Motion 中,只需给 motion
组件加上 layout
属性,就能让它在不同布局之间平滑过渡,这就是布局动画。
布局(layout)指什么?
- 位置相关:如 CSS 的
flex
、position
、grid
- 尺寸相关:如
width
、height
- 列表中元素的整体位置(比如排序/重排)
普通的 initial
和 animate
不能实现布局动画,必须用 layout
。
示例:
vue
<template>
<motion.div layout :style="{ justifySelf: position }" />
</template>

可以看到我们每次进行布局变化,带有layout
属性会让组件从上一个布局平滑过渡到新布局,否则会瞬移。
布局动画让 UI 交互更自然,尤其适合列表增删、排序等场景,配合 AnimatePresence
效果更佳。
自定义布局动画过度效果
你可以通过以下方式自定义布局动画过度效果
vue
<motion.div
layout
:transition="{
layout: {
duration: 1.5,
},
}"
/>
修复布局动画borderRadius的失真问题
有时布局动画会导致如 borderRadius
属性在过渡时出现失真。解决方法:
- 这些属性用内联 style 明确指定
vue
<motion.div
layout
:style="{ borderRadius: expanded ? '20px' : '150px' }"
/>

注意:用 CSS 变量设置
borderRadius
无法避免失真,必须用具体值。
layout 属性的更多用法
layout
不只是布尔值,还可以:
layout="position"
:只平滑过渡位置,尺寸变化瞬间完成layout="size"
:只平滑过渡尺寸,位置变化瞬间完成 适合需要分别控制位置/尺寸动画的场景。

布局动画元素被拉伸
或压扁
怎么解决
有时,由于布局动画的作用,调整大小的组件的内容可能会被
压扁
或拉伸
。如果你在制作布局动画时发现这种情况,只需将布局属性设置为 position,就有可能解决这个问题。
下面是这种情况的一个例子:
- 删除水平列表中的项目将影响每个组件的大小。默认情况下,您会注意到当物品被删除时,组件会稍微挤压。
- 将内容放在
motion
组件下,并将layout
属性设置为position

共享布局动画
不同于前面介绍的布局动画(基于同一组件的布局样式变化进行过渡转换),共享布局动画是在具有相同layoutId
属性的不同组件进行动画。我们来看下下面这个例子:
vue
<template>
<div class="list">
<div
v-for="item in items"
:key="item"
class="item"
@click="selected = item"
tabindex="0"
>
<div>Item {{ item }}</div>
<Arrow
v-if="item === selected"
layoutId="arrow"
:style="{
height: '24px',
color: '#5686F5',
transform: 'rotate(-90deg)'
}"
/>
</div>
</div>
</template>
// Arrow组件
<template>
<Motion
layoutId="arrow"
:style="{ height: '24px', color: '#5686F5', transform: 'rotate(-90deg)' }"
/>
</template>
当两个组件共享相同的 layoutId
时, Motion
会自动在它们之间创建过渡动画。在这个例子中:
- 每个列表项都有一个可选的箭头指示器
- 箭头组件使用
layoutId="arrow"
来标识 - 当用户点击不同的列表项时,箭头会平滑地从一个位置移动到另一个位置

总结
以上就是关于motion
布局动画的一些基础使用。
对布局动画感兴趣的同学可以查看以下文档。
或者加入Motion Vue
的discord频道,可以问我关于Motion Vue
的使用问题。或者提提建议,我需要听到更多来自Vue
社区的反馈。
相关文档