(22)Vue 中的动画特效——① Vue 中的 CSS 动画原理 | Vue 基础理论实操
本文版权归 “公众号 | 前端一万小时” 所有,欢迎转载!
转载请注明出处,未经同意,不可修改文章内容。
本系列文章已在“公众号 | 前端一万小时”更新完毕,有需要的小伙伴可按需前往查看。
“前端一万小时”两大明星专栏——“ 从零基础到轻松就业 ”、“ 前端面试刷题 ”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 “按钮”控制页面展示与否
在讲解动画原理之前,我们先来实现一个非常简单的功能。通过点击“切换”按钮来控制页面内容展示与否。
代码及功能效果如下:
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>前端一万小时-Vue 中 CSS 动画原理</title>
<script src="./vue.js"></script>
</head>
<div id="root">
<div v-if="show">Hello,前端一万小时</div> <!-- 1️⃣在组件中创建一个 div 标签,
内容是“Hello,前端一万小时”;
2️⃣内容的显示与否,通过 v-if 的 show 来决定;
<button @click="handleClick">切换</button> <!-- 4️⃣增加一个 button “切换”按钮,
并绑定 click 事件 handleClick; -->
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
show: true // 3️⃣在 data 中定义 show,默认值为 true;
methods: {
handleClick: function() { // 5️⃣在 methods 中写 handleClick 的逻辑。
this.show = !this.show
</script>
</body>
</html>

2 页面效果渐隐渐现(动画)的效果
现在有一个需求:让
Hello,前端一万小时
在显示/隐藏时,有渐隐渐现的动画效果。
如果希望
Hello,前端一万小时
有动画效果的话,那么在它的标签外层,要用一个
<transition>
标签把它包裹起来:
<div id="root">
<transition name="fade"> <!-- 给 transition 起名叫 fade(或者其他任意名字都可以)。 -->
<div v-if="show">Hello,前端一万小时</div>
</transition>
<button @click="handleClick">切换</button>
</div>
当写完这些代码后,再到页面上刷新查看:

发现动画效果并没有出现!这是什么原因呢?其实我们的代码并没有写完。
2.1 过渡动画的原理
在写下一部分代码之前,我们先把 transition 对应的过渡动画的原理搞清楚。
当一个元素被 transition 包裹了之后,Vue 会自动分析元素的 CSS 样式,然后构建一个动画的流程。
上图的黑线条及线条上的三个圆就是动画的流程:
-
在动画即将被执行的这个瞬间(动画即将开始时),Vue 会给被 transition 包裹的 div 上增加两个 class 名,分别是 fade-enter 和 fade-enter-active。
-
当动画第一帧执行结束后(动画过程中),Vue 分析了 transition 标签、知道它是一个动画效果后,在动画运行到第二帧时会把 fade-enter 的 class 去除,同时再增加一个 fade-enter-to 的 class 名。
-
接着动画执行到结束时,Vue 会把 fade-enter-active 和 fade-enter-to 这两个 class 移除。
我们继续看下文的代码实例。
2.2 显示的动画
当一个 div 元素被
<transition>
包裹起来时,Vue 会自动构建一个动画流程。
我们给在动画即将开始时添加的 fade-enter 和 fade-enter-active 两个 class 写一些样式:
<head>
<meta charset="UTF-8">
<title>前端一万小时-Vue 中 CSS 动画原理</title>
<script src="./vue.js"></script>
<style>
.fade-enter {
opacity: 0;
} /* 在第一帧的时候 .fade-enter 的样式会被增加进来。 */
.fade-enter-active {
transition: opacity 3s;
</style>
</head>
保存后在浏览器中打开点击“切换”,隐藏了
Hello,前端一万小时
后,再
显示
的时候,有一个明显的过渡效果:

❓为什么这样写就有动画效果呢?
如上图所示,
.fade-enter-active
在动画即将开始时就已经存在了,而直到动画结束时才被移除。而我们在这个 class 里写的内容就是对
opacity
进行一个 transition 的监控。如果监控到
opacity
发生变化,就会让元素的不透明度在 3 秒之间,慢慢的渐变到
opacity
对应的值。
接下来继续看 fade-enter:
fade-enter 在第一帧的时候存在,但当动画运行到第二帧的时候它就被移除了。我们在
.fade-enter
里面写了
opacity: 0;
。当点击按钮时,页面即将被显示(动画运行到第一帧),fade-enter 和 fade-enter-active 一起存在。这时,这个 div 标签是处于隐藏状态。
当动画运行到第二帧,Vue 自动移除了 fade-enter,然后 div 元素上的
opacity
就自动恢复到了原始的值 1。恰好 fade-enter-active 监听到
opacity
发生了变化,那么它会让这个变化在 3 秒钟内完成。这样就使得我们慢慢显示的动画效果得以实现!
❓为什么 class 名都以 fade 开头?
答:因为我们给 transition 起的名字叫“fade”,所以 class 名都是用 fade 开头。如果不给 transition 起名,那么 Vue 默认的前缀是
v
,即
v-enter
/
v-enter-active
等。
所以代码这样写也是可以的:
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>前端一万小时-Vue 中 CSS 动画原理</title>
<script src="./vue.js"></script>
<style> /* ❗️fade 替换成了 v。 */
.v-enter {
opacity: 0;
.v-enter-active {
transition: opacity 3s;
</style>
</head>
<div id="root">
<transition>
<div v-if="show">Hello,前端一万小时</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
show: true
methods: {
handleClick: function() {
this.show = !this.show
</script>
</body>
</html>
2.3 隐藏的动画
当一个 div 元素被包裹在 transition 标签之内时,这个 div 元素从“显示”状态变为“隐藏”状态的动画效果流程如上图。
这时 Vue 会自动构建一个隐藏的动画流程:
- 隐藏的第一个瞬间时,Vue 会自动增加 fade-leave 和 fade-leave-active 的 class;
- 在第二帧时,Vue 会把 fade-leave 去除掉,增加一个 fade-leave-to 的 class;
- 最后一帧时,Vue 会把 fade-leave-active 和 fade-leave-to 这两个 class 都移除。
结合这些 Vue 会自动做的事,我们就可以编写出当元素隐藏状态的动画效果了:
<style>
.v-enter {
opacity: 0;
.v-enter-active {
transition: opacity 3s;
/* ❗️隐藏状态的动画 CSS 代码。 */
.v-leave-to {
opacity: 0;
.v-leave-active {
transition: opacity 3s;
</style>
保存后,刷新页面可以看到,隐藏/显示时都有动画效果:

❓为什么加了这段代码,就有隐藏状态的动画效果了?
答:先看 v-leave-active,也就是上图中的 fade-leave-active:从动画执行的第一个瞬间,到动画执行完毕,整个过程中 fade-leave-active 都存在。即在动画运行过程中,我们要求时刻监听 div 的 opacity 属性,一旦 opacity 属性发生变化,就让它在 3 秒慢慢的进行过渡。
再看 v-leave-to,即图中的 fade-leave-to:在离开动画执行的第一个瞬间,并没有执行 fade-leave-to 的一些操作,而是在第二个瞬间才把 fade-leave-to 加上。
❓在第一个瞬间,div 标签的 opacity 是多少?
答:当从显示状态变成隐藏状态时,第一个瞬间还是显示的,即 opacity 还是 1。
动画到第二帧时,把 fade-leave-to 加进来了,opacity 突然就变成 0 了。恰好我们一直让 fade-leave-acitve 监听着 div 的
opacity
变化,所以当
opacity
从 1 变为 0 时,这个变化就有 3 秒的过渡。同时 fade-leave-to 一直存在,直到动画结束后才会被取消掉。
通过这种在某一时刻,自动向 div 元素上增加一些 class 的底层原理,Vue 帮我们实现了 CSS 动画效果。因为这种动画是通过 CSS3 里的 transition 来实现的,transition 翻译成中文就是“过渡”,所以 Vue 也把这种动画效果叫做“过渡动画”效果。
❓div 的显示和隐藏是通过 v-if 来控制的,改成 v-show 可不可以?
答:可以。只要用 transition 标签包裹在 div 的外层,不管是用 v-if 还是 v-show 来控制显示或隐藏,都会带有动画效果。(除了 v-show 和 v-if 之外,甚至写动态组件也会自带过渡效果。动态组件的过渡,我们将在后面的内容讲到。)
我们把 v-if 改成 v-show,时间改为 1 秒,保存之后刷新网页查看。
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>前端一万小时-Vue 中 CSS 动画原理</title>
<script src="./vue.js"></script>
<style>
/* ❗️整理了 CSS 代码。 */
.v-enter,
.v-leave-to {
opacity: 0;
.v-enter-active,
.v-leave-active {
transition: opacity 1s;
} /* 2️⃣时间改为 1 秒。 */
</style>
</head>
<div id="root">
<transition>
<div v-show="show">Hello,前端一万小时</div> <!-- 1️⃣v-if 改成 v-show; -->
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
show: true
methods: {
handleClick: function() {
this.show = !this.show