Vue 提供了(le)兩個內(nèi)置組件,可(kě)以幫助你制作(zuò)基于狀态變化的過渡和(hé)動畫(huà):
<Transition> 會在一(yī)個元素或組件進入和(hé)離開(kāi) DOM 時(shí)應用動畫(huà)。本章(zhāng)節會介紹如(rú)何使用它。
<TransitionGroup> 會在一(yī)個 v-for 列表中的元素或組件被插入,移動,或移除時(shí)應用動畫(huà)。我們将在下(xià)一(yī)章(zhāng)節中介紹。
除了(le)這(zhè)兩個組件,我們也可(kě)以通(tōng)過其他(tā)技術(shù)手段來應用動畫(huà),比如(rú)切換 CSS class 或用狀态綁定樣式來驅動動畫(huà)。這(zhè)些其他(tā)的方法會在動畫(huà)技巧章(zhāng)節中展開(kāi)。
<Transition> 組件
<Transition> 是一(yī)個內(nèi)置組件,這(zhè)意味着它在任意别的組件中都(dōu)可(kě)以被使用,無需注冊。它可(kě)以将進入和(hé)離開(kāi)動畫(huà)應用到通(tōng)過默認插槽傳遞給它的元素或組件上(shàng)。進入或離開(kāi)可(kě)以由以下(xià)的條件之一(yī)觸發:
由 v-if 所觸發的切換
由 v-show 所觸發的切換
由特殊元素 <component> 切換的動态組件
改變特殊的 key 屬性
以下(xià)是最基本用法的示例:
template
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
css
/* 下(xià)面我們會解釋這(zhè)些 class 是做(zuò)什麽的 */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
TIP
<Transition> 僅支持單個元素或組件作(zuò)為(wèi)其插槽內(nèi)容。如(rú)果內(nèi)容是一(yī)個組件,這(zhè)個組件必須僅有一(yī)個根元素。
當一(yī)個 <Transition> 組件中的元素被插入或移除時(shí),會發生下(xià)面這(zhè)些事情:
Vue 會自(zì)動檢測目标元素是否應用了(le) CSS 過渡或動畫(huà)。如(rú)果是,則一(yī)些 CSS 過渡 class 會在适當的時(shí)機被添加和(hé)移除。
如(rú)果有作(zuò)為(wèi)監聽器的 JavaScript 鈎子,這(zhè)些鈎子函數(shù)會在适當時(shí)機被調用。
如(rú)果沒有探測到 CSS 過渡或動畫(huà)、也沒有提供 JavaScript 鈎子,那(nà)麽 DOM 的插入、删除操作(zuò)将在浏覽器的下(xià)一(yī)個動畫(huà)幀後執行(xíng)。
基于 CSS 的過渡效果
CSS 過渡 class
一(yī)共有 6 個應用于進入與離開(kāi)過渡效果的 CSS class。
v-enter-from:進入動畫(huà)的起始狀态。在元素插入之前添加,在元素插入完成後的下(xià)一(yī)幀移除。
v-enter-active:進入動畫(huà)的生效狀态。應用于整個進入動畫(huà)階段。在元素被插入之前添加,在過渡或動畫(huà)完成之後移除。這(zhè)個 class 可(kě)以被用來定義進入動畫(huà)的持續時(shí)間(jiān)、延遲與速度曲線類型。
v-enter-to:進入動畫(huà)的結束狀态。在元素插入完成後的下(xià)一(yī)幀被添加 (也就是 v-enter-from 被移除的同時(shí)),在過渡或動畫(huà)完成之後移除。
v-leave-from:離開(kāi)動畫(huà)的起始狀态。在離開(kāi)過渡效果被觸發時(shí)立即添加,在一(yī)幀後被移除。
v-leave-active:離開(kāi)動畫(huà)的生效狀态。應用于整個離開(kāi)動畫(huà)階段。在離開(kāi)過渡效果被觸發時(shí)立即添加,在過渡或動畫(huà)完成之後移除。這(zhè)個 class 可(kě)以被用來定義離開(kāi)動畫(huà)的持續時(shí)間(jiān)、延遲與速度曲線類型。
v-leave-to:離開(kāi)動畫(huà)的結束狀态。在一(yī)個離開(kāi)動畫(huà)被觸發後的下(xià)一(yī)幀被添加 (也就是 v-leave-from 被移除的同時(shí)),在過渡或動畫(huà)完成之後移除。
v-enter-active 和(hé) v-leave-active 給我們提供了(le)為(wèi)進入和(hé)離開(kāi)動畫(huà)指定不同速度曲線的能(néng)力,我們将在下(xià)面的小節中看(kàn)到一(yī)個示例。
為(wèi)過渡效果命名
我們可(kě)以給 <Transition> 組件傳一(yī)個 name prop 來聲明(míng)一(yī)個過渡效果名:
template
<Transition name="fade">
...
</Transition>
對于一(yī)個有名字的過渡效果,對它起作(zuò)用的過渡 class 會以其名字而不是 v 作(zuò)為(wèi)前綴。比如(rú),上(shàng)方例子中被應用的 class 将會是 fade-enter-active 而不是 v-enter-active。這(zhè)個“fade”過渡的 class 應該是這(zhè)樣:
css
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
CSS 的 transition
<Transition> 一(yī)般都(dōu)會搭配原生 CSS 過渡一(yī)起使用,正如(rú)你在上(shàng)面的例子中所看(kàn)到的那(nà)樣。這(zhè)個 transition CSS 屬性是一(yī)個簡寫形式,使我們可(kě)以一(yī)次定義一(yī)個過渡的各個方面,包括需要(yào)執行(xíng)動畫(huà)的屬性、持續時(shí)間(jiān)和(hé)速度曲線。
下(xià)面是一(yī)個更高級的例子,它使用了(le)不同的持續時(shí)間(jiān)和(hé)速度曲線來過渡多個屬性:
template
<Transition name="slide-fade">
<p v-if="show">hello</p>
</Transition>
css
/*
進入和(hé)離開(kāi)動畫(huà)可(kě)以使用不同
持續時(shí)間(jiān)和(hé)速度曲線。
*/
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
CSS 的 animation
原生 CSS 動畫(huà)和(hé) CSS transition 的應用方式基本上(shàng)是相同的,隻有一(yī)點不同,那(nà)就是 *-enter-from 不是在元素插入後立即移除,而是在一(yī)個 animationend 事件觸發時(shí)被移除。
對于大多數(shù)的 CSS 動畫(huà),我們可(kě)以簡單地(dì)在 *-enter-active 和(hé) *-leave-active class 下(xià)聲明(míng)它們。下(xià)面是一(yī)個示例:
template
<Transition name="bounce">
<p v-if="show" style="text-align: center;">
Hello here is some bouncy text!
</p>
</Transition>
css
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
自(zì)定義過渡 class
你也可(kě)以向 <Transition> 傳遞以下(xià)的 props 來指定自(zì)定義的過渡 class:
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
你傳入的這(zhè)些 class 會覆蓋相應階段的默認 class 名。這(zhè)個功能(néng)在你想要(yào)在 Vue 的動畫(huà)機制下(xià)集成其他(tā)的第三方 CSS 動畫(huà)庫時(shí)非常有用,比如(rú) Animate.css:
template
<!-- 假設你已經在頁面中引入了(le) Animate.css -->
<Transition
name="custom-classes"
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<p v-if="show">hello</p>
</Transition>
同時(shí)使用 transition 和(hé) animation
Vue 需要(yào)附加事件監聽器,以便知道(dào)過渡何時(shí)結束。可(kě)以是 transitionend 或 animationend,這(zhè)取決于你所應用的 CSS 規則。如(rú)果你僅僅使用二者的其中之一(yī),Vue 可(kě)以自(zì)動探測到正确的類型。
然而在某些場景中,你或許想要(yào)在同一(yī)個元素上(shàng)同時(shí)使用它們兩個。舉例來說,Vue 觸發了(le)一(yī)個 CSS 動畫(huà),同時(shí)鼠标懸停觸發另一(yī)個 CSS 過渡。此時(shí)你需要(yào)顯式地(dì)傳入 type prop 來聲明(míng),告訴 Vue 需要(yào)關心哪種類型,傳入的值是 animation 或 transition:
template
<Transition type="animation">...</Transition>
深層級過渡與顯式過渡時(shí)長(cháng)
盡管過渡 class 僅能(néng)應用在 <Transition> 的直接子元素上(shàng),我們還是可(kě)以使用深層級的 CSS 選擇器,在深層級的元素上(shàng)觸發過渡效果。
template
<Transition name="nested">
<div v-if="show" class="outer">
<div class="inner">
Hello
</div>
</div>
</Transition>
css
/* 應用于嵌套元素的規則 */
.nested-enter-active .inner,
.nested-leave-active .inner {
transition: all 0.3s ease-in-out;
}
.nested-enter-from .inner,
.nested-leave-to .inner {
transform: translateX(30px);
opacity: 0;
}
/* ... 省略了(le)其他(tā)必要(yào)的 CSS */
我們甚至可(kě)以在深層元素上(shàng)添加一(yī)個過渡延遲,從(cóng)而創建一(yī)個帶漸進延遲的動畫(huà)序列:
css
/* 延遲嵌套元素的進入以獲得交錯效果 */
.nested-enter-active .inner {
transition-delay: 0.25s;
}
然而,這(zhè)會帶來一(yī)個小問(wèn)題。默認情況下(xià),<Transition> 組件會通(tōng)過監聽過渡根元素上(shàng)的第一(yī)個 transitionend 或者 animationend 事件來嘗試自(zì)動判斷過渡何時(shí)結束。而在嵌套的過渡中,期望的行(xíng)為(wèi)應該是等待所有內(nèi)部元素的過渡完成。
在這(zhè)種情況下(xià),你可(kě)以通(tōng)過向 <Transition> 組件傳入 duration prop 來顯式指定過渡的持續時(shí)間(jiān) (以毫秒為(wèi)單位)。總持續時(shí)間(jiān)應該匹配延遲加上(shàng)內(nèi)部元素的過渡持續時(shí)間(jiān):
template
<Transition :duration="550">...</Transition>
如(rú)果有必要(yào)的話,你也可(kě)以用對象的形式傳入,分開(kāi)指定進入和(hé)離開(kāi)所需的時(shí)間(jiān):
template
<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>
性能(néng)考量
你可(kě)能(néng)注意到我們上(shàng)面例子中展示的動畫(huà)所用到的 CSS 屬性大多是 transform 和(hé) opacity 之類的。用這(zhè)些屬性制作(zuò)動畫(huà)非常高效,因為(wèi):
他(tā)們在動畫(huà)過程中不會影響到 DOM 結構,因此不會每一(yī)幀都(dōu)觸發昂貴的 CSS 布局重新計算。
大多數(shù)的現代浏覽器都(dōu)可(kě)以在執行(xíng) transform 動畫(huà)時(shí)利用 GPU 進行(xíng)硬件加速。
相比之下(xià),像 height 或者 margin 這(zhè)樣的屬性會觸發 CSS 布局變動,因此執行(xíng)它們的動畫(huà)效果更昂貴,需要(yào)謹慎使用。我們可(kě)以在 CSS-Triggers 這(zhè)類的網站查詢哪些屬性會在執行(xíng)動畫(huà)時(shí)觸發 CSS 布局變動。
JavaScript 鈎子
你可(kě)以通(tōng)過監聽 <Transition> 組件事件的方式在過渡過程中挂上(shàng)鈎子函數(shù):
html
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<!-- ... -->
</Transition>
js
export default {
// ...
methods: {
// 在元素被插入到 DOM 之前被調用
// 用這(zhè)個來設置元素的 "enter-from" 狀态
onBeforeEnter(el) {},
// 在元素被插入到 DOM 之後的下(xià)一(yī)幀被調用
// 用這(zhè)個來開(kāi)始進入動畫(huà)
onEnter(el, done) {
// 調用回調函數(shù) done 表示過渡結束
// 如(rú)果與 CSS 結合使用,則這(zhè)個回調是可(kě)選參數(shù)
done()
},
// 當進入過渡完成時(shí)調用。
onAfterEnter(el) {},
onEnterCancelled(el) {},
// 在 leave 鈎子之前調用
// 大多數(shù)時(shí)候,你應該隻會用到 leave 鈎子
onBeforeLeave(el) {},
// 在離開(kāi)過渡開(kāi)始時(shí)調用
// 用這(zhè)個來開(kāi)始離開(kāi)動畫(huà)
onLeave(el, done) {
// 調用回調函數(shù) done 表示過渡結束
// 如(rú)果與 CSS 結合使用,則這(zhè)個回調是可(kě)選參數(shù)
done()
},
// 在離開(kāi)過渡完成、
// 且元素已從(cóng) DOM 中移除時(shí)調用
onAfterLeave(el) {},
// 僅在 v-show 過渡中可(kě)用
onLeaveCancelled(el) {}
}
}
這(zhè)些鈎子可(kě)以與 CSS 過渡或動畫(huà)結合使用,也可(kě)以單獨使用。
在使用僅由 JavaScript 執行(xíng)的動畫(huà)時(shí),最好是添加一(yī)個 :css="false" prop。這(zhè)顯式地(dì)向 Vue 表明(míng)可(kě)以跳(tiào)過對 CSS 過渡的自(zì)動探測。除了(le)性能(néng)稍好一(yī)些之外(wài),還可(kě)以防止 CSS 規則意外(wài)地(dì)幹擾過渡效果。
template
<Transition
...
:css="false"
>
...
</Transition>
在有了(le) :css="false" 後,我們就自(zì)己全權負責控制什麽時(shí)候過渡結束了(le)。這(zhè)種情況下(xià)對于 @enter 和(hé) @leave 鈎子來說,回調函數(shù) done 就是必須的。否則,鈎子将被同步調用,過渡将立即完成。
這(zhè)裏是使用 GreenSock 庫執行(xíng)動畫(huà)的一(yī)個示例,你也可(kě)以使用任何你想要(yào)的庫,比如(rú) Anime.js 或者 Motion One。
可(kě)複用過渡效果
得益于 Vue 的組件系統,過渡效果是可(kě)以被封裝複用的。要(yào)創建一(yī)個可(kě)被複用的過渡,我們需要(yào)為(wèi) <Transition> 組件創建一(yī)個包裝組件,并向內(nèi)傳入插槽內(nèi)容:
vue
<!-- MyTransition.vue -->
<script>
// JavaScript 鈎子邏輯...
</script>
<template>
<!-- 包裝內(nèi)置的 Transition 組件 -->
<Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- 向內(nèi)傳遞插槽內(nèi)容 -->
</Transition>
</template>
<style>
/*
必要(yào)的 CSS...
注意:避免在這(zhè)裏使用 <style scoped>
因為(wèi)那(nà)不會應用到插槽內(nèi)容上(shàng)
*/
</style>
現在 MyTransition 可(kě)以在導入後像內(nèi)置組件那(nà)樣使用了(le):
template
<MyTransition>
<div v-if="show">Hello</div>
</MyTransition>
出現時(shí)過渡
如(rú)果你想在某個節點初次渲染時(shí)應用一(yī)個過渡效果,你可(kě)以添加 appear prop:
template
<Transition appear>
...
</Transition>
元素間(jiān)過渡
除了(le)通(tōng)過 v-if / v-show 切換一(yī)個元素,我們也可(kě)以通(tōng)過 v-if / v-else / v-else-if 在幾個組件間(jiān)進行(xíng)切換,隻要(yào)确保任一(yī)時(shí)刻隻會有一(yī)個元素被渲染即可(kě):
template
<Transition>
<button v-if="docState === 'saved'">Edit</button>
<button v-else-if="docState === 'edited'">Save</button>
<button v-else-if="docState === 'editing'">Cancel</button>
</Transition>
過渡模式
在之前的例子中,進入和(hé)離開(kāi)的元素都(dōu)是在同時(shí)開(kāi)始動畫(huà)的,因此我們不得不将它們設為(wèi) position: absolute 以避免二者同時(shí)存在時(shí)出現的布局問(wèn)題。
然而,很(hěn)多情況下(xià)這(zhè)可(kě)能(néng)并不符合需求。我們可(kě)能(néng)想要(yào)先執行(xíng)離開(kāi)動畫(huà),然後在其完成之後再執行(xíng)元素的進入動畫(huà)。手動編排這(zhè)樣的動畫(huà)是非常複雜(zá)的,好在我們可(kě)以通(tōng)過向 <Transition> 傳入一(yī)個 mode prop 來實現這(zhè)個行(xíng)為(wèi):
template
<Transition mode="out-in">
...
</Transition>
将之前的例子改為(wèi) mode="out-in" 後是這(zhè)樣:
Click to cycle through states:
Edit
<Transition> 也支持 mode="in-out",雖然這(zhè)并不常用。
組件間(jiān)過渡
<Transition> 也可(kě)以作(zuò)用于動态組件之間(jiān)的切換:
template
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
<Transition> 的 props (比如(rú) name) 也可(kě)以是動态的!這(zhè)讓我們可(kě)以根據狀态變化動态地(dì)應用不同類型的過渡:
template
<Transition :name="transitionName">
<!-- ... -->
</Transition>
這(zhè)個特性的用處是可(kě)以提前定義好多組 CSS 過渡或動畫(huà)的 class,然後在它們之間(jiān)動态切換。
你也可(kě)以根據你的組件的當前狀态在 JavaScript 過渡鈎子中應用不同的行(xíng)為(wèi)。最後,創建動态過渡的終極方式還是創建可(kě)複用的過渡組件,并讓這(zhè)些組件根據動态的 props 來改變過渡的效果。掌握了(le)這(zhè)些技巧後,就真的隻有你想不到,沒有做(zuò)不到的了(le)。
網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發