Appearance
用 Transition 做進出場動畫
到目前為止,v-if 的顯示隱藏、:is 的元件切換,都是瞬間完成的「硬切」。<Transition> 讓元素的進場與退場可以套用 CSS 動畫,而且不用改任何邏輯,只要包一層。
<Transition> 基本用法
把會進出畫面的單一元素(或元件)包進 <Transition>,並給它一個 name:
src/App.vue:
vue
<script setup>
import { ref } from 'vue'
const added = ref(false)
</script>
<template>
<main class="app">
<button type="button" @click="added = !added">
{{ added ? '從購物車移除' : '加入購物車' }}
</button>
<Transition name="fade">
<p v-if="added" class="notice">已加入購物車!</p>
</Transition>
</main>
</template>
<style scoped>
.app {
padding: 2rem;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>按下按鈕,提示文字淡入;再按一次,淡出後才從 DOM 移除。邏輯完全沒變,還是那個 v-if;<Transition> 做的事,是在進出場的過程中幫元素掛上約定好的 CSS class,動畫本身由我們的 CSS 決定。
CSS class 的約定
<Transition name="fade"> 在進出場過程會依序掛上六個 class,名稱以 name 為前綴:
| Class | 掛上的時機 |
|---|---|
fade-enter-from | 進場的起點(插入前的樣子),下一影格移除 |
fade-enter-active | 整個進場過程,transition 屬性通常寫這裡 |
fade-enter-to | 進場的終點(完成後的樣子) |
fade-leave-from | 退場的起點 |
fade-leave-active | 整個退場過程 |
fade-leave-to | 退場的終點,動畫結束後元素才移除 |
所以剛才的範例翻譯過來就是:進場從 opacity: 0(fade-enter-from)過渡到正常;退場從正常過渡到 opacity: 0(fade-leave-to);過渡花 0.3 秒(兩個 -active)。Vue 負責在對的時機掛 class 與延後移除元素,動畫的長相全部寫在 CSS,這就是 <Transition> 的契約。
它也能包住上一節學過的東西:<Transition> 裡放 <component :is>,元件切換就有進出場;搭配 mode="out-in"(舊的先退場、新的再進場)可以避免兩個元件短暫並存的跳動。要注意 <Transition> 只接受單一子元素或元件。
<TransitionGroup>:列表的進出場
列表的新增與移除,用 <TransitionGroup>。它是 <Transition> 的列表版:允許多個子元素,每個子元素都套用同一組 class 約定:
vue
<template>
<TransitionGroup name="fade" tag="ul">
<li v-for="book in books" :key="book.id">{{ book.title }}</li>
</TransitionGroup>
</template>tag="ul" 指定它渲染成 <ul>。這裡有一個比平常更嚴格的要求:每個子元素都必須有穩定且唯一的 key。第二章講過 key 是 Vue 追蹤列表項目的身分證;對 <TransitionGroup> 來說,沒有正確的 key,Vue 就分不清「哪一筆是新增、哪一筆是移除」,動畫會套在錯的項目上。用陣列 index 當 key 在這裡幾乎保證出錯。
<TransitionGroup> 還能替「移動中」的項目掛 fade-move class 做排序動畫,屬於進階用法,需要時再查官方文件即可。
進出場動畫補完,本章的元件組合工具就全部到齊了。最後一節換一個角度收尾:把這些工具交給 AI 組裝,看看我們該在哪些地方把關。
