Vue 中的过渡与动画
1. 动画实现的基础思路
在 Web 开发中,实现动画效果主要有两种技术途径:
- CSS 过渡 (Transition) 与动画 (Animation)
- 通过
transition和animation属性实现。 - 性能较好,由浏览器直接处理。
- 通过
- JavaScript 动画
- 通过
setTimeout/requestAnimationFrame等方式,用 JS 动态改变元素样式(如位置、尺寸)。 - 优点:控制更精细、灵活,可以实现暂停、倒放、处理复杂逻辑等。
- 缺点:可能比 CSS 动画消耗更多性能。
- 通过
2. 在 Vue 中应用原生动画方法
Vue 作为一个前端框架,本身不限制原生动画技术的使用。
2.1 CSS 方式:动态添加/移除类
最直接的方式是在合适的时机,通过 Vue 的数据绑定动态地为一个元素添加或移除一个带有 transition 或 animation 属性的 CSS 类。
<div :class="{ 'animate-class': shouldAnimate }"></div>/* CSS */
.animate-class {
transition: all 0.5s ease;
transform: translateX(100px);
}2.2 JS 方式:数据驱动与第三方库
通过 JS 控制一个数据,并将这个数据绑定到元素的 style 上。通过改变这个数据,驱动视图更新,从而形成动画。
核心思路:
- 在
<template>中,将元素的位置、尺寸等样式信息绑定到data中的数据。 - 在
<script>中,使用setTimeout、setInterval或requestAnimationFrame逐步改变这些数据。 - 数据一变,视图自动更新,形成动画。
- 在
可借助的第三方库:
Velocity.jsanime.js(非常流行)
这些库能帮助我们更好地控制数据在指定时间内的状态变化。我们只需要在库的回调中更新 Vue 的 data 即可。
3. 内置组件 <transition>
Vue 考虑到元素的出现和消失是最常见的动画场景,因此提供了一个内置组件 <transition> 来简化这类动画的开发。
3.1 作用与适用场景
- 作用: 自动监听其插槽内唯一根元素的出现和消失,并在合适的时机应用 CSS 过渡或动画效果。
- 适用场景: 主要用于单个元素的进入 (Enter) 和离开 (Leave) 动画。例如由
v-if或v-show控制的元素。 - 注意:
<transition>组件本身不渲染任何 DOM 元素,它只是一个包装器。
3.2 核心要点
- 唯一根元素: 插槽内必须有且仅有一个根元素。可以使用
v-if/v-else来切换两个元素,因为在同一时刻只有一个元素被渲染。 - 监控对象:
v-if条件渲染导致的 DOM 元素新增或移除。v-show条件渲染导致的元素display样式的显示或隐藏。
3.3 过渡流程与 CSS 类名
<transition> 组件在元素进入和离开的不同阶段,会自动添加和移除特定的 CSS 类名。我们可以通过定义这些类的样式来实现动画。
类名的默认前缀是 v-,如果给 <transition> 组件设置了 name 属性,例如 name="fade",则前缀会变为 fade-。
进入过渡 (Enter)
当一个元素被插入时,会依次发生以下事件:
v-enter: 进入过渡的起始状态。元素被插入后立即添加,在下一帧被移除。用于定义动画开始前的样式(如opacity: 0)。v-enter-active: 进入过渡的激活状态。在整个进入过程中生效,定义过渡或动画的持续时间、延迟和曲线(如transition: opacity 0.5s ease;)。v-enter-to: 进入过渡的结束状态。在v-enter被移除后(即下一帧)立即添加,在过渡/动画完成后移除。定义动画结束后的样式(如opacity: 1)。
流程图解:
元素插入 -> 添加
v-enter,v-enter-active-> [下一帧] -> 移除v-enter, 添加v-enter-to-> [过渡/动画结束] -> 移除v-enter-active,v-enter-to
离开过渡 (Leave)
当一个元素被移除时,会依次发生以下事件:
v-leave: 离开过渡的起始状态。元素被删除时立即添加,在下一帧被移除。v-leave-active: 离开过渡的激活状态。在整个离开过程中生效,定义过渡或动画。v-leave-to: 离开过渡的结束状态。在v-leave被移除后(即下一帧)立即添加,在过渡/动画完成后移除。用于定义元素离开后的最终状态(如opacity: 0)。
流程图解:
触发删除 -> 添加
v-leave,v-leave-active-> [下一帧] -> 移除v-leave, 添加v-leave-to-> [过渡/动画结束] -> 移除v-leave-active,v-leave-to-> 元素从 DOM 中删除
代码示例:淡入淡出
<button @click="show = !show">Toggle</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>/* 进入的起始状态 和 离开的结束状态 */
.fade-enter, .fade-leave-to {
opacity: 0;
}
/* 进入和离开的整个过程 */
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}3.4 自定义过渡类名
除了使用 name 属性外,还可以通过 props 直接指定类名,这在需要结合第三方 CSS 动画库(如 Animate.css)时非常有用。
<transition
enter-active-class="animated bounceInLeft"
leave-active-class="animated bounceOutRight">
<p v-if="show">hello</p>
</transition>3.5 初始渲染过渡 (appear)
默认情况下,<transition> 不会在初始渲染时播放动画。可以通过添加 appear 属性来启用。
<transition name="fade" appear>
<p>Hello</p>
</transition>4. 多元素及动态组件的过渡
4.1 使用 v-if / v-else
<transition> 可以包裹 v-if/v-else/v-else-if 结构,因为在任何时刻只有一个元素被渲染。
4.2 key 属性的重要性
当切换相同标签名的元素时(例如,从一个 <h1> 切换到另一个 <h1>),Vue 为了高效会就地复用这个元素,只更新其内容。这会导致过渡效果不触发。
为了让 Vue 将它们视为独立的元素并触发过渡,需要为它们添加唯一的 key 属性。
<transition name="fade" mode="out-in">
<h1 v-if="isTitle1" key="title1">Title 1</h1>
<h1 v-else key="title2">Title 2</h1>
</transition>4.3 过渡模式 (mode)
默认情况下,进入和离开的动画是同时发生的。mode 属性可以控制它们的执行顺序:
in-out: 新元素先过渡进入,完成后,旧元素再过渡离开。out-in: 旧元素先过渡离开,完成后,新元素再过渡进入。(更常用)
4.4 动态组件 <component> 的过渡
<transition> 也可以包裹动态组件,当 :is 绑定的组件切换时,会触发过渡。
<transition name="fade" mode="out-in">
<component :is="currentComponent"></component>
</transition>5. 列表过渡 <transition-group>
对于 v-for 渲染的列表,需要使用 <transition-group> 组件。
5.1 与 <transition> 的区别
- 渲染元素:
<transition-group>默认会渲染一个<span>元素作为包裹容器。可以通过tag属性指定渲染成其他元素,如tag="ul"。 - 内部元素: 插槽内可以是多个元素(通常是
v-for的结果)。 key是必须的: 列表中的每个元素都必须有唯一的key属性。
5.2 列表的进入与离开
列表项的进入和离开动画与 <transition> 的用法完全相同,使用相同的 CSS 过渡类。
5.3 列表的移动过渡 (v-move)
<transition-group> 最强大的功能是,当列表项的顺序发生改变时(如排序、删除某项),它会自动为正在移动的元素添加一个 v-move 类(如果设置了 name,则为 name-move)。
我们可以利用这个类来实现平滑的移动动画。
核心技巧:
v-move类通常只需要设置transition属性即可。Vue 会在背后通过 FLIP 技术自动处理元素的位置变换。
代码示例:平滑的列表增删和排序
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id" class="list-item">
{{ item.text }}
</li>
</transition-group>/* 进入和离开的动画 */
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
/* 移动时的动画 */
.list-move {
transition: transform 1s;
}
/* 解决移除元素时的塌陷问题 */
.list-leave-active {
position: absolute;
}特别注意: 当一个元素离开时,为了让其他元素能够平滑地移动到新位置,而不是瞬间“跳”过去,需要将离开的元素(即应用了
leave-active类的元素)设置为position: absolute,使其脱离文档流。
5.4 FLIP 动画思想简介
v-move 的平滑过渡效果背后是 FLIP (First, Last, Invert, Play) 技术的应用。
- First: 记录元素在变化前的位置。
- Last: 让元素立即变化到最终位置,并记录这个位置。
- Invert: 计算出前后位置的差值,通过
transform将元素瞬间移回(“反转”)到初始位置。 - Play: 移除
transform,并添加transition效果,让元素平滑地“播放”动画回到其最终位置。
由于第 2、3 步发生得极快,人眼观察不到,看到的只是第 4 步的平滑动画。Vue 帮我们处理了这一切。