過渡
Vue提供兩個內建元件,可以幫助處理響應狀態變化時的過渡和動畫
<Transition>
在元素或元件進入和離開DOM時應用動畫。這在本頁上有介紹。<TransitionGroup>
在元素或元件被插入到、從v-for
列表中移除或在其內部移動時應用動畫。這將在下一章中介紹。
除了這兩個元件之外,我們還可以使用其他技術,如切換CSS類或透過樣式繫結實現狀態驅動的動畫,在Vue中應用動畫。這些額外的技術將在動畫技巧章節中介紹。
<Transition> 元件
<Transition>
是一個內建元件:這意味著它可以在任何元件的模板中使用,而無需註冊。它可以用於對其預設插槽傳入的元素或元件應用進入和離開動畫。進入或離開可以由以下之一觸發
- 透過
v-if
進行條件渲染 - 透過
v-show
進行條件顯示 - 透過
<component>
特殊元素動態切換元件 - 更改特殊的
key
屬性
這是一個最基本使用示例
模板
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
CSS
/* we will explain what these classes do next! */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
hello
提示
<Transition>
只支援單個元素或元件作為其插槽內容。如果內容是元件,該元件也必須只有一個根元素。
當 <Transition>
元件中的元素被插入或移除時,會發生以下情況
Vue會自動檢查目標元素是否應用了CSS過渡或動畫。如果應用了,將新增/移除一系列CSS過渡類,時機適當。
如果有監聽器監聽JavaScript鉤子,則這些鉤子將在適當的時機被呼叫。
如果沒有檢測到CSS過渡/動畫,並且沒有提供JavaScript鉤子,則插入和/或刪除的DOM操作將在瀏覽器的下一個動畫幀上執行。
基於CSS的過渡
過渡類
共有六個類用於進入/離開過渡。
v-enter-from
:進入的起始狀態。在元素插入前新增,元素插入後一幀移除。v-enter-active
:進入的活躍狀態。在整個進入階段應用。在元素插入前新增,在過渡/動畫完成後移除。這個類可以用來定義進入過渡的持續時間、延遲和緩動曲線。v-enter-to
:進入的結束狀態。在元素插入後一幀新增(同時移除v-enter-from
),在過渡/動畫完成後移除。v-leave-from
:離開的起始狀態。在離開過渡觸發時立即新增,一幀後移除。v-leave-active
:離開的活躍狀態。在整個離開階段應用。在離開過渡觸發時立即新增,在過渡/動畫完成後移除。這個類可以用來定義離開過渡的持續時間、延遲和緩動曲線。v-leave-to
:離開的結束狀態。在離開過渡觸發後一幀新增(同時移除v-leave-from
),在過渡/動畫完成後移除。
v-enter-active
和v-leave-active
允許我們為進入/離開過渡指定不同的緩動曲線,以下章節將給出一個示例。
命名過渡
可以透過name
屬性命名一個過渡。
模板
<Transition name="fade">
...
</Transition>
對於命名過渡,其過渡類將以其名稱為字首而不是v
。例如,上述過渡應用的是fade-enter-active
而不是v-enter-active
。fade過渡的CSS應如下所示
CSS
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
CSS過渡
<Transition>
通常與原生CSS過渡結合使用,如上述基本示例所示。CSS的transition
屬性是一個簡寫,允許我們指定過渡的多個方面,包括應動畫化的屬性、過渡的持續時間以及緩動曲線。
以下是一個更高階的示例,它轉換多個屬性,為進入和離開指定不同的持續時間緩動曲線。
模板
<Transition name="slide-fade">
<p v-if="show">hello</p>
</Transition>
CSS
/*
Enter and leave animations can use different
durations and timing functions.
*/
.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;
}
hello
CSS動畫
原生CSS動畫的應用方式與CSS過渡相同,區別在於*-enter-from
不是在元素插入後立即移除,而是在animationend
事件發生時移除。
對於大多數CSS動畫,我們可以在*-enter-active
和*-leave-active
類下簡單宣告它們。以下是一個示例
模板
<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);
}
}
你好,這裡有一些彈跳文字!
自定義過渡類
您還可以透過向<Transition>
傳遞以下屬性來指定自定義過渡類
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
這些將覆蓋常規類名。當您想將Vue的過渡系統與現有的CSS動畫庫(如Animate.css)結合使用時,這特別有用。
模板
<!-- assuming Animate.css is included on the page -->
<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>
同時使用過渡和動畫
Vue需要附加事件監聽器以瞭解何時過渡結束。它可以是被應用的CSS規則的型別決定的transitionend
或animationend
。如果您只使用其中之一,Vue可以自動檢測正確的型別。
但是,在某些情況下,您可能希望在同一個元素上同時使用這兩種效果,例如,當Vue觸發CSS動畫時,在懸停時新增CSS過渡效果。在這些情況下,您必須透過傳遞具有值為animation
或transition
的type
屬性來顯式宣告Vue應該關注的型別。
模板
<Transition type="animation">...</Transition>
巢狀過渡和顯式過渡持續時間
儘管過渡類只應用於<Transition>
中的直接子元素,但我們可以使用巢狀CSS選擇器來過渡巢狀元素
模板
<Transition name="nested">
<div v-if="show" class="outer">
<div class="inner">
Hello
</div>
</div>
</Transition>
CSS
/* rules that target nested elements */
.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;
}
/* ... other necessary CSS omitted */
我們甚至可以在進入時為巢狀元素新增過渡延遲,這會建立一個交錯進入動畫序列
CSS
/* delay enter of nested element for staggered effect */
.nested-enter-active .inner {
transition-delay: 0.25s;
}
然而,這會引發一個小問題。預設情況下,<Transition>
元件嘗試透過監聽根過渡元素的第一個transitionend
或animationend
事件來自動確定何時過渡完成。對於巢狀過渡,所需的操作應該是等待所有內部元素的過渡都完成。
在這種情況下,您可以使用<transition>
元件上的duration
屬性(以毫秒為單位)指定顯式過渡持續時間。總持續時間應等於延遲加上內部元素的過渡持續時間
模板
<Transition :duration="550">...</Transition>
你好
如果需要,您還可以使用物件指定進入和離開的持續時間
模板
<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>
效能考慮因素
您可能已經注意到上述動畫大多使用transform
和opacity
等屬性。這些屬性易於動畫化,因為
它們在動畫期間不影響文件佈局,因此它們不會在每一幀上觸發昂貴的CSS佈局計算。
大多數現代瀏覽器在動畫
transform
時可以利用GPU硬體加速。
相比之下,height
或margin
等屬性將觸發CSS佈局,因此它們動畫化起來成本更高,應謹慎使用。
JavaScript鉤子
您可以透過監聽<Transition>
元件上的事件來使用JavaScript掛鉤到過渡過程
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
// called before the element is inserted into the DOM.
// use this to set the "enter-from" state of the element
function onBeforeEnter(el) {}
// called one frame after the element is inserted.
// use this to start the entering animation.
function onEnter(el, done) {
// call the done callback to indicate transition end
// optional if used in combination with CSS
done()
}
// called when the enter transition has finished.
function onAfterEnter(el) {}
// called when the enter transition is cancelled before completion.
function onEnterCancelled(el) {}
// called before the leave hook.
// Most of the time, you should just use the leave hook
function onBeforeLeave(el) {}
// called when the leave transition starts.
// use this to start the leaving animation.
function onLeave(el, done) {
// call the done callback to indicate transition end
// optional if used in combination with CSS
done()
}
// called when the leave transition has finished and the
// element has been removed from the DOM.
function onAfterLeave(el) {}
// only available with v-show transitions
function onLeaveCancelled(el) {}
這些鉤子可以與CSS過渡/動畫結合使用,或者單獨使用。
在使用僅JavaScript的過渡時,通常新增:css="false"
屬性是個好主意。這會明確告訴Vue跳過自動CSS過渡檢測。除了效能略高外,這還可以防止CSS規則意外干擾過渡。
模板
<Transition
...
:css="false"
>
...
</Transition>
使用:css="false"
時,我們也完全負責控制過渡何時結束。在這種情況下,對於@enter
和@leave
鉤子,需要提供done
回撥。否則,鉤子將同步呼叫,並且過渡將立即完成。
以下是一個使用GSAP庫執行動畫的示例。當然,您可以使用任何其他動畫庫,例如Anime.js或Motion One
可重用過渡
可以透過Vue的元件系統重用過渡。要建立可重用的過渡,我們可以建立一個元件,該元件包裝了<Transition>
元件,並將槽內容傳遞下去。
vue
<!-- MyTransition.vue -->
<script>
// JavaScript hooks logic...
</script>
<template>
<!-- wrap the built-in Transition component -->
<Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- pass down slot content -->
</Transition>
</template>
<style>
/*
Necessary CSS...
Note: avoid using <style scoped> here since it
does not apply to slot content.
*/
</style>
現在MyTransition
可以像內建版本一樣匯入和使用。
模板
<MyTransition>
<div v-if="show">Hello</div>
</MyTransition>
出現在過渡
如果您還希望在節點的初始渲染時應用過渡,可以新增appear
屬性。
模板
<Transition appear>
...
</Transition>
元素間的過渡
除了使用v-if
/ v-show
切換元素外,我們還可以使用v-if
/ v-else
/ v-else-if
在兩個元素之間進行過渡,只要我們確保在任何給定時刻只顯示一個元素。
模板
<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>
點選迴圈狀態
過渡模式
在上一個示例中,進入和離開元素同時進行動畫,我們必須將它們設定為position: absolute
以避免在DOM中同時存在兩個元素時的佈局問題。
但是,在某些情況下這不是一個選項,或者根本不是我們想要的操作。我們可能希望先對離開元素進行動畫,然後只有在離開動畫完成後才將進入元素插入。手動編排這樣的動畫將非常複雜——幸運的是,我們可以透過傳遞一個mode
屬性給<Transition>
來啟用這種行為。
模板
<Transition mode="out-in">
...
</Transition>
以下是之前示例中帶有mode="out-in"
的演示。
點選迴圈狀態
<Transition>
還支援mode="in-out"
,儘管它使用得較少。
元件間的過渡
<Transition>
也可以用於動態元件周圍。
模板
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
元件A
動態過渡
<Transition>
的name
等屬性也可以是動態的!這允許我們根據狀態變化動態應用不同的過渡。
模板
<Transition :name="transitionName">
<!-- ... -->
</Transition>
當您使用Vue的過渡類約定定義了CSS過渡/動畫,並希望在這些過渡之間切換時,這可能會很有用。
您還可以根據元件的當前狀態,在JavaScript過渡鉤子中應用不同的行為。最後,建立動態過渡的終極方法是使用可複用過渡元件,這些元件接受props來改變要使用的過渡的性質。這可能聽起來有點陳詞濫調,但真正的限制實際上只是您的想象力。
具有鍵屬性(Key Attribute)的過渡
有時您需要強制重新渲染DOM元素,以便過渡發生。
以這個計數器元件為例
vue
<script setup>
import { ref } from 'vue';
const count = ref(0);
setInterval(() => count.value++, 1000);
</script>
<template>
<Transition>
<span :key="count">{{ count }}</span>
</Transition>
</template>
如果我們排除了key
屬性,則只會更新文字節點,因此不會發生過渡。然而,當存在key
屬性時,Vue知道每當count
發生變化時,都會建立一個新的span
元素,因此Transition
元件有兩個不同的元素可以過渡。
相關