本文 AI 產出,尚未審核

Vue3 Transition 與 Animate.css / GSAP 整合


簡介

在單頁應用程式 (SPA) 中,過渡與動畫不只是視覺上的點綴,更能提升使用者體驗、引導操作流程、降低認知負擔。Vue 3 內建的 <transition><transition-group> 組件讓開發者可以輕鬆掛載 CSS 或 JavaScript 動畫,而 Animate.cssGSAP 則是兩個在前端社群廣受好評的動畫庫:前者以簡潔的 CSS class 為主,適合快速套用;後者則提供強大的時間軸 (timeline) 與腳本控制,能完成更複雜的交互。

本篇文章將說明如何在 Vue 3 中把這兩個庫結合使用,從最簡單的 class 切換到進階的程式化動畫,並提供完整的程式碼範例、常見陷阱與最佳實踐,讓 初學者 能快速上手、中階開發者 能在專案中穩定運用。


核心概念

1. Vue 3 的過渡機制

Vue 3 透過 <transition>(單一元素)與 <transition-group>(列表)包裹目標元素,然後在元素進入或離開時自動加入/移除特定的 CSS class。預設的 class 名稱如下:

階段 加入的 class
進入開始 (enter) v-enter-fromv-enter-activev-enter-to
離開開始 (leave) v-leave-fromv-leave-activev-leave-to

開發者只要在 CSS 中定義這些 class 的樣式,就能完成動畫。若想使用自訂名稱,只需要在 <transition> 上加上 name="my",對應的 class 會變成 my-enter-*my-leave-*

技巧:在 Vue 3 中,<transition> 會自動偵測 v-showv-ifv-for 等條件式變化,無需額外的 JavaScript 監聽。


2. 整合 Animate.css

2.1 為什麼選擇 Animate.css?

  • 即插即用:只需要在 HTML 元素上加上預設的 class(如 animate__bounceIn)即可。
  • 輕量:整個庫只有約 10KB(gzip 後),適合不需要太複雜動畫的專案。

2.2 基本使用方式

  1. 安裝與匯入
npm install animate.css --save
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import 'animate.css'   // 全域匯入
createApp(App).mount('#app')
  1. 透過 <transition> 包裹,配合 enter-active-classleave-active-class
<template>
  <button @click="show = !show">Toggle Box</button>

  <transition
    enter-active-class="animate__animated animate__fadeIn"
    leave-active-class="animate__animated animate__fadeOut"
  >
    <div v-if="show" class="box">Hello Vue 3!</div>
  </transition>
</template>

<script setup>
import { ref } from 'vue'
const show = ref(false)
</script>

<style scoped>
.box {
  width: 200px;
  height: 100px;
  background: #42b983;
  color: #fff;
  line-height: 100px;
  text-align: center;
  margin-top: 1rem;
}
</style>

說明

  • animate__animated 為 Animate.css 必須的基礎 class。
  • enter-active-classleave-active-class 直接對應 Vue 的 v-enter-activev-leave-active,因此不需要自行寫 v-enter-fromv-leave-to

2.3 動態切換不同動畫

<template>
  <select v-model="animIn">
    <option value="animate__fadeIn">Fade In</option>
    <option value="animate__zoomIn">Zoom In</option>
    <option value="animate__bounceIn">Bounce In</option>
  </select>

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

  <transition
    :enter-active-class="`animate__animated ${animIn}`"
    leave-active-class="animate__animated animate__fadeOut"
  >
    <div v-if="show" class="box">切換動畫</div>
  </transition>
</template>

<script setup>
import { ref } from 'vue'
const show = ref(false)
const animIn = ref('animate__fadeIn')
</script>

重點:使用 模板字串 (\${}``) 可以把 Vue 的狀態直接注入到 class 名稱,讓使用者即時切換動畫。


3. 整合 GSAP (GreenSock Animation Platform)

3.1 為什麼選擇 GSAP?

  • 時間軸控制:允許同時控制多個屬性、序列化動畫、重複與倒放。
  • 高效能:底層使用 requestAnimationFrame、硬體加速,對於大量元素或複雜運算仍保持流暢。
  • 插件豐富:如 ScrollTriggerDraggable,可擴展至滾動觸發、拖曳等交互。

3.2 安裝與基本設定

npm install gsap --save
// main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

注意:GSAP 本身不需要全域匯入 CSS,只要在需要的組件中 import { gsap } from 'gsap' 即可。

3.3 使用 Vue 3 的過渡鉤子(hooks)

