前言:
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:
- 在 CSS 过渡和动画中自动应用 class
- 可以配合使用第三方 CSS 动画库,如 Animate.css
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
那么接下来就让我们好好Vue中的过度与动画.
1. 过渡或动画的了解
Vue 提供了 transition
的封装组件,来处理过渡以及动画
1.1 使用过渡或动画的场景
在下列情形中,可以给任何元素和组件添加进入/离开过渡
- 条件渲染(使用
v-if
) - 条件展示(使用
v-show
) - 动态组件
- 组件的根节点
1.2 使用说明
只有dom从隐藏到显示,或从显示到隐藏, 才能使用vue的过渡或动画
动画的元素需要被Vue所提供的组件
transition
所包裹在插入或删除
transition
包裹元素时,Vue会在恰当的实际添加或删除类名如果过渡的组件提供了
JavaScript钩子函数
,则Vue会在恰当的时机调用这些钩子函数
如果没有找到
JavaScrip钩子函数
,并且也没有检测到CSS过渡/动画,则会在下一帧中立即执行
2. Vue 过渡的使用
2.1 认识过渡
Vue过渡/动画两个过程,分别为进入和离开.
Vue会在不同的需要过渡的元素在进入/离开的不同时机给元素添加不同的类名
不同阶段的类名:
Enter | Enter | Leave | Leave |
---|---|---|---|
Opacity:0 | Opacity:1 | Opacity:1 | Opacity:0 |
v-enter | v-enter-to | v-leave | v-leave-to |
v-enter-active | v-enter-active | v-leave-active | v-leave-active |
2.2 过度类名的了解
在进入/离开的过渡中,会有 6 个 class 切换。
-
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。 -
v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 -
v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。 -
v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。 -
v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 -
v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
现在我们对于这些类名有了一定的了解之后,我们通过案例来加深对于这些类名的理解和记忆
2.3 示例:
<style>
div{
font-size:30px;
font-weight: bold;
}
.v-enter{
color:red;
}
.v-enter-to{
color:blue;
}
.v-enter-active{
transition:all 5s;
}
.v-leave{
color: blue;
}
.v-leave-to{
color: purple;
}
.v-leave-active{
transition:all 3s;
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition>
<div v-if="isShow">需要动画的元素</div>
</transition>
</div>
<script>
// 实例
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
2.4 过渡前缀
过渡前缀说明
- 如果你使用一个没有名字的
<transition>
,则v-
是这些类名的默认前缀。 - 如果使用了
<transition name="myname">
,那么v-enter
会替换为myname-enter
。
示例:
<style>
div{
font-size:30px;
font-weight: bold;
}
.fade-enter,
.fade-leave-to{
color:red;
}
.fade-enter-to{
color:blue;
}
.fade-enter-active,
.fade-leave-active{
transition:all 3s;
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition name="fade">
<div v-if="isShow">需要动画的元素</div>
</transition>
</div>
<script>
// 实例中注册组件
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
2.5 在过渡中使用贝塞尔曲线
<style>
div{
position: fixed;
font-size:30px;
font-weight: bold;
}
.fade-enter,
.fade-leave-to{
left:200px;
}
.fade-enter-to{
left:10px;
}
.fade-leave{
left:10px;
}
.fade-enter-active{
transition:all 3s linear;
}
.fade-leave-active{
transition:all 3s cubic-bezier(.94,.1,.93,.08);
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition name="fade">
<div v-if="isShow">需要动画的元素</div>
</transition>
</div>
<script>
// 实例中注册组件
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
3. CSS动画
我们处理可以在Vue中使用过渡transition
,还可以使用CSS动画animation
<style>
div{
font-size:30px;
font-weight: bold;
}
@keyframes fade-in{
0%{
transform: scale(0);
}
50%{
transform: scale(1.5);
}
100%{
transform: scale(1);
}
}
.fade-enter-active{
animation: fade-in 10s linear;
}
.fade-leave-active{
animation: fade-in 10s linear reverse;
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition name="fade">
<div v-if="isShow">需要动画的元素</div>
</transition>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
3. 自定义过度动画的类名
我们可以通过以下 attribute
来自定义过渡类名:
enter-class
enter-active-class
-
enter-to-class
(2.1.8+) leave-class
leave-active-class
-
leave-to-class
(2.1.8+)
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。
示例:
<style>
div{
font-size:30px;
font-weight: bold;
}
@keyframes fade-in{
0%{transform: scale(0);}
50%{transform: scale(1.5);}
100%{transform: scale(1);}
}
.enter{
transform-origin:left center;
animation: fade-in 3s linear;
}
.leave{
transform-origin:left center;
animation: fade-in 3s linear reverse;
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition
name="fade"
enter-active-class="enter"
leave-active-class="leave"
>
<div v-if="isShow">需要动画的元素</div>
</transition>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
4.动画插件库
animated.css 使用的是keyframes 的动画
4.1 安装
安装
npm install animate.css --save-dev
引入
<link rel="stylesheet" href="./node_modules/animate.css/animate.css">
4.2 使用cdn
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
4.3 使用
<transition enter-active-class="animated rotateIn" leave-active-class="animated rotateIn">
<div v-show="show" class='box'>Hello world</div>
</transition>
使用animation动画库的好处就是不需要我们自己去写负责的动画
4.4 最开始入场动画
<transition
name="fade"
enter-active-class="animated swing"
leave-active-class="animated flipInX"
>
<div v-show="show">Hello world</div>
</transition>
但是你会发现最开始是没有入场动画的,如果我也希望网页一打开最开始就有入场动画
应该如下处理,添加appear属性
<transition
name="fade"
appear
enter-active-class="animated swing"
leave-active-class="animated flipInX"
appear-active-class="animated swing"
>
<div v-show="show">Hello wold</div>
</transition>
4.5 animation动画和transition过渡一起使用
4.5.1 动画过渡一起使用
示例:
<style>
.fade-enter,
.fade-leave-to{
opacity:0;
}
.fade-enter-active,
.fade-leave-active{
transition: opacity 3s
}
</style>
<div id="app">
<transition
name="fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated flipInX fade-leave-active"
appear-active-class="animated swing"
>
<div v-show="show">Hello wold</div>
</transition>
此时你会发现animated.css的animation动画的时长和transition的时长不一样,那么要以哪个为准呢,其实vue也不知道,所以我们可以添加一个type属性来制定动画时长以谁为准
<transition
type="transition"
name="fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated flipInX fade-leave-active"
appear-active-class="animated swing"
>
<div v-show="show">Hello wold</div>
</transition>
4.5.2 自定义过渡/动画时间
同时我们也可以自定义动画时长,通过绑定duration,5秒后
<transition
:duration="5000"
name="fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated flipInX fade-leave-active"
appear-active-class="animated swing"
>
<div v-show="show">Hello wold</div>
</transition>
我们也可以单独定义入场动画时长和出场动画时长
<transition
:duration="{enter:5000,leave:10000}"
name="fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated flipInX fade-leave-active"
appear-active-class="animated swing"
>
<div v-show="show">Hello wold</div>
</transition>
5. Vue中的JS动画
vue中的js动画是通过vue提供给的动画钩子函数来绑定事件,然后在事件函数中处理对应的动画
5.1 动画入场的钩子函数
before-enter 动画入场运动前一刻执行
enter 动画运动时执行
after-enter 在动画enter函数中运行完毕并调用回调done时执行
<div id="app">
<transition
name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
>
<div v-show="show">Hello wold</div>
</transition>
<button @click="handleChange">点击</button>
</div>
<script>
var app = new Vue({
el:'#app',
data:{
show: true
},
methods:{
handleChange(){
this.show= !this.show
},
handleBeforeEnter(el){
// 动画入场的前一刻执行before-enter动画钩子函数
el.style.color="red"
},
handleEnter(el,done){
// 动画运行的钩子函数
// done是动画运行完成后的回调函数
// 当动画运行完成以后执行done函数会触发after-enter动画钩子函数的运行
setTimeout(() => {
el.style.color = "blue";
},2000);
setTimeout(() => {
done()
},4000)
},
handleAfterEnter(el){
// 动画完成后执行的动画钩子函数
// 只用当动画钩子函数enter运行完毕后并且调用done时才会触发次函数
el.style.color="#000"
}
}
})
</script>
5.2 动画出场的钩子函数
同样动画出场的钩子函数与动画入场的钩子函数使用是一致的
before-leave
leave
after-leave
我们发现js动画没有动画时长需要我么自己调整,我们可以借助Velocity.js处理js动画
注意:
- 使用JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则过渡会立即完成。
- 对于仅使用 JavaScript 过渡的元素添加
v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。
例如:
<transition
:css="false"
name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
>
<div v-show="show">Hello wold</div>
</transition>
5.3 Vue中JS动画与Velocity.js的结合
首先进入官网下载Velocity.js
然后引入
<script src="./velocity.js"></script>
再次使用
<div id="app">
<transition
name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
>
<div v-show="show">Hello wold</div>
</transition>
<button @click="handleChange">点击</button>
</div>
<script src="./vue.js"></script>
<script>
var app = new Vue({
el:'#app',
data:{
show: true
},
methods:{
handleChange(){
this.show= !this.show
},
handleBeforeEnter(el){
el.style.opacity=0 // 动画开始样式
},
handleEnter(el,done){
Velocity(el,{
opacity: 1 // 动画结束后的样式
},{
duration: 2000, // 动画运行的时间
complete: done // 动画运动完成后执行的函数
})
},
handleAfterEnter(el){
el.style.fontSize="50px"
}
}
})
</script>
6.Vue中多个元素之间的动画
6.1 多个元素之前切换动画
多元素过渡,就是利用v-if
或者v-show
等指令,切换元素的显示, 需要显示的元素就会有入场动画,需要隐藏的就有出场动画.
过渡示例:
<style>
div{
font-size:30px;
font-weight: bold;
opacity:1;
}
.fade-enter,.fade-leave-to{
opacity: 0;
}
.fade-enter-active,.fade-leave-active{
transition:all 3s;
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition
name="fade"
>
<div v-if="isShow" key="hello">Hello wold</div>
<div v-else key="bye">Bye wold</div>
</transition>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
一定要注意动画内部两个div的标签复用,如果不加key,只会看到内容改变了,透明度样式并没有发生变化,这就是vue中的dom复用的机制,就是为了提升效率, 所以我们要加可以表示 唯一的dom
注意:
- 当有相同标签名的元素切换时,需要通过
key
属性设置唯一的值来标记以让 Vue 区分它们 -
key
属性的作用,在讲v-for
已经介绍过来, 为了不让DOM元素复用 - DOM元素复用是Vue提升性能的手段,但是因为DOM元素复用了,也就没有动画效果了,
- 因此通过
key
属性阻止DOM元素复用
6.2 出场入场动画先后顺如
默认出场入场动画是同时进行的,同时生效的进入和离开的过渡不能满足所有要求,所以Vue提供了过渡模式
mode属性说明
Vue在transition标签上提供了一个mode属性,来设置出场入场动画的先后顺序
mode的属性值为
in-out
,则为先执行入场动画,等入场动画执行完毕后在执行出场动画,mode属性值为
out-in
则先执行出场动画,在执行入场动画
示例:
<style>
div{
font-size:30px;
font-weight: bold;
opacity:1;
}
.fade-enter,.fade-leave-to{
opacity: 0;
}
.fade-enter-active,.fade-leave-active{
transition:all 3s;
}
</style>
<div id="app">
<!-- 按钮,点击切换元素的显示和隐藏 -->
<button @click="isShow = !isShow">点击切换</button>
<!-- 显示元素 -->
<transition
name="fade"
mode="in-out"
>
<div v-if="isShow" key="hello">Hello wold</div>
<div v-else key="bye">Bye wold</div>
</transition>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
7. Vue中组件之间的动画
7.1. 不利用动态组件的写法
示例:
<style>
.fade-enter,.fade-leave-to{
opacity:0
}
.fade-enter-active,.fade-leave-active{
transition: opacity 1s;
}
</style>
<div id="app">
<transition
name="fade"
mode="out-in"
>
<child v-if="show"></child>
<child-one v-else></child-one>
</transition>
<button @click="handleChange">点击切换</button>
</div>
<script>
Vue.component("child",{
template:"<div>chile</div>"
})
Vue.component("child-one",{
template:"<div>chile-on</div>"
})
var app = new Vue({
el:'#app',
data:{
show: true
},
methods:{
handleChange(){
this.show= !this.show
}
}
})
</script>
7.2. 利用动态组件的方式切换组件
<style>
.fade-enter,.fade-leave-to{
opacity:0
}
.fade-enter-active,.fade-leave-active{
transition: opacity 1s;
}
</style>
<div id="app">
<transition name="fade" mode="out-in">
<component :is="type"></component>
</transition>
<button @click="handleChange">点击切换</button>
</div>
<script>
Vue.component("child",{
template:"<div>chile</div>"
})
Vue.component("child-one",{
template:"<div>chile-on</div>"
})
var app = new Vue({
el:'#app',
data:{
type: "child"
},
methods:{
handleChange(){
this.type = this.type=="child"?"child-one":"child"
}
}
})
</script>
8. Vue中的列表过度
目前为止,关于过渡我们已经讲到:
- 单个节点
- 同一时间渲染多个节点中的一个
那么怎么同时渲染整个列表,比如使用 v-for
?在这种场景中,使用 <transition-group>
组件。
特点:
-
<transition>
标签会以一个真实元素呈现:默认是<span>
。也可以通过tag
属性 更换为其他元素。 -
过渡模式
不可用,因为我们不再相互切换特有的元素。 - 内部元素总是需要提供唯一的
key
属性 值。 - CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
示例如下:
vue中的列表过渡使用的是transition-group标签嵌套
<style>
.fade-enter,.fade-leave-to{
opacity:0
}
.fade-enter-active,.fade-leave-active{
transition: opacity 1s;
}
</style>
<div id="app">
<transition-group name="fade">
<div v-for="item in list" :key="item.id">
{{item.title}}
</div>
</transition-group>
<button @click="handleChange">点击新增</button>
</div>
<script>
var app = new Vue({
el:'#app',
data:{
count: 0,
list: []
},
methods:{
handleChange(){
this.list.push({
id: this.count++,
title: "Hello World"
})
}
}
})
</script>
其实transition-group的原理就是给每一个列表循环的单独的元素套了一层transition标签,说白了,还是单个元素的过渡动画
就像这样
<transition>
<div>Hello World</div>
</transition>
<transition>
<div>Hello World</div>
</transition>
<transition>
<div>Hello World</div>
</transition>
....
注意,for循环的key值尽量不要用index索引,会导致性能降低,其次项目功能上也会造成一定的影响,如果能不用index作为key值,尽量不要用index作为key值
9. 封装Vue动画
利用组件和slot的方式,将动画封装成一个组件,这样可以通过组件调用发方式复用相同动画
<style>
.box{
font-size:30px;
font-weight:bold;
}
.fade-enter,.fade-leave-to{
opacity:0
}
.fade-enter-active,.fade-leave-active{
transition: all 3s
}
</style>
<div id="app">
<button @click="handleChange">点击切换</button>
<fade :show="show" name="fade">
<div class="box">Hello wold</div>
</fade>
</div>
<!-- 组件模板 -->
<template id="myTransition">
<transition
:name="name"
>
<slot v-if="show"></slot>
</transition>
</template>
<script>
// 定义全局组件
Vue.component("fade",{
props:["show","name"],
template:`#myTransition`,
})
// Vue 实例
const vm = new Vue({
el:'#app',
data:{
show: true
},
methods:{
handleChange(){
this.show= !this.show
}
}
})
</script>
当然你还可以继续进一步封装完善, 达到你想要的目的