做自(zì)由與創造的先行者

Transition

Vue.js中文(wén)手冊

Vue 提供了(le)兩個内置組件,可以幫助你(nǐ)制作(zuò)基于狀态變化的過渡和(hé)動畫(huà):

<Transition> 會(huì)在一個元素或組件進入和(hé)離開(kāi) DOM 時(shí)應用(yòng)動畫(huà)。本章節會(huì)介紹如何使用(yòng)它。

<TransitionGroup> 會(huì)在一個 v-for 列表中的元素或組件被插入,移動,或移除時(shí)應用(yòng)動畫(huà)。我們将在下(xià)一章節中介紹。

除了(le)這(zhè)兩個組件,我們也(yě)可以通過其他(tā)技術手段來(lái)應用(yòng)動畫(huà),比如切換 CSS class 或用(yòng)狀态綁定樣式來(lái)驅動動畫(huà)。這(zhè)些(xiē)其他(tā)的方法會(huì)在動畫(huà)技巧章節中展開(kāi)。

<Transition> 組件 ​

<Transition> 是一個内置組件,這(zhè)意味着它在任意别的組件中都可以被使用(yòng),無需注冊。它可以将進入和(hé)離開(kāi)動畫(huà)應用(yòng)到(dào)通過默認插槽傳遞給它的元素或組件上(shàng)。進入或離開(kāi)可以由以下(xià)的條件之一觸發:

由 v-if 所觸發的切換

由 v-show 所觸發的切換

由特殊元素 <component> 切換的動态組件

改變特殊的 key 屬性

以下(xià)是最基本用(yòng)法的示例:

template

<button @click="show = !show">Toggle</button>

<Transition>

<p v-if="show">hello</p>

</Transition>

css

/* 下(xià)面我們會(huì)解釋這(zhè)些(xiē) class 是做什(shén)麽的 */

.v-enter-active,

.v-leave-active {

transition: opacity 0.5s ease;

}

.v-enter-from,

.v-leave-to {

opacity: 0;

}

TIP

<Transition> 僅支持單個元素或組件作(zuò)爲其插槽内容。如果内容是一個組件,這(zhè)個組件必須僅有一個根元素。

當一個 <Transition> 組件中的元素被插入或移除時(shí),會(huì)發生下(xià)面這(zhè)些(xiē)事(shì)情:

Vue 會(huì)自(zì)動檢測目标元素是否應用(yòng)了(le) CSS 過渡或動畫(huà)。如果是,則一些(xiē) CSS 過渡 class 會(huì)在适當的時(shí)機被添加和(hé)移除。

如果有作(zuò)爲監聽器的 JavaScript 鈎子,這(zhè)些(xiē)鈎子函數會(huì)在适當時(shí)機被調用(yòng)。

如果沒有探測到(dào) CSS 過渡或動畫(huà)、也(yě)沒有提供 JavaScript 鈎子,那麽 DOM 的插入、删除操作(zuò)将在浏覽器的下(xià)一個動畫(huà)幀後執行。

基于 CSS 的過渡效果 ​

CSS 過渡 class ​

一共有 6 個應用(yòng)于進入與離開(kāi)過渡效果的 CSS class。

v-enter-from:進入動畫(huà)的起始狀态。在元素插入之前添加,在元素插入完成後的下(xià)一幀移除。

v-enter-active:進入動畫(huà)的生效狀态。應用(yòng)于整個進入動畫(huà)階段。在元素被插入之前添加,在過渡或動畫(huà)完成之後移除。這(zhè)個 class 可以被用(yòng)來(lái)定義進入動畫(huà)的持續時(shí)間、延遲與速度曲線類型。

v-enter-to:進入動畫(huà)的結束狀态。在元素插入完成後的下(xià)一幀被添加 (也(yě)就是 v-enter-from 被移除的同時(shí)),在過渡或動畫(huà)完成之後移除。

v-leave-from:離開(kāi)動畫(huà)的起始狀态。在離開(kāi)過渡效果被觸發時(shí)立即添加,在一幀後被移除。

v-leave-active:離開(kāi)動畫(huà)的生效狀态。應用(yòng)于整個離開(kāi)動畫(huà)階段。在離開(kāi)過渡效果被觸發時(shí)立即添加,在過渡或動畫(huà)完成之後移除。這(zhè)個 class 可以被用(yòng)來(lái)定義離開(kāi)動畫(huà)的持續時(shí)間、延遲與速度曲線類型。