Vue 允許在 <transition> 中使用 @before-enter@enter@after-enter@enter-cancelled@before-leave@leave@after-leave 等事件,我們可以在這些鉤子裡呼叫 GSAP。

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

  <transition
    @enter="enter"
    @leave="leave"
  >
    <div v-if="show" ref="box" class="box">GSAP 動畫</div>
  </transition>
</template>

<script setup>
import { ref } from 'vue'
import { gsap } from 'gsap'

const show = ref(false)
const box = ref(null)

// 進入動畫
function enter(el, done) {
  // el 為即將插入的 DOM,done 為告知 Vue 動畫結束的回呼
  gsap.fromTo(
    el,
    { opacity: 0, y: -30 },
    {
      opacity: 1,
      y: 0,
      duration: 0.6,
      ease: 'power2.out',
      onComplete: done,   // 必須呼叫 done,否則 Vue 會一直認為動畫未結束
    }
  )
}

// 離開動畫
function leave(el, done) {
  gsap.to(el, {
    opacity: 0,
    y: 30,
    duration: 0.4,
    ease: 'power2.in',
    onComplete: done,
  })
}
</script>

<style scoped>
.box {
  width: 200px;
  height: 100px;
  background: #ff6f61;
  color: #fff;
  line-height: 100px;
  text-align: center;
  margin-top: 1rem;
}
</style>

重點

  1. 必須在 enterleave 兩個鉤子中手動呼叫 done,告訴 Vue 動畫已完成。
  2. el 參數即為目標 DOM,可直接傳給 GSAP。

3.4 使用 GSAP Timeline 建立序列動畫

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

  <transition-group
    name="list"
    tag="ul"
    @enter="listEnter"
    @leave="listLeave"
  >
    <li
      v-for="item in items"
      :key="item.id"
      class="list-item"
    >{{ item.text }}</li>
  </transition-group>
</template>

<script setup>
import { ref, computed } from 'vue'
import { gsap } from 'gsap'

const show = ref(true)
const items = computed(() => {
  return show.value
    ? [
        { id: 1, text: '第一項' },
        { id: 2, text: '第二項' },
        { id: 3, text: '第三項' },
      ]
    : []
})

// 進入序列
function listEnter(el, done) {
  const tl = gsap.timeline({ onComplete: done })
  tl.from(el, {
    opacity: 0,
    y: -20,
    duration: 0.3,
    ease: 'back.out(1.7)',
  })
}

// 離開序列(倒序)
function listLeave(el, done) {
  const tl = gsap.timeline({ onComplete: done })
  tl.to(el, {
    opacity: 0,
    y: 20,
    duration: 0.2,
    ease: 'power1.in',
  })
}
</script>

<style scoped>
ul {
  list-style: none;
  padding: 0;
}
.list-item {
  background: #4a90e2;
  color: #fff;
  margin: 0.5rem 0;
  padding: 0.5rem;
  border-radius: 4px;
}
</style>

說明<transition-group> 會把每個子元素的 enterleave 鉤子分別傳入,讓我們能為每個 <li> 個別建立時間軸。

3.5 結合 Animate.css 與 GSAP

有時候想先用 Animate.css 做簡單的淡入淡出,接著在 enter 鉤子裡用 GSAP 加上更細緻的彈性動畫,這種混合寫法非常靈活:

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

  <transition
    enter-active-class="animate__animated animate__fadeIn"
    leave-active-class="animate__animated animate__fadeOut"
    @after-enter="gsapEnter"
  >
    <div v-if="show" class="card">混合動畫卡片</div>
  </transition>
</template>

<script setup>
import { ref } from 'vue'
import { gsap } from 'gsap'

const show = ref(false)

function gsapEnter(el) {
  // Animate.css 已完成淡入,我們再加上微小的縮放彈性
  gsap.fromTo(
    el,
    { scale: 0.9 },
    { scale: 1, duration: 0.4, ease: 'elastic.out(1, 0.5)' }
  )
}
</script>

<style scoped>
.card {
  width: 260px;
  height: 140px;
  background: #7ed321;
  color: #fff;
  line-height: 140px;
  text-align: center;
  border-radius: 8px;
  margin-top: 1rem;
}
</style>

技巧@after-enter 在 Animate.css 完成後才觸發,確保兩段動畫不會互相干擾。


常見陷阱與最佳實踐

