前言:
Vue 在插入腾仅、更新或者移除 DOM 時(shí)献汗,提供多種不同方式的應(yīng)用過渡效果熏矿。包括以下工具:
- 在 CSS 過渡和動畫中自動應(yīng)用 class
- 可以配合使用第三方 CSS 動畫庫,如 Animate.css
- 在過渡鉤子函數(shù)中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 動畫庫宇植,如 Velocity.js
那么接下來就讓我們好好Vue中的過度與動畫.
1. 過渡或動畫的了解
Vue 提供了 transition
的封裝組件怠苔,來處理過渡以及動畫
1.1 使用過渡或動畫的場景
在下列情形中同廉,可以給任何元素和組件添加進(jìn)入/離開過渡
- 條件渲染(使用
v-if
) - 條件展示(使用
v-show
) - 動態(tài)組件
- 組件的根節(jié)點(diǎn)
1.2 使用說明
只有dom從隱藏到顯示,或從顯示到隱藏, 才能使用vue的過渡或動畫
動畫的元素需要被Vue所提供的組件
transition
所包裹在插入或刪除
transition
包裹元素時(shí),Vue會在恰當(dāng)?shù)膶?shí)際添加或刪除類名如果過渡的組件提供了
JavaScript鉤子函數(shù)
,則Vue會在恰當(dāng)?shù)臅r(shí)機(jī)調(diào)用這些鉤子函數(shù)
如果沒有找到
JavaScrip鉤子函數(shù)
,并且也沒有檢測到CSS過渡/動畫,則會在下一幀中立即執(zhí)行
2. Vue 過渡的使用
2.1 認(rèn)識過渡
Vue過渡/動畫兩個(gè)過程,分別為進(jìn)入和離開.
Vue會在不同的需要過渡的元素在進(jìn)入/離開的不同時(shí)機(jī)給元素添加不同的類名
不同階段的類名:
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 過度類名的了解
在進(jìn)入/離開的過渡中,會有 6 個(gè) class 切換。
-
v-enter
:定義進(jìn)入過渡的開始狀態(tài)迫肖。在元素被插入之前生效锅劝,在元素被插入之后的下一幀移除。 -
v-enter-active
:定義進(jìn)入過渡生效時(shí)的狀態(tài)蟆湖。在整個(gè)進(jìn)入過渡的階段中應(yīng)用故爵,在元素被插入之前生效,在過渡/動畫完成之后移除隅津。這個(gè)類可以被用來定義進(jìn)入過渡的過程時(shí)間诬垂,延遲和曲線函數(shù)。 -
v-enter-to
:2.1.8 版及以上定義進(jìn)入過渡的結(jié)束狀態(tài)伦仍。在元素被插入之后下一幀生效 (與此同時(shí)v-enter
被移除)结窘,在過渡/動畫完成之后移除。 -
v-leave
:定義離開過渡的開始狀態(tài)充蓝。在離開過渡被觸發(fā)時(shí)立刻生效隧枫,下一幀被移除。 -
v-leave-active
:定義離開過渡生效時(shí)的狀態(tài)谓苟。在整個(gè)離開過渡的階段中應(yīng)用官脓,在離開過渡被觸發(fā)時(shí)立刻生效,在過渡/動畫完成之后移除涝焙。這個(gè)類可以被用來定義離開過渡的過程時(shí)間卑笨,延遲和曲線函數(shù)。 -
v-leave-to
:2.1.8 版及以上定義離開過渡的結(jié)束狀態(tài)仑撞。在離開過渡被觸發(fā)之后下一幀生效 (與此同時(shí)v-leave
被刪除)赤兴,在過渡/動畫完成之后移除。
現(xiàn)在我們對于這些類名有了一定的了解之后,我們通過案例來加深對于這些類名的理解和記憶
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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</button>
<!-- 顯示元素 -->
<transition>
<div v-if="isShow">需要?jiǎng)赢嫷脑?lt;/div>
</transition>
</div>
<script>
// 實(shí)例
const vm = new Vue({
el:"#app",
data:{
isShow:true
}
})
</script>
2.4 過渡前綴
過渡前綴說明
- 如果你使用一個(gè)沒有名字的
<transition>
派草,則v-
是這些類名的默認(rèn)前綴搀缠。 - 如果使用了
<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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</button>
<!-- 顯示元素 -->
<transition name="fade">
<div v-if="isShow">需要?jiǎng)赢嫷脑?lt;/div>
</transition>
</div>
<script>
// 實(shí)例中注冊組件
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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</button>
<!-- 顯示元素 -->
<transition name="fade">
<div v-if="isShow">需要?jiǎng)赢嫷脑?lt;/div>
</transition>
</div>
<script>
// 實(shí)例中注冊組件
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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</button>
<!-- 顯示元素 -->
<transition name="fade">
<div v-if="isShow">需要?jiǎng)赢嫷脑?lt;/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+)
他們的優(yōu)先級高于普通的類名,這對于 Vue 的過渡系統(tǒng)和其他第三方 CSS 動畫庫簸州,如 Animate.css 結(jié)合使用十分有用鉴竭。
示例:
<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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</button>
<!-- 顯示元素 -->
<transition
name="fade"
enter-active-class="enter"
leave-active-class="leave"
>
<div v-if="isShow">需要?jiǎng)赢嫷脑?lt;/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 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動畫庫的好處就是不需要我們自己去寫負(fù)責(zé)的動畫
4.4 最開始入場動畫
<transition
name="fade"
enter-active-class="animated swing"
leave-active-class="animated flipInX"
>
<div v-show="show">Hello world</div>
</transition>
但是你會發(fā)現(xiàn)最開始是沒有入場動畫的,如果我也希望網(wǎng)頁一打開最開始就有入場動畫
應(yīng)該如下處理,添加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>
此時(shí)你會發(fā)現(xiàn)animated.css的animation動畫的時(shí)長和transition的時(shí)長不一樣,那么要以哪個(gè)為準(zhǔn)呢,其實(shí)vue也不知道,所以我們可以添加一個(gè)type屬性來制定動畫時(shí)長以誰為準(zhǔn)
<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 自定義過渡/動畫時(shí)間
同時(shí)我們也可以自定義動畫時(shí)長,通過綁定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>
我們也可以單獨(dú)定義入場動畫時(shí)長和出場動畫時(shí)長
<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提供給的動畫鉤子函數(shù)來綁定事件,然后在事件函數(shù)中處理對應(yīng)的動畫
5.1 動畫入場的鉤子函數(shù)
before-enter 動畫入場運(yùn)動前一刻執(zhí)行
enter 動畫運(yùn)動時(shí)執(zhí)行
after-enter 在動畫enter函數(shù)中運(yùn)行完畢并調(diào)用回調(diào)done時(shí)執(zhí)行
<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">點(diǎn)擊</button>
</div>
<script>
var app = new Vue({
el:'#app',
data:{
show: true
},
methods:{
handleChange(){
this.show= !this.show
},
handleBeforeEnter(el){
// 動畫入場的前一刻執(zhí)行before-enter動畫鉤子函數(shù)
el.style.color="red"
},
handleEnter(el,done){
// 動畫運(yùn)行的鉤子函數(shù)
// done是動畫運(yùn)行完成后的回調(diào)函數(shù)
// 當(dāng)動畫運(yùn)行完成以后執(zhí)行done函數(shù)會觸發(fā)after-enter動畫鉤子函數(shù)的運(yùn)行
setTimeout(() => {
el.style.color = "blue";
},2000);
setTimeout(() => {
done()
},4000)
},
handleAfterEnter(el){
// 動畫完成后執(zhí)行的動畫鉤子函數(shù)
// 只用當(dāng)動畫鉤子函數(shù)enter運(yùn)行完畢后并且調(diào)用done時(shí)才會觸發(fā)次函數(shù)
el.style.color="#000"
}
}
})
</script>
5.2 動畫出場的鉤子函數(shù)
同樣動畫出場的鉤子函數(shù)與動畫入場的鉤子函數(shù)使用是一致的
before-leave
leave
after-leave
我們發(fā)現(xiàn)js動畫沒有動畫時(shí)長需要我么自己調(diào)整,我們可以借助Velocity.js處理js動畫
注意:
- 使用JavaScript 過渡的時(shí)候,在 enter 和 leave 中必須使用 done 進(jìn)行回調(diào)岸浑。否則過渡會立即完成搏存。
- 對于僅使用 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的結(jié)合
首先進(jìn)入官網(wǎng)下載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">點(diǎn)擊</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 // 動畫結(jié)束后的樣式
},{
duration: 2000, // 動畫運(yùn)行的時(shí)間
complete: done // 動畫運(yùn)動完成后執(zhí)行的函數(shù)
})
},
handleAfterEnter(el){
el.style.fontSize="50px"
}
}
})
</script>
6.Vue中多個(gè)元素之間的動畫
6.1 多個(gè)元素之前切換動畫
多元素過渡,就是利用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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</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>
一定要注意動畫內(nèi)部兩個(gè)div的標(biāo)簽復(fù)用,如果不加key,只會看到內(nèi)容改變了,透明度樣式并沒有發(fā)生變化,這就是vue中的dom復(fù)用的機(jī)制,就是為了提升效率, 所以我們要加可以表示 唯一的dom
注意:
- 當(dāng)有相同標(biāo)簽名的元素切換時(shí),需要通過
key
屬性設(shè)置唯一的值來標(biāo)記以讓 Vue 區(qū)分它們 -
key
屬性的作用,在講v-for
已經(jīng)介紹過來, 為了不讓DOM元素復(fù)用 - DOM元素復(fù)用是Vue提升性能的手段,但是因?yàn)镈OM元素復(fù)用了,也就沒有動畫效果了,
- 因此通過
key
屬性阻止DOM元素復(fù)用
6.2 出場入場動畫先后順如
默認(rèn)出場入場動畫是同時(shí)進(jìn)行的,同時(shí)生效的進(jìn)入和離開的過渡不能滿足所有要求,所以Vue提供了過渡模式
mode屬性說明
Vue在transition標(biāo)簽上提供了一個(gè)mode屬性,來設(shè)置出場入場動畫的先后順序
mode的屬性值為
in-out
,則為先執(zhí)行入場動畫,等入場動畫執(zhí)行完畢后在執(zhí)行出場動畫,mode屬性值為
out-in
則先執(zhí)行出場動畫,在執(zhí)行入場動畫
示例:
<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">
<!-- 按鈕,點(diǎn)擊切換元素的顯示和隱藏 -->
<button @click="isShow = !isShow">點(diǎn)擊切換</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. 不利用動態(tài)組件的寫法
示例:
<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">點(diǎn)擊切換</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. 利用動態(tài)組件的方式切換組件
<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">點(diǎn)擊切換</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中的列表過度
目前為止,關(guān)于過渡我們已經(jīng)講到:
- 單個(gè)節(jié)點(diǎn)
- 同一時(shí)間渲染多個(gè)節(jié)點(diǎn)中的一個(gè)
那么怎么同時(shí)渲染整個(gè)列表责静,比如使用 v-for
袁滥?在這種場景中,使用 <transition-group>
組件灾螃。
特點(diǎn):
-
<transition>
標(biāo)簽會以一個(gè)真實(shí)元素呈現(xiàn):默認(rèn)是<span>
题翻。也可以通過tag
屬性 更換為其他元素。 -
過渡模式
不可用腰鬼,因?yàn)槲覀儾辉傧嗷デ袚Q特有的元素嵌赠。 - 內(nèi)部元素總是需要提供唯一的
key
屬性 值。 - CSS 過渡的類將會應(yīng)用在內(nèi)部的元素中熄赡,而不是這個(gè)組/容器本身姜挺。
示例如下:
vue中的列表過渡使用的是transition-group標(biāo)簽嵌套
<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">點(diǎn)擊新增</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>
其實(shí)transition-group的原理就是給每一個(gè)列表循環(huán)的單獨(dú)的元素套了一層transition標(biāo)簽,說白了,還是單個(gè)元素的過渡動畫
就像這樣
<transition>
<div>Hello World</div>
</transition>
<transition>
<div>Hello World</div>
</transition>
<transition>
<div>Hello World</div>
</transition>
....
注意,for循環(huán)的key值盡量不要用index索引,會導(dǎo)致性能降低,其次項(xiàng)目功能上也會造成一定的影響,如果能不用index作為key值,盡量不要用index作為key值
9. 封裝Vue動畫
利用組件和slot的方式,將動畫封裝成一個(gè)組件,這樣可以通過組件調(diào)用發(fā)方式復(fù)用相同動畫
<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">點(diǎn)擊切換</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 實(shí)例
const vm = new Vue({
el:'#app',
data:{
show: true
},
methods:{
handleChange(){
this.show= !this.show
}
}
})
</script>
當(dāng)然你還可以繼續(xù)進(jìn)一步封裝完善, 達(dá)到你想要的目的