v-leave-to:離開(kāi)動畫(huà)的結束狀态。在一個離開(kāi)動畫(huà)被觸發後的下(xià)一幀被添加 (也(yě)就是 v-leave-from 被移除的同時(shí)),在過渡或動畫(huà)完成之後移除。

v-enter-active 和(hé) v-leave-active 給我們提供了(le)爲進入和(hé)離開(kāi)動畫(huà)指定不同速度曲線的能(néng)力,我們将在下(xià)面的小(xiǎo)節中看(kàn)到(dào)一個示例。

爲過渡效果命名 ​

我們可以給 <Transition> 組件傳一個 name prop 來(lái)聲明(míng)一個過渡效果名:

template

<Transition name="fade">

...

</Transition>

對(duì)于一個有名字的過渡效果,對(duì)它起作(zuò)用(yòng)的過渡 class 會(huì)以其名字而不是 v 作(zuò)爲前綴。比如,上(shàng)方例子中被應用(yòng)的 class 将會(huì)是 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> 一般都會(huì)搭配原生 CSS 過渡一起使用(yòng),正如你(nǐ)在上(shàng)面的例子中所看(kàn)到(dào)的那樣。這(zhè)個 transition CSS 屬性是一個簡寫形式,使我們可以一次定義一個過渡的各個方面,包括需要執行動畫(huà)的屬性、持續時(shí)間和(hé)速度曲線。

下(xià)面是一個更高(gāo)級的例子,它使用(yòng)了(le)不同的持續時(shí)間和(hé)速度曲線來(lái)過渡多個屬性:

template

<Transition name="slide-fade">

<p v-if="show">hello</p>

</Transition>

css

/*

進入和(hé)離開(kāi)動畫(huà)可以使用(yòng)不同

持續時(shí)間和(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 的應用(yòng)方式基本上(shàng)是相同的,隻有一點不同,那就是 *-enter-from 不是在元素插入後立即移除,而是在一個 animationend 事(shì)件觸發時(shí)被移除。

對(duì)于大(dà)多數的 CSS 動畫(huà),我們可以簡單地在 *-enter-active 和(hé) *-leave-active class 下(xià)聲明(míng)它們。下(xià)面是一個示例:

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 ​

你(nǐ)也(yě)可以向 <Transition> 傳遞以下(xià)的 props 來(lái)指定自(zì)定義的過渡 class:

enter-from-class

enter-active-class

enter-to-class

leave-from-class

leave-active-class

leave-to-class

你(nǐ)傳入的這(zhè)些(xiē) class 會(huì)覆蓋相應階段的默認 class 名。這(zhè)個功能(néng)在你(nǐ)想要在 Vue 的動畫(huà)機制下(xià)集成其他(tā)的第三方 CSS 動畫(huà)庫時(shí)非常有用(yòng),比如 Animate.css:

template

<!-- 假設你(nǐ)已經在頁面中引入了(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í)使用(yòng) transition 和(hé) animation ​

Vue 需要附加事(shì)件監聽器,以便知(zhī)道(dào)過渡何時(shí)結束。可以是 transitionend 或 animationend,這(zhè)取決于你(nǐ)所應用(yòng)的 CSS 規則。如果你(nǐ)僅僅使用(yòng)二者的其中之一,Vue 可以自(zì)動探測到(dào)正确的類型。

然而在某些(xiē)場景中,你(nǐ)或許想要在同一個元素上(shàng)同時(shí)使用(yòng)它們兩個。舉例來(lái)說,Vue 觸發了(le)一個 CSS 動畫(huà),同時(shí)鼠标懸停觸發另一個 CSS 過渡。此時(shí)你(nǐ)需要顯式地傳入 type prop 來(lái)聲明(míng),告訴 Vue 需要關心哪種類型,傳入的值是 animation 或 transition:

template

<Transition type="animation">...</Transition>

深層級過渡與顯式過渡時(shí)長 ​

盡管過渡 class 僅能(néng)應用(yòng)在 <Transition> 的直接子元素上(shàng),我們還是可以使用(yòng)深層級的 CSS 選擇器,在深層級的元素上(shàng)觸發過渡效果。

template

<Transition name="nested">

<div v-if="show" class="outer">

<div class="inner">

Hello

</div>

</div>

</Transition>

css

/* 應用(yòng)于嵌套元素的規則 */

.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ā)必要的 CSS */

我們甚至可以在深層元素上(shàng)添加一個過渡延遲,從(cóng)而創建一個帶漸進延遲的動畫(huà)序列:

css

/* 延遲嵌套元素的進入以獲得交錯效果 */

.nested-enter-active .inner {

transition-delay: 0.25s;

}

然而,這(zhè)會(huì)帶來(lái)一個小(xiǎo)問題。默認情況下(xià),<Transition> 組件會(huì)通過監聽過渡根元素上(shàng)的第一個 transitionend 或者 animationend 事(shì)件來(lái)嘗試自(zì)動判斷過渡何時(shí)結束。而在嵌套的過渡中,期望的行爲應該是等待所有内部元素的過渡完成。

在這(zhè)種情況下(xià),你(nǐ)可以通過向 <Transition> 組件傳入 duration prop 來(lái)顯式指定過渡的持續時(shí)間 (以毫秒爲單位)。總持續時(shí)間應該匹配延遲加上(shàng)内部元素的過渡持續時(shí)間:

template

<Transition :duration="550">...</Transition>

如果有必要的話(huà),你(nǐ)也(yě)可以用(yòng)對(duì)象的形式傳入,分開(kāi)指定進入和(hé)離開(kāi)所需的時(shí)間:

template

<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>

性能(néng)考量 ​

你(nǐ)可能(néng)注意到(dào)我們上(shàng)面例子中展示的動畫(huà)所用(yòng)到(dào)的 CSS 屬性大(dà)多是 transform 和(hé) opacity 之類的。用(yòng)這(zhè)些(xiē)屬性制作(zuò)動畫(huà)非常高(gāo)效,因爲:

他(tā)們在動畫(huà)過程中不會(huì)影響到(dào) DOM 結構,因此不會(huì)每一幀都觸發昂貴的 CSS 布局重新計(jì)算(suàn)。

大(dà)多數的現(xiàn)代浏覽器都可以在執行 transform 動畫(huà)時(shí)利用(yòng) GPU 進行硬件加速。

相比之下(xià),像 height 或者 margin 這(zhè)樣的屬性會(huì)觸發 CSS 布局變動,因此執行它們的動畫(huà)效果更昂貴,需要謹慎使用(yòng)。我們可以在 CSS-Triggers 這(zhè)類的網站(zhàn)查詢哪些(xiē)屬性會(huì)在執行動畫(huà)時(shí)觸發 CSS 布局變動。

JavaScript 鈎子 ​

你(nǐ)可以通過監聽 <Transition> 組件事(shì)件的方式在過渡過程中挂上(shàng)鈎子函數:

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: {

// 在元素被插入到(dào) DOM 之前被調用(yòng)

// 用(yòng)這(zhè)個來(lái)設置元素的 "enter-from" 狀态

onBeforeEnter(el) {},

// 在元素被插入到(dào) DOM 之後的下(xià)一幀被調用(yòng)

// 用(yòng)這(zhè)個來(lái)開(kāi)始進入動畫(huà)

onEnter(el, done) {

// 調用(yòng)回調函數 done 表示過渡結束

// 如果與 CSS 結合使用(yòng),則這(zhè)個回調是可選參數

done()

},

// 當進入過渡完成時(shí)調用(yòng)。

onAfterEnter(el) {},

onEnterCancelled(el) {},

// 在 leave 鈎子之前調用(yòng)

// 大(dà)多數時(shí)候,你(nǐ)應該隻會(huì)用(yòng)到(dào) leave 鈎子

onBeforeLeave(el) {},

// 在離開(kāi)過渡開(kāi)始時(shí)調用(yòng)

// 用(yòng)這(zhè)個來(lái)開(kāi)始離開(kāi)動畫(huà)

onLeave(el, done) {

// 調用(yòng)回調函數 done 表示過渡結束

// 如果與 CSS 結合使用(yòng),則這(zhè)個回調是可選參數

done()

},

// 在離開(kāi)過渡完成、

// 且元素已從(cóng) DOM 中移除時(shí)調用(yòng)

onAfterLeave(el) {},

// 僅在 v-show 過渡中可用(yòng)

onLeaveCancelled(el) {}

}

}

這(zhè)些(xiē)鈎子可以與 CSS 過渡或動畫(huà)結合使用(yòng),也(yě)可以單獨使用(yòng)。

在使用(yòng)僅由 JavaScript 執行的動畫(huà)時(shí),最好(hǎo)是添加一個 :css="false" prop。這(zhè)顯式地向 Vue 表明(míng)可以跳過對(duì) CSS 過渡的自(zì)動探測。除了(le)性能(néng)稍好(hǎo)一些(xiē)之外(wài),還可以防止 CSS 規則意外(wài)地幹擾過渡效果。

