首页 > 编程笔记 > JavaScript笔记
阅读:2
Vue动画效果的多种实现方式(非常详细)
动画的类型主要划分为 CSS 样式动画和 JavaScript 脚本动画,其中,CSS 样式动画可以划分为 transition 过渡动画和 animation 逐帧动画。
在 Vue 中,只能使用内置组件 transition 实现单一元素的动画,对于多元素分组动画,则可以使用 transition-group 组件处理。
我们将动画划分为两类,分别是开始动画与结束动画:
下面通过图 1 演示这 6 个类名发生的时刻。值得一提的是,动画类名中的 v 指代的是用户自定义的动画样式类名,而不是字面意义上的 v 字母。

图 1 过渡动画效果中类名发生的时刻
除了上面这种形式,我们还可以为过渡效果命名。比如 fade-enter-active、fade-leave-active 等自定义动画样式类名,就是利用 fade 单词替换了动画类名中的 v 字母。这就是自定义动画样式的类名规则,即只能使用自定义动画样式名称替换 v 字母,后面的命名规则不可改变。
现在只需要利用内置组件 transition 包裹动画元素,并给 transition 组件设置属性 name,name 的值对应自定义动画样式名称 fade。需要注意的是,transition 组件的操控范围只限一个子元素,无法对多个包裹的子元素进行动画样式处理。
请思考下面代码的运行效果:
比如定义一个 slide 滑动逐帧动画效果,只需要在 slide-enter-active 和 slide-leave-active 中,指定 slide-in 与 slide-out 的逐帧动画名称,并在逐帧动画中利用百分比或者 from、to 来实现 X 轴位移操作。
在自定义动画样式后,只需将 Vue 的 transition 动画组件的 name 属性值修改成 slide,即可实现指定元素滑入位移和滑出位移的动画效果。
下方代码就是在前面代码的基础上修改的,读者可自行对比体会,这里不再赘述:
animate.css 是一个拥有众多绚丽动画方式的 CSS 动画类库(这里只做简单演示,相关内容读者可自行查看bootstrap官方网站)。Vue 为了更好地与第三方动画类库结合,为 transition 组件设置了 enter-from-class、enter-active-class、enter-to-class、leave-from-class、leave-active-class、leave-to-class 这 6 个样式绑定的属性,这 6 个属性的名称与之前的 CSS 动画类名大致相同,意义也相差无几,这里不多做讲解。
我们可以通过 CDN 的方式引入 animate.css CSS 动画类库,在遵循其标准的前提下,直接利用 transition 组件提供的 6 个属性设置动画效果。
任何动画样式都需要先设置 animate__animated 样式,以此表明它是 animate.css 提供的动画样式,然后设置特定的动画效果样式名称,比如在 enter-active-class 中设置 animate__bounceInRight,在 leave-active-class 中设置 animate__bounceOutRight 后,即可在不写任何自定义动画样式的情况下,实现绚丽的动画效果。
将上面描述的过程通过代码实现,具体如下:
transition 组件提供了 8 个有关 JavaScript 动画的钩子函数,主要包括 before-enter(动画进入前)、enter(动画进入)、after-enter(动画进入后)、enter-cancelled(进入取消)、before-leave(动画离开前)、leave(动画离开)、after-leave(动画离开后)、leave-cancelled(离开取消)。
从名称上来看,这 8 个 transition 组件提供的钩子函数与 Vue 生命周期钩子函数相似,但读者要知道它们是完全不同的。这 8 个 transition 组件的钩子函数的位置是在 method 方法当中,而不是与 method 方法并列。
Vue 动画的钩子函数主要包括两个参数,即 el 和 done,其中,el 是要操作的元素目标,done 是一个函数,代表过渡是否结束。
值得一提的是,当我们想要在 @enter 和 @leave 中确认动画是否结束时,可以通过调用 done 函数来实现,否则钩子函数将被同步调用,过渡将会立即完成,这就会产生与预期动画不一致的结果。
下面利用 before-enter、enter、before-leave、leave 这 4 个钩子函数实现一个进度条效果,请读者思考下面的代码:
运行代码后,可以发现页面上出现了一个按钮,如下图所示:

图 2 初始效果
首次点击按钮后会发现,页面上出现了一个类似进度条的动画,逐渐增加直至 300px,如下图所示,再次点击按钮,进度条逐渐减少直至消失:

图 3 首次点击按钮后出现进度条动画(截图效果)
transition 组件与 transition-group 组件的属性基本一致,唯一不同的是,transition-group 组件的 tag 标签属性指定一个元素作为容器元素来渲染,由于 transition-group 组件控制的是多元素的分组动画,因此必须给它包裹的每个子元素设置唯一的 key 属性来进行区分,否则无法实现动画效果。
请思考下面代码的运行效果:
此外,页面上还出现了一个按钮,当点击该按钮时,会在页面上的随机位置添加一个元素,当点击某个元素时,该元素会被移除。初始页面效果如下图所示:

图 4 初始页面效果
点击按钮后的页面效果如下图所示:

图 5 点击按钮后的页面效果
点击元素后的页面效果如下图所示:

图 6 点击元素后的页面效果
在 Vue 中,只能使用内置组件 transition 实现单一元素的动画,对于多元素分组动画,则可以使用 transition-group 组件处理。
基于CSS的过渡动画效果
基于 CSS 的样式实现动画效果,开发人员需要编写自定义动画样式。样式的名称不能随意命名,需要遵循一定的规则标准。我们将动画划分为两类,分别是开始动画与结束动画:
- 开始动画的类名主要有 3 个,包括 v-enter-from(进入动画起始)、v-enter-active(进入动画生效)、v-enter-to(进入动画结束);
- 结束动画的类名与开始动画的类名是对应的,主要有 3 个,包括 v-leave-from(离开动画起始)、v-leave-active(离开动画生效),v-leave-to(离开动画结束)。
下面通过图 1 演示这 6 个类名发生的时刻。值得一提的是,动画类名中的 v 指代的是用户自定义的动画样式类名,而不是字面意义上的 v 字母。

