2024-10-17|閱讀時間 ‧ 約 0 分鐘

EP40 - 過渡轉換

Transition,官方文件介紹六個內建的組件,
在實用範例裡頭有出現過摟,做動畫的時候需要他~
不知道還有沒有其他的應用?

Vue 提供了兩個內建組件,可以幫助在狀態變更時進行過渡和動畫處理:

  • <Transition>:用於在元素或組件進入和離開 DOM 時應用動畫。這部分內容在本頁面中涵蓋。
  • <TransitionGroup>:用於在元素或組件插入、移除或在 v-for 列表中移動時應用動畫。這部分內容在下一篇EP中涵蓋。

除了這兩個組件,我們還可以使用其他技術在 Vue 中應用動畫,例如切換 CSS 類或通過樣式綁定進行狀態驅動的動畫。這些額外技術在《動畫技術》章節中進行說明。

<Transition> 組件

<Transition> 是一個內建組件:這意味著它可以在任何組件的模板中使用,而無需註冊。它可以用於在傳遞給它的元素或組件上應用進入和離開的動畫,這些元素或組件通過其預設插槽傳遞。進入或離開的觸發可以通過以下方式之一:

  • 使用 v-if 進行條件渲染
  • 使用 v-show 進行條件顯示
  • 通過 <component> 特殊元素進行動態組件切換
  • 更改特殊的 key 屬性

以下是最基本用法的示例:

<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
/* 我們將在接下來解釋這些類的作用! */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
opacity: 0;
}

Try it in the playground

提示

<Transition> 只支持單個元素或組件作為其插槽內容。如果內容是一個組件,則該組件也必須只有一個根元素。

<Transition> 組件中的元素被插入或移除時,會發生以下情況:

  1. Vue 會自動檢測目標元素是否應用了 CSS 過渡或動畫。如果有,將在適當的時間添加或移除多個 CSS 過渡類
  2. 如果有 JavaScript 鉤子的監聽器,這些鉤子將在適當的時間被調用。
  3. 如果沒有檢測到 CSS 過渡/動畫且未提供 JavaScript 鉤子,則插入和/或移除的 DOM 操作將在瀏覽器的下一個動畫幀上執行。

基於 CSS 的過渡 - CSS-Based Transitions​

過渡類別 - Transition Classes​

有六個類別用於進入/離開過渡。


  • 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-activev-leave-active 使我們能夠為進入/離開過渡指定不同的緩動曲線,接下來的部分將會提供範例。

命名過渡效果 - Named Transitions​

可以通過 name 屬性來給過渡效果命名:

<template>
<Transition name="fade">
...
</Transition>
</template>

對於命名過渡效果,它的過渡類名將以指定的名稱為前綴,而不是 v。例如,對於上述過渡效果,應用的類名將是 fade-enter-active 而不是 v-enter-activefade 過渡效果的 CSS 應如下所示:

.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

這樣設置後,在元素進入和離開時會有漸隱漸現的效果。

CSS 過渡效果 - CSS Transitions​

<Transition> 元件最常與原生的 CSS 過渡效果結合使用,如上面的基本範例所示。transition CSS 屬性是一個簡寫,允許我們指定過渡效果的多個方面,包括需要動畫化的屬性、過渡的持續時間和緩動曲線。

這裡是一個更進階的範例,過渡多個屬性,並為進入和離開設置不同的持續時間和緩動曲線:

<template>
<Transition name="slide-fade">
<p v-if="show">hello</p>
</Transition>
</template>
/*
進入和離開動畫可以使用不同的持續時間和時間函數。
*/
.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;
}

在這個範例中,當元素進入時,它會在 0.3 秒內使用 ease-out 緩動曲線從右側滑入並逐漸顯現;而當元素離開時,它會在 0.8 秒內使用自定義的 cubic-bezier 緩動曲線滑出並逐漸隱藏。這樣的設置使得過渡效果更加豐富和細緻。

Try it in the playground

CSS 動畫 - CSS Animations​

原生 CSS 動畫的應用方式與 CSS 過渡效果相同,不同之處在於 *-enter-from 不會在元素插入後立即被移除,而是在 animationend 事件後才移除。

對於大多數 CSS 動畫,我們可以簡單地將它們聲明在 *-enter-active*-leave-active 類中。以下是一個範例:

<template>
<Transition name="bounce">
<p v-if="show" style="text-align: center;">
Hello here is some bouncy text!
</p>
</Transition>
</template>
.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);
}
}

在這個範例中,當元素進入時會執行 bounce-in 動畫,動畫持續 0.5 秒;當元素離開時,則執行相同動畫的反向過程。這使得文本在顯示和隱藏時有彈跳的效果。

