本文主要归纳在 Vuejs 学习过程中对于 Vuejs 动画效果的各个相关要点。由于本人水平有限,如文中出现错误请多多包涵并指正,感谢。如果需要看更清晰的代码高亮,请跳转至我的个人站点的 深入理解 Vuejs 动画效果 查看本文。
Vue 中的 CSS 动画原理
将标签外部添加 transition
标签,将其包裹起来。他的原理图如下,即当一个元素被transition
包裹之后,Vue 会自动分析元素的 CSS 样式,然后构建一个动画流程。
下面示例图中的线和点,就可以称之为一个动画流程。Vue 会在动画即将执行的瞬间,往内部被包裹的的 div
上增添两个 class
名,分别是 fade-enter
和 fade-enter-active
。当动画第一帧执行结束之后;Vue 会在动画执行到第二帧的时候,把之前添加的 fade-enter
这个 class
去除,然后再增加一个 fade-enter-to
的 class
名;接着动画继续执行,执行到结束的瞬间,Vue 会把之前添加的 fade-enter-active
和 fade-enter-to
两个 class
都去除掉。
![](https://upload-images.jianshu.io/upload_images/12904618-4b1e833be65a1c58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/623/format/webp)
当动画从显示状态变为隐藏状态时,原理图如下,流程跟上相似:
![](https://upload-images.jianshu.io/upload_images/12904618-e2cdda280062eebb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/715/format/webp)
Vue 过渡动画案例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue中的css动画原理</title>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<style>
.fade-enter {
opacity: 0;
}
.fade-enter-active {
transition: opacity 2s;
}
.fade-leave-to {
opacity: 0;
}
.fade-leave-active {
transition: opacity 2s;
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<div v-if="show">
hello world
</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
}
}
})
</script>
</body>
</html>
JSbin 预览
因为 transition
上设置的 name
属性名为 fade
。所以 style
中 CSS 样式为 fade
开头。如果 transition
上没有设置 name
属性名,那么style
中 CSS 样式为 v
开头,即 v-enter
、v-center-active
等。
Vue 中使用 CCS3 @keyframes 动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> keyframes 动画 </title>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.js"></script>
<style>
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
.fade-enter-active {
transform-origin: left center;
animation: bounce-in 1s;
}
.fade-leave-active {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<div v-if="show">
hello world
</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
}
}
})
</script>
</body>
</html>
自定义
如果 CSS 样式没有进行这种方式命名,而是我们通过自定义的方式命名的,如下:
.active {
transform-origin: left center;
animation: bounce-in 1s;
}
.leave {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
那么在 transition
标签里面就要对这个自定义的 class
进行声明。
<transition name="fade"
enter-active-class="active"
leave-active-class="leave"
>
<div v-if="show">
hello world
</div>
</transition>
Animate.css 库
Animate.css库官网 提供了众多 CSS 效果。引入和使用的原理和 iconfont
类似。
- 下载该库之后,在
link
标签下引入。
<link rel="stylesheet" type="text/css" href="./animate.css">
- 然后在
transition
标签中定义enter-active-class
与leave-active-class
为animate
库中相应的样式。下面示例即是入场动画使用swing
,出场动画使用shake
。
<transition name="fade"
enter-active-class="animated swing"
leave-active-class="animated shake"
>
<div v-if="show">
hello world
</div>
</transition>
值得注意的是,当引入并使用 Animate.css 库时,必须使用自定义 class
名的形式使用 Animate.css。同时,class
当中必须包含一个 animated
,然后再将相应的效果添加到 animated
之后。
appear
上面一节的示例中,当我们在页面刚刚刷新时(第一次元素显示时)也加上一些动画效果,则需要加上 appear
和 appear-active-class
。
<transition name="fade"
appear
enter-active-class="animated swing"
leave-active-class="animated shake"
appear-active-class="animated swing"
>
<div v-if="show">
hello world
</div>
</transition>
同时使用过渡和动画
之前提到的 Animate.css 库 提供的动画是 @keyframes
类型 CSS3 的动画。假如我们希望动画不仅仅只有 @keyframes
的效果,还另外有过渡的动画效果时。这个时候我们可以这样进行代码的编写。
即在 animated
的 class
之后再添加 过渡动画的 CSS class
。
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 3s;
}
<transition
type="transition"
name="fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated shake fade-leave-active"
appear-active-class="animated swing"
>
<div v-if="show">
hello world
</div>
</transition>
动画执行时长: 值得注意的是,
animate
之中的动画执行是 1s,当过渡动画超过 1s 时,在transition
里面添加属性type
,就会以transition
里面动画的时长为动画执行时长。同样的,这个执行时长也可以自定义,通过:duration
属性来进行设置,例如当属性被设置为:duration="10000"
,即时长为 10s。除此之外,还可以单独设置出场、入场动画为不同的时长,:duration="{ enter: 5000, leave: 10000 }"
。
Vue 中的 JS 动画
动画钩子
before-enter
before-enter
会接收到一个参数 el
,即指的是动画 transition
包裹的标签。
<div id="app">
<transition name="fade" @before-enter="handleBeforeEnter">
<div v-show="show">
hello world
</div>
</transition>
<button @click="handleClick">切换</button>
</div>
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
},
// 接收到一个参数 el 代指被包裹的标签
handleBeforeEnter: function(el){
el.style.color = 'red'
}
}
})
enter
enter
会接收两个参数,一个为 el
,指的仍然是动画 transition
包裹的标签。 一个为 done
,是一个回调函数。
<div id="app">
<transition name="fade" @before-enter="handleBeforeEnter" @enter="handleEnter">
<div v-show="show">
hello world
</div>
</transition>
<button @click="handleClick">切换</button>
</div>
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
},
handleBeforeEnter: function(el){
el.style.color = 'red'
},
handleEnter: function(el, done){
setTimeout(() => {
el.style.color = 'green'
done()
},2000)
}
}
})
上面 handleEnter
中的 setTimeout
执行完之后,调用了 done()
。而当done()
被调用的时候,Vue 又会触发一个 after-enter
事件。
after-enter
after-enter
也要接收到参数 el
。
<div id="app">
<transition name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
>
<div v-show="show">
hello world
</div>
</transition>
<button @click="handleClick">切换</button>
</div>
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
},
handleBeforeEnter: function(el){
el.style.color = 'red'
},
handleEnter: function(el, done){
setTimeout(() => {
// 2s 之后改变颜色
el.style.color = 'green'
},2000)
setTimeout(() => {
// 4s 之后 done()
done()
},4000)
},
handleAfterEnter: function(el){
el.style.color = "#000"
}
}
})
出场动画钩子
相应的。出场动画的钩子函数为 before-leave
、leave
和 after-leave
。用法与上面所讲的入场动画钩子函数相似。
动画钩子示例
velocity.js 动画库
在 velocityjs 官网 下载 velocity.js
文件。并通过 script
标签引入 velocity.js
。
按照下面的写法,将 el
、{opacity: 1}
、{duration: 1000, complete: done}
当做参数传递给 Velocity
。
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
},
handleBeforeEnter: function(el){
el.style.opacity = 0
},
handleEnter: function(el, done){
Velocity(el, {opacity: 1}, {duration: 1000, complete: done})
},
handleAfterEnter: function(el){
alert('动画结束')
}
}
})
多个元素与组件的过渡动画
多个元素间过渡动画
在下面的示例中,之所以加上 key
值,是为了让 Vue 不去复用 DOM,达到我们想要的效果。给 transition
添加 mode
属性,out-in
、in-out
分别表示多个属性切换时候的不同的出场、入场顺序效果。
<div id="app">
<transition mode="out-in" name="fade">
<div v-if="show" key="hello">hello world</div>
<div v-else key="bye">Bye World</div>
</transition>
<button @click="handleClick">切换</button>
</div>
多个组件间过渡动画
在下面的示例中,两个组件通过 button
点击之后触发的事件,进行内容的显示和隐藏的切换。
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 1s;
}
<div id="app">
<transition mode="out-in" name="fade">
<child v-if="show"></child>
<child-one v-else></child-one>
</transition>
<button @click="handleClick">切换</button>
</div>
Vue.component('child',{
template: '<div>child</div>'
})
Vue.component('child-one',{
template: '<div>child-one</div>'
})
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
}
}
})
动态组件
除此之外也可以参考之前 深入理解 Vuejs 组件 中动态组件的方式。给 component
外边的 transition
添加 mode
。
列表过渡
参照下面 JSbin 中的示例,构造了一段代码,简单的实现了一个点击 button
按钮添加列表展示数据的 demo。现在我们要针对一整个列表,在增加或者删除的时候实现过渡效果。这里就需要一个新的标签 transition-group
。
![](https://upload-images.jianshu.io/upload_images/12904618-4492cb9fea478ef9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/588/format/webp)
添加 transition-group
等同于给每一个 div
之外添加 transition
。相当于把列表的过渡转化成单个元素标签的过渡。
除此之外跟前几节的过渡动画一样,需要在 style
中添加相应的样式。
动画封装
当需要频繁使用一个动画效果的时候,我们将动画封装到一个组件之中是很好的方法。结合之前的动画钩子,可以实现将模板、样式都封装到组件的效果。当需要使用的时候,直接使用该组件模板标签,并添加相应的 show
属性即可。
<div id="app">
<fade :show="show">
<div>hello world</div>
</fade>
<fade :show="show">
<h1>hello world</h1>
</fade>
<button @click="handleClick">toggle</button>
</div>
// 封装 fade 子组件
Vue.component('fade',{
props: ['show'], // 接收父组件传递过来的 show 参数
// template 模板,并使用动画钩子将样式封装在函数中
template: `
<transition @before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter">
<slot v-if="show"></slot>
</transition>
`,
methods: {
handleBeforeEnter: function(el){
el.style.color = 'red'
},
handleEnter: function(el,done){
setTimeout(() => {
el.style.color = 'green'
},2000)
setTimeout(() => {
done()
},4000)
},
handleAfterEnter: function(el){
el.style.color = "#000"
}
}
})
var vm = new Vue({
el: "#app",
data: {
show: true
},
methods: {
handleClick: function(){
this.show = ! this.show
}
}
})