关于动画
# Vue 使用 CSS 过渡动画
# 动画原理
Vue 中通过使用 transition 标签来实现动画,假设要实现一个淡入淡出的动画,那么他的原理如下:
- 当一个元素被 transition 包裹以后,Vue 会自动分析元素的CSS 样式,然后构建一个动画的流程。这里为 transition 设置
name='fade'
。如果不自定义命名,vue 中默认是name='v'
淡入动画执行流程
- 当淡入动画被执行的瞬间,Vue 会自动为该元素加 class,
fade-enter
和fade-enter-active
。 - 当淡入动画执行完第一帧之后
fade-enter
便被移除当动画执行的过程中,另外增加一个 class 为fade-enter-to
- 直到淡入动画结束的时候,才会移除
fade-enter-active
和fade-enter-to
淡出动画执行流程
- 当淡出动画被执行的瞬间,Vue 会自动为该元素加 class,
fade-leave
和fade-leave-active
。动画执行完第一帧之后fade-leave
便被移除 - 当淡出动画执行完第一帧之后
fade-leave
便被移除当动画执行的过程中,另外增加一个 class 为fade-leave-to
- 直到淡出动画结束的时候,才会移除
fade-leave-active
和fade-leave-to
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 3s;
}
<div id='app'>
<transition name='fade'>
<div v-show='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>
# 使用 keyframes
上述的只是简单的入门例子,那如何做更复杂的动画呢?我们可以使用 css3 更多的动画方式
还是使用上述的例子,只是更改 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;
}
# 使用 Animate.css
其实可以不设置命名fade
,而是单独在 transiton
中设置 class,也能实现动画效果,如下:
<transition
enter-active-class='fade-enter-active'
leave-active-class='fade-leave-active'
>
<div v-show='show'>
hello world
</div>
</transition>
这样我们就可以使用流行的 Animate.css 库了,引入对应的 css 后,直接更改 class 的类名即可,用法如下:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css">
<transition
enter-active-class='animated fadeIn'
leave-active-class='animated fadeOut'
>
<div v-show='show'>
hello world
</div>
</transition>
另外还可以appear
,使网页刷新时也有动画效果
<transition
appear
enter-active-class='animated fadeIn'
leave-active-class='animated fadeOut'
appear-active-class='animated fadeIn'
>
<div v-show='show'>
hello world
</div>
</transition>
关于时长问题,Vue 中可以直接在transition
中设置时长,这个时长是设置 transition 的执行效果的
<transition :duration='3000'></transition>
<!-- 还可以设置的更复杂 -->
<transition :duration='{enter: 5000, leave: 3000}'></transition>
<!-- 使用 duration 是手动设置时长的方式,还可以自动设置以 transition 为基准的时长 -->
<transiton type='transiton'></transiton>
最后,可以直接使用“混合动画”,因为 Animate.css 使用的是 css3 中的 @keyframes 动画,开发者可以增加使用 transition 过渡动画,最后例子如下:
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 3s;
}
<div id='app'>
<transition
name='fade'
:duration='{enter: 5000, leave: 3000}'
appear
enter-active-class='animated swing fade-enter-active'
leave-active-class='animated shake fade-leave-active'
appear-active-class='animated fadeIn'
>
<div v-show='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>
# 多个元素或组件的过渡
首先需要了解到一点,Vue 中会尽量地使用可以复用的组件和元素,所以开发者需要通过使用属性 key
,来防止组件和元素的复用,或者可以使用动态组件。
另外还可以在 transition 中设置 mode 来决定切换顺序
以下是多个元素过渡的例子:
.v-enter,
.v-leave-to {
opacity:0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 1s;
}
<div id='app'>
<transition mode='in-out'>
<div v-if='show' key='hello'>
hello world
</div>
<div v-else key='else'>
else 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>
以下是多个组件过渡的例子,这里使用动态组件。
<div id='app'>
<transition mode='out-in'>
<component :is='type'></component>
</transition>
<button @click='handleClick'>
切换
</button>
</div>
<script>
Vue.component('child-one', {
template: '<div>child-one</div>'
})
Vue.component('child-two', {
template: '<div>child-two</div>'
})
var vm = new Vue({
el: '#app',
data: {
type: 'child-two'
},
methods: {
handleClick: function () {
this.type = this.type === 'child-one' ? 'child-two' : 'child-one'
}
}
})
</script>
# 列表过渡动画
使用 transition-group,原理其实是给每个列表项都包裹一个 transition
.v-enter,
.v-leave-to {
opacity:0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 1s;
}
<div id='app'>
<transition-group>
<div v-for='item in list' :key='item.id'>
{{ item.content }}
</div>
</transition-group>
<button @click='handleClick'>
Add
</button>
</div>
<script>
var count = 0;
var vm = new Vue({
el: '#app',
data: {
list: []
},
methods: {
handleClick: function () {
this.list.push({
id: count++,
content: 'hello world'
})
}
}
})
</script>
# Vue 使用 JS 动画
# 使用动画钩子
使用 js 来写动画同样需要使用 transition 标签,Vue 提供了一系列动画钩子来实现 js 动画。
<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>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
// 使用 js 来实现入场动画钩子
handleClick: function(){
this.show = !this.show
},
handleBeforeEnter: function(el){
el.style.color = 'red'
},
handleEnter: function(el, done){
setTimeout(() => {
el.style.color = 'blue'
}, 2000)
setTimeout(() => {
done()
}, 4000)
},
handleAfterEnter: function(el){
el.style.color = 'black'
}
}
})
</script>
# 使用 velocity.js
velocity.js 是一个简单易用、高性能、功能丰富的轻量级JS动画库。
<script src="//cdnjs.cloudflare.com/ajax/libs/velocity/2.0.3/velocity.min.js"></script>
可以使用各种方式引入 velocity 后
<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>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
// 使用 js 来实现入场动画钩子
handleClick: function () {
this.show = !this.show
},
handleBeforeEnter: function (el) {
el.style.color = 'red'
},
handleEnter: function (el, done) {
Velocity(el, {
color: 'blue'
}, {
duration: 2000,
complete: done
})
},
handleAfterEnter: function (el) {
el.style.color = 'black'
console.log('end')
}
}
})
</script>
# 封装动画
其实,所谓的封装只不过是制作一个组件,然后使用插槽而已。代码如下:
.v-enter,
.v-leave-to {
opacity:0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 3s;
}
<div id='app'>
<fade :show='show'>
<div>
hello world
</div>
</fade>
<button @click='handleClick'>
切换
</button>
</div>
<script>
Vue.component('fade', {
props: ['show'],
template: '<transition><div v-show="show"><slot></slot></div></transition>'
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick: function () {
this.show = !this.show
}
}
})
</script>
还可以直接在组件中使用 js 动画,而不是使用外部的 css
<div id='app'>
<fade :show='show'>
<div>
hello world
</div>
</fade>
<button @click = 'handleClick'>
切换
</button>
</div>
<script>
Vue.component('fade', {
props: ['show'],
template: `
<transition
@before-enter='handleBeforeEnter'
@enter='handleEnter'
@after-enter='handleAfterEnter'
>
<div v-show="show">
<slot></slot>
</div>
</transition>`,
methods: {
handleBeforeEnter: function(el){
el.style.color = 'red'
},
handleEnter: function(el, done){
setTimeout(() => {
el.style.color = 'blue'
}, 2000)
setTimeout(() => {
done()
}, 4000)
},
handleAfterEnter: function(el){
el.style.color = 'black'
}
}
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick: function(){
this.show = !this.show
}
}
})
</script>