图 1 过渡动画效果中类名发生的时刻
除了上面这种形式,我们还可以为过渡效果命名。比如 fade-enter-active、fade-leave-active 等自定义动画样式类名,就是利用 fade 单词替换了动画类名中的 v 字母。这就是自定义动画样式的类名规则,即只能使用自定义动画样式名称替换 v 字母,后面的命名规则不可改变。
现在只需要利用内置组件 transition 包裹动画元素,并给 transition 组件设置属性 name,name 的值对应自定义动画样式名称 fade。需要注意的是,transition 组件的操控范围只限一个子元素,无法对多个包裹的子元素进行动画样式处理。
请思考下面代码的运行效果:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于CSS的过渡动画效果</title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.40/vue.global.js"></script> <style> /* 开始动画与结束动画都是透明度渐变动画 */ .fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; } /* 开始动画的初始透明度为 0,结束动画的最终透明度也为 0 */ .fade-enter-from, .fade-leave-to { opacity: 0; } </style> </head> <body> <div id="app"> <button @click="show = !show">切换动画</button> <transition name="fade"> <div v-if="show">Hello Vue3!</div> </transition> </div> <script> const { createApp } = Vue createApp({ data () { return { show: false, } }, }).mount('#app') </script> </body> </html>点击“切换动画”按钮后可以看到动画元素的显现是一个渐显的过程,而动画元素的隐藏则是一个渐隐的过程,完全实现了基于 CSS 的过渡动画效果。
基于CSS的逐帧动画效果
CSS 的过渡动画只能实现开始与结束的动画效果,而 animation 逐帧动画却可以实现更丰富、更细腻的动画效果。不过对于 Vue 来说,不管是过渡动画还是逐帧动画,用户都是利用 6 个样式类来自定义样式类名,从而实现动画效果的。比如定义一个 slide 滑动逐帧动画效果,只需要在 slide-enter-active 和 slide-leave-active 中,指定 slide-in 与 slide-out 的逐帧动画名称,并在逐帧动画中利用百分比或者 from、to 来实现 X 轴位移操作。
在自定义动画样式后,只需将 Vue 的 transition 动画组件的 name 属性值修改成 slide,即可实现指定元素滑入位移和滑出位移的动画效果。
下方代码就是在前面代码的基础上修改的,读者可自行对比体会,这里不再赘述:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于CSS的逐帧动画效果</title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.40/vue.global.js"></script> <style> /* 开始动画与结束动画都是指定逐帧动画名称 */ .slide-enter-active { animation: slide-in 0.5s ease; } .slide-leave-active { animation: slide-out 0.5s ease; } /* 逐帧动画可以使用百分比,如果只设置开始动画与结束动画,则可以使用 from, to */ @keyframes slide-in { 0% { transform: translateX(100px); } 100% { transform: translateX(0); } } @keyframes slide-out { from { transform: translateX(0); } to { transform: translateX(100px); } } </style> </head> <body> <div id="app"> <button @click="show = !show">切换动画</button> <transition name="slide"> <div v-if="show">Hello Vue3!</div> </transition> </div> <script> const { createApp } = Vue createApp({ data () { return { show: false, } }, }).mount('#app') </script> </body> </html>
基于第三方动画类库的CSS动画效果
对于 Vue 的 CSS 动画,是不是只有自定义动画样式的处理方案呢?其实 Vue 的 CSS 动画还可以和第三方动画类库完美结合,从而可以更快、更便捷地实现炫酷的 CSS 动画效果。animate.css 是一个拥有众多绚丽动画方式的 CSS 动画类库(这里只做简单演示,相关内容读者可自行查看bootstrap官方网站)。Vue 为了更好地与第三方动画类库结合,为 transition 组件设置了 enter-from-class、enter-active-class、enter-to-class、leave-from-class、leave-active-class、leave-to-class 这 6 个样式绑定的属性,这 6 个属性的名称与之前的 CSS 动画类名大致相同,意义也相差无几,这里不多做讲解。
我们可以通过 CDN 的方式引入 animate.css CSS 动画类库,在遵循其标准的前提下,直接利用 transition 组件提供的 6 个属性设置动画效果。
任何动画样式都需要先设置 animate__animated 样式,以此表明它是 animate.css 提供的动画样式,然后设置特定的动画效果样式名称,比如在 enter-active-class 中设置 animate__bounceInRight,在 leave-active-class 中设置 animate__bounceOutRight 后,即可在不写任何自定义动画样式的情况下,实现绚丽的动画效果。
将上面描述的过程通过代码实现,具体如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于第三方动画类库的CSS动画效果</title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.40/vue.global.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet" /> </head> <body> <div id="app"> <button @click="show = !show">切换动画</button> <transition enter-active-class="animate__animated animate__bounceInRight" leave-active-class="animate__animated animate__bounceOutRight" > <div v-if="show">Hello Vue3!</div> </transition> </div> <script> const { createApp } = Vue createApp({ data () { return { show: false, } }, }).mount('#app') </script> </body> </html>
基于 JavaScript 的动画效果
在 Vue 中,除了利用 CSS 实现动画效果,还支持 JavaScript 的操作实现。transition 组件提供了 8 个有关 JavaScript 动画的钩子函数,主要包括 before-enter(动画进入前)、enter(动画进入)、after-enter(动画进入后)、enter-cancelled(进入取消)、before-leave(动画离开前)、leave(动画离开)、after-leave(动画离开后)、leave-cancelled(离开取消)。
从名称上来看,这 8 个 transition 组件提供的钩子函数与 Vue 生命周期钩子函数相似,但读者要知道它们是完全不同的。这 8 个 transition 组件的钩子函数的位置是在 method 方法当中,而不是与 method 方法并列。
Vue 动画的钩子函数主要包括两个参数,即 el 和 done,其中,el 是要操作的元素目标,done 是一个函数,代表过渡是否结束。
值得一提的是,当我们想要在 @enter 和 @leave 中确认动画是否结束时,可以通过调用 done 函数来实现,否则钩子函数将被同步调用,过渡将会立即完成,这就会产生与预期动画不一致的结果。
下面利用 before-enter、enter、before-leave、leave 这 4 个钩子函数实现一个进度条效果,请读者思考下面的代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>基于JavaScript的动画效果</title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.40/vue.global.js"></script> </head> <body> <div id="app"> <button @click="show = !show">切换动画</button> <transition @before-enter="beforeEnter" @enter="enter" @before-leave="beforeLeave" @leave="leave" > <div style="width: 100px; height: 100px; background-color: green" v-if="show"></div> </transition> </div> <script> const { createApp } = Vue createApp({ data () { return { show: false, elementWidth: 100, } }, methods: { beforeEnter (el) { // el是动画元素目标 this.elementWidth = 100 // 初始元素宽度 this.style.width = this.elementWidth + 'px' }, enter: function (el, done) { // 通过定时器,每隔10毫秒,将元素宽度增加10px let count = 1 const interval = setInterval(() => { el.style.width = this.elementWidth + count * 10 + 'px' count++ // 当达到指定条件时(宽度为300px),停止定时器,并终止动画 if (count > 20) { // 在原有100px的基础上,增加了200px(200px+100px) clearInterval(interval) done() // 动画完成 } }, 20) }, beforeLeave: function (el) { this.elementWidth = 300 el.style.width = this.elementWidth + 'px' }, leave: function (el, done) { // 通过定时器,每隔10毫秒,将元素宽度减少10px let round = 1 const interval = setInterval(() => { el.style.width = this.elementWidth - round * 10 + 'px' round++ // 当达到指定条件时,停止定时器,并终止动画 if (round > 20) { clearInterval(interval) done() // 动画完成 } }, 20) }, }, }).mount('#app') </script> </body> </html>在上面的代码中:
- 初始进度条 div 不显示,当点击“切换动画”按钮后,在动画进入前(beforeEnter方法中),为要实现的进度条设置了初始宽度;
- 在动画进入时(enter 方法中),通过定时器实现了进度条逐渐增加的效果,并在实现效果后终止动画;
- 在动画离开前(beforeLeave方法中),再次设置进度条初始宽度;
- 在动画离开后(leave方法中),做的事情与动画进入时类似,即通过定时器实现进度条逐渐减少的效果,并在实现效果后终止动画。
运行代码后,可以发现页面上出现了一个按钮,如下图所示:

图 2 初始效果
首次点击按钮后会发现,页面上出现了一个类似进度条的动画,逐渐增加直至 300px,如下图所示,再次点击按钮,进度条逐渐减少直至消失:

图 3 首次点击按钮后出现进度条动画(截图效果)
多元素分组动画效果
transition 动画组件只能对单一元素进行动画控制,如果想实现列表等多元素的分组动画操作,则需要利用 transition-group 组件实现。transition 组件与 transition-group 组件的属性基本一致,唯一不同的是,transition-group 组件的 tag 标签属性指定一个元素作为容器元素来渲染,由于 transition-group 组件控制的是多元素的分组动画,因此必须给它包裹的每个子元素设置唯一的 key 属性来进行区分,否则无法实现动画效果。
请思考下面代码的运行效果:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>多元素分组动画效果</title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.40/vue.global.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet" /> </head> <body> <div id="app"> <button class="btn btn-primary" @click="addItem">Add Item</button> <!-- 指定ul作为容器元素来渲染 --> <transition-group tag="ul" enter-active-class="animate__animated animate__bounceInRight" leave-active-class="animate__animated animate__bounceOutRight" > <!-- 每个子元素都必须有唯一的key属性 --> <li v-for="(number, index) in numbers" @click="removeItem(index)" :key="number"> {{number}} </li> </transition-group> </div> <script> const { createApp } = Vue createApp({ data () { return { numbers: [1, 2, 3, 4, 5], } }, methods: { addItem () { // 在列表随机位置添加一个元素 const pos = Math.floor(Math.random() * this.numbers.length) this.numbers.splice(pos, 0, this.numbers.length + 1) }, removeItem (index) { // 移除指定位置的元素 this.numbers.splice(index, 1) }, }, }).mount('#app') </script> </body> </html>上面的代码利用 transition-group 组件实现了列表的分组动画操作,在 transition-group 组件上通过 tag 标签属性指定 ul 作为容器元素来渲染,并通过列表的形式展示数组。
此外,页面上还出现了一个按钮,当点击该按钮时,会在页面上的随机位置添加一个元素,当点击某个元素时,该元素会被移除。初始页面效果如下图所示:

图 4 初始页面效果
点击按钮后的页面效果如下图所示:

图 5 点击按钮后的页面效果
点击元素后的页面效果如下图所示:

图 6 点击元素后的页面效果