陷阱 說明 解決方案
忘記呼叫 done enter/leave 鉤子裡未執行 done(),會導致過渡卡住、無法觸發 after-enter 等後續事件。 確保每個 GSAP 動畫的 onCompleteonReverseComplete 都呼叫 done
Animate.css 與自訂 CSS 衝突 Animate.css 使用 !important 來保證動畫屬性,若自行寫的 CSS 也使用 !important,可能導致樣式被覆寫。 盡量避免在同一屬性上使用 !important,或在自訂 CSS 中使用更高的選擇子權重(例如 .my-box.animate__fadeIn)。
過渡時間不一致 Vue 內建的 enter-active / leave-active 持續時間與 Animate.css/GSAP 動畫時間不匹配,會出現「卡頓」或「閃爍」。 統一時間:在 <transition> 上使用 duration 屬性或在 GSAP 時間軸裡設定 duration,並在 Animate.css 中使用 animate__duration-2s 等類別。
大量元素使用 GSAP 若在 <transition-group> 中一次渲染上百個元素,直接為每個元素建立 Timeline 會產生記憶體與效能問題。 使用 批次動畫:先把所有元素放入同一個 Timeline,或使用 gsap.utils.selector 只跑一次 from/to
SSR (Server‑Side Rendering) 問題 在 Nuxt 或 Vite SSR 中,windowdocument 在伺服器端不存在,直接 import gsap 會拋錯。 onMounted 之後才載入或呼叫 GSAP,或使用 import('gsap').then(...) 動態載入。

最佳實踐

  1. 統一過渡名稱:若同一個元件內同時使用 CSS 與 JS 動畫,建議在 <transition> 上指定 name="my",避免與全域的 v-enter-* 混淆。
  2. 把動畫邏輯抽成 composable:例如 useFadeTransition.js,裡面封裝 enterleave 函式,讓多個元件可以共享。
  3. 使用 prefers-reduced-motion:對於需要無障礙支援的使用者,應在 CSS 中加上 @media (prefers-reduced-motion: reduce) { ... },或在 JS 中檢測 window.matchMedia,在必要時停用動畫。
  4. 保持動畫簡潔:過長或過於炫目的動畫會分散注意力,建議單次過渡控制在 0.2~0.6 秒 之間。
  5. 測試跨瀏覽器:GSAP 在大多數現代瀏覽器表現一致,但在舊版 IE(若仍需支援)需加入 gsapCSSPlugin polyfill。

實際應用場景

場景 為何需要動畫 建議的實作方式
彈出式對話框 (Modal) 讓使用者感受到聚焦與層級變化,降低突兀感。 先用 Animate.css 的 fadeInDown 作淡入,接著 GSAP 加上彈性 scale
列表排序或過濾 項目移動、加入或刪除時要有視覺提示,避免使用者迷失。 使用 <transition-group> 搭配 GSAP Timeline,設定 stagger 讓每個項目依序動畫。
頁面切換 (Router View) SPA 內部切換時,給予過渡感,提升流暢度。 router-view 包裹 <transition>,使用 enter-active-class="animate__slideInRight"leave-active-class="animate__slideOutLeft";若需要更複雜的視差效果,可在 enter 鉤子裡使用 GSAP 的 fromTo
滾動觸發動畫 用戶瀏覽長頁面時,讓關鍵內容在視窗內出現時動態呈現。 結合 GSAP ScrollTrigger<transition>:在 onMounted 時為目標元素建立 ScrollTrigger,觸發時使用 gsap.from
表單驗證錯誤提示 錯誤訊息需要快速吸引注意力。 使用 Animate.css 的 shake 動畫,搭配 v-if 觸發 <transition>;若想要自訂抖動幅度,可在 enter 中使用 gsap.to(el, { x: '+=10', repeat: 5, yoyo: true })

總結

  • Vue 3 的 <transition> / <transition-group>CSSJavaScript 動畫提供了統一的生命週期介面。
  • Animate.css 讓開發者只需加 class 就能得到常見的淡入、彈跳、翻轉等效果,非常適合 快速原型小型專案
  • GSAP 則提供了 時間軸、彈性插值、滾動觸發 等高階功能,適合 複雜交互大量元素需要精細控制 的場景。
  • 透過 Vue 的過渡鉤子,我們可以在 enter / leave 階段自由呼叫 GSAP,並且在 Animate.css 完成後再加入額外的 GSAP 動畫,實現 混合式動畫
  • 常見陷阱多與 同步問題(未呼叫 done)以及 樣式衝突 有關,遵守最佳實踐(統一命名、抽象 composable、考慮 reduced‑motion)即可寫出 可維護、效能佳 的動畫程式碼。

掌握了上述技巧,你就能在 Vue 3 專案中 自如切換 從簡單的 CSS 動畫到強大的 GSAP 時間軸,為使用者打造既流暢又具吸引力的互動體驗。祝開發順利 🎉!