Skip to content

用 Transition 做進出場動畫

重新認識 Vue.js: 008 天絕對看不完的 Vue.js 3.x 指南

到目前為止,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: 0fade-enter-from)過渡到正常;退場從正常過渡到 opacity: 0fade-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 組裝,看看我們該在哪些地方把關。