template

<Transition

...

:css="false"

>

...

</Transition>

在有了(le) :css="false" 後,我們就自(zì)己全權負責控制什(shén)麽時(shí)候過渡結束了(le)。這(zhè)種情況下(xià)對(duì)于 @enter 和(hé) @leave 鈎子來(lái)說,回調函數 done 就是必須的。否則,鈎子将被同步調用(yòng),過渡将立即完成。

這(zhè)裏是使用(yòng) GreenSock 庫執行動畫(huà)的一個示例,你(nǐ)也(yě)可以使用(yòng)任何你(nǐ)想要的庫,比如 Anime.js 或者 Motion One。

可複用(yòng)過渡效果 ​

得益于 Vue 的組件系統,過渡效果是可以被封裝複用(yòng)的。要創建一個可被複用(yòng)的過渡,我們需要爲 <Transition> 組件創建一個包裝組件,并向内傳入插槽内容:

vue

<!-- MyTransition.vue -->

<script>

// JavaScript 鈎子邏輯...

</script>

<template>

<!-- 包裝内置的 Transition 組件 -->

<Transition

name="my-transition"

@enter="onEnter"

@leave="onLeave">

<slot></slot> <!-- 向内傳遞插槽内容 -->

</Transition>

</template>

<style>

/*

必要的 CSS...

注意:避免在這(zhè)裏使用(yòng) <style scoped>

因爲那不會(huì)應用(yòng)到(dào)插槽内容上(shàng)

*/

</style>

現(xiàn)在 MyTransition 可以在導入後像内置組件那樣使用(yòng)了(le):

template

<MyTransition>

<div v-if="show">Hello</div>

</MyTransition>

出現(xiàn)時(shí)過渡 ​

如果你(nǐ)想在某個節點初次渲染時(shí)應用(yòng)一個過渡效果,你(nǐ)可以添加 appear prop:

template

<Transition appear>

...

</Transition>

元素間過渡 ​

除了(le)通過 v-if / v-show 切換一個元素,我們也(yě)可以通過 v-if / v-else / v-else-if 在幾個組件間進行切換,隻要确保任一時(shí)刻隻會(huì)有一個元素被渲染即可:

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)的元素都是在同時(shí)開(kāi)始動畫(huà)的,因此我們不得不将它們設爲 position: absolute 以避免二者同時(shí)存在時(shí)出現(xiàn)的布局問題。

然而,很(hěn)多情況下(xià)這(zhè)可能(néng)并不符合需求。我們可能(néng)想要先執行離開(kāi)動畫(huà),然後在其完成之後再執行元素的進入動畫(huà)。手動編排這(zhè)樣的動畫(huà)是非常複雜(zá)的,好(hǎo)在我們可以通過向 <Transition> 傳入一個 mode prop 來(lái)實現(xiàn)這(zhè)個行爲:

template

<Transition mode="out-in">

...

</Transition>

将之前的例子改爲 mode="out-in" 後是這(zhè)樣:

Click to cycle through states:

Edit

<Transition> 也(yě)支持 mode="in-out",雖然這(zhè)并不常用(yòng)。

組件間過渡 ​

<Transition> 也(yě)可以作(zuò)用(yòng)于動态組件之間的切換:

template

<Transition name="fade" mode="out-in">

<component :is="activeComponent"></component>

</Transition>

<Transition> 的 props (比如 name) 也(yě)可以是動态的!這(zhè)讓我們可以根據狀态變化動态地應用(yòng)不同類型的過渡:

template

<Transition :name="transitionName">

<!-- ... -->

</Transition>

這(zhè)個特性的用(yòng)處是可以提前定義好(hǎo)多組 CSS 過渡或動畫(huà)的 class,然後在它們之間動态切換。

你(nǐ)也(yě)可以根據你(nǐ)的組件的當前狀态在 JavaScript 過渡鈎子中應用(yòng)不同的行爲。最後,創建動态過渡的終極方式還是創建可複用(yòng)的過渡組件,并讓這(zhè)些(xiē)組件根據動态的 props 來(lái)改變過渡的效果。掌握了(le)這(zhè)些(xiē)技巧後,就真的隻有你(nǐ)想不到(dào),沒有做不到(dào)的了(le)。

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:TransitionGroup
上(shàng)一篇:插件