Try it in the playground

自訂過渡類名 - Custom Transition Classes​

你也可以通過向 <Transition> 傳遞以下屬性來指定自訂過渡類名:

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

這些屬性會覆蓋傳統的類名。這在你希望將 Vue 的過渡系統與現有的 CSS 動畫庫(如 Animate.css)結合使用時特別有用:

<template>
<!-- 假設 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>
</template>

在這個範例中,當元素進入時會應用 animate__tada 動畫效果,而當元素離開時會應用 animate__bounceOutRight 動畫效果。這種方法使你可以靈活地使用任何 CSS 動畫庫來增強過渡效果。

Try it in the playground

同時使用過渡和動畫 - Using Transitions and Animations Together​

Vue 需要附加事件監聽器來判斷過渡何時結束。這可能是 transitionendanimationend,取決於應用的 CSS 規則類型。如果你只使用其中之一,Vue 可以自動檢測正確的類型。

但是,有時你可能希望在同一個元素上同時使用兩者,例如,通過 Vue 觸發的 CSS 動畫,與懸停時的 CSS 過渡效果。在這些情況下,你需要通過 type 屬性明確指定 Vue 應關注的類型,值為 animationtransition

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

嵌套過渡和明確的過渡持續時間

雖然過渡類名只應用於 <Transition> 的直接子元素,但我們可以使用嵌套的 CSS 選擇器來過渡嵌套元素:

<template>
<Transition name="nested">
<div v-if="show" class="outer">
<div class="inner">
Hello
</div>
</div>
</Transition>
</template>
/* 針對嵌套元素的規則 */
.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;
}

/* 延遲嵌套元素進入以實現分段效果 */
.nested-enter-active .inner {
transition-delay: 0.25s;
}

不過,這會帶來一個小問題。默認情況下,<Transition> 組件嘗試通過監聽根過渡元素上的第一個 transitionendanimationend 事件來自動判斷過渡何時完成。對於嵌套過渡,理想行為應該是等待所有內部元素的過渡完成。

在這種情況下,你可以在 <Transition> 組件上使用 duration 屬性來指定明確的過渡持續時間(以毫秒為單位)。總持續時間應與嵌套元素的延遲加過渡持續時間相匹配:

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

Try it in the playground

如果需要,你也可以使用物件為進入和離開的持續時間指定不同的值:

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

性能考量 - Performance Considerations

你可能會注意到上面顯示的動畫主要使用了像 transformopacity 這樣的屬性。這些屬性之所以在動畫中高效,原因如下:

  • 在動畫過程中,它們不會影響文檔的佈局,因此不會在每個動畫幀上觸發昂貴的 CSS 佈局計算。
  • 大多數現代瀏覽器在動畫化 transform 時能利用 GPU 硬體加速。

相比之下,像 heightmargin 這樣的屬性會觸發 CSS 佈局,因此動畫化的成本更高,應謹慎使用。

JavaScript 鉤子 - JavaScript Hooks​

你可以透過在 <Transition> 組件上監聽事件來使用 JavaScript 鉤子來連接過渡過程:

<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<!-- ... -->
</Transition>

以下是各個鉤子的功能說明:

// 在元素插入 DOM 之前調用。
// 使用這個來設置元素的 "enter-from" 狀態
function onBeforeEnter(el) {}

// 在元素插入後的一幀調用。
// 使用這個來開始進入動畫。
function onEnter(el, done) {
// 調用 done 回調來指示過渡結束
// 如果與 CSS 一起使用則為可選
done()
}

// 當進入過渡完成時調用。
function onAfterEnter(el) {}

// 當進入過渡在完成之前被取消時調用。
function onEnterCancelled(el) {}

// 在離開鉤子之前調用。
// 大多數情況下,你應該只使用離開鉤子
function onBeforeLeave(el) {}

// 當離開過渡開始時調用。
// 使用這個來開始離開動畫。
function onLeave(el, done) {
// 調用 done 回調來指示過渡結束
// 如果與 CSS 一起使用則為可選
done()
}

// 當離開過渡完成並且
// 元素已被移除自 DOM 時調用。
function onAfterLeave(el) {}

// 只有在 v-show 過渡中可用
function onLeaveCancelled(el) {}

這些鉤子可以與 CSS 過渡/動畫結合使用,或者單獨使用。

在使用僅 JavaScript 的過渡時,通常建議添加 :css="false" 屬性。這明確告訴 Vue 跳過自動 CSS 過渡檢測。這樣不僅性能稍好,還可以防止 CSS 規則意外干擾過渡:

<template>
<Transition
...
:css="false"
>
...
</Transition>
</template>

使用 :css="false" 時,我們也完全負責控制過渡結束的時間。在這種情況下,done 回調對於 @enter@leave 鉤子是必需的。否則,鉤子將會同步調用,過渡將立即結束。

以下是一個使用 GSAP 庫來執行動畫的示例。當然,你也可以使用其他任何動畫庫,例如 Anime.jsMotion One

Try it in the playground

可重用的過渡 - Reusable Transitions​

過渡可以通過 Vue 的組件系統重用。要創建可重用的過渡,我們可以創建一個組件來包裝 <Transition> 組件並傳遞插槽內容:

<!-- MyTransition.vue -->
<script>
// JavaScript 鉤子邏輯...
</script>

<template>
<!-- 包裝內建的 Transition 組件 -->
<Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- 傳遞插槽內容 -->
</Transition>
</template>

<style>
/*
必要的 CSS...
注意:避免在這裡使用 <style scoped>,因為它
不適用於插槽內容。
*/
</style>

現在,MyTransition 可以像內建版本一樣導入和使用:

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

在出現時的過渡 - Transition on Appear​

如果您想在節點的初始渲染時應用過渡,可以添加 appear 屬性:

<template>
<Transition appear>
...
</Transition>
</template>

這樣可以確保在組件首次出現時也能應用過渡效果,增強用戶體驗。使用 appear 屬性,Vue 將會在元素首次插入到 DOM 中時觸發過渡,而不僅僅是在狀態變化時。

元素之間的過渡 - Transition Between Elements​

除了使用 v-ifv-show 切換元素外,我們還可以使用 v-ifv-elsev-else-if 之間進行過渡,只要確保在任何時刻只有一個元素被顯示即可:

<template>
<Transition>
<button v-if="docState === 'saved'">編輯</button>
<button v-else-if="docState === 'edited'">保存</button>
<button v-else-if="docState === 'editing'">取消</button>
</Transition>
</template>

這種方法允許我們在不同狀態之間進行平滑過渡,從而改善用戶界面的互動性和可用性。當用戶進行操作時,過渡效果能夠讓介面變得更流暢且具吸引力。

Try it in the playground

過渡模式 - Transition Modes​

在前面的示例中,進入和離開的元素同時被動畫化,因此我們必須將它們設置為 position: absolute,以避免在兩個元素同時存在於 DOM 中時出現佈局問題。

然而,在某些情況下,這並不是一個選項,或者根本不是我們希望的行為。我們可能希望離開的元素先被動畫化,而在離開動畫完成之前,不再插入進入的元素。手動協調這種動畫會非常複雜,幸運的是,我們可以通過將模式屬性傳遞給 <Transition> 來啟用此行為:

<template>
<Transition mode="out-in">
...
</Transition>
</template>

可以在上一個範例中,使用 mode="out-in"

此外,<Transition> 也支持 mode="in-out",儘管使用頻率較低。

元件之間的過渡 - Transition Between Components​

<Transition> 也可以用於包裹動態元件:

<template>
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
</template>

在這個例子中,<Transition> 被用來包裹一個動態元件,這樣在切換不同元件時就可以應用過渡效果。這使得用戶在不同元件之間的轉換時,體驗到更流暢的動畫效果。

這樣的用法特別適合需要頻繁切換的情境,例如在單頁應用(SPA)中切換不同的視圖或組件。利用 Vue 的過渡系統,可以輕鬆地實現動態過渡效果,提升用戶體驗。

Try it in the playground

動態過渡 - Dynamic Transitions​

<Transition> 的屬性,例如 name,也可以是動態的!這使我們能根據狀態變化動態應用不同的過渡效果:

<template>
<Transition :name="transitionName">
<!-- ... -->
</Transition>
</template>

這在你使用 Vue 的過渡類別約定定義了 CSS 過渡或動畫時非常有用,特別是當你想在它們之間切換時。

你也可以根據元件的當前狀態在 JavaScript 過渡鉤子中應用不同的行為。最終,創建動態過渡的最佳方式是通過可重用的過渡元件,這些元件接受屬性以改變所使用的過渡類型。雖然這聽起來可能有些老套,但實際上唯一的限制就是你的想像力。

帶有 key 屬性的過渡 - Transitions with the Key Attribute​

有時你需要強制重新渲染一個 DOM 元素,才能讓過渡發生。

以這個計數器元件為例:

<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 組件在兩個不同的元素之間進行過渡。

Try it in the playground

相關資料 <Transition> API reference

沒想到這內建的組件Transition資料這麼多 Orz....
越來越覺得前端的學好真的頗難 www
繼續努力~學習的過程真有趣
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.