Vue.js: 过渡与动画
最后更新:2026-10-20
过渡与动画让你的 Vue 应用更生动——元素进入/离开时平滑过渡,列表变化时优雅重排。Vue 内置了 <Transition> 和 <TransitionGroup> 组件,配合 CSS 类名或 JS 钩子实现各种动画效果。
掌握过渡动画是提升用户体验的关键——好的动画让应用感觉"流畅、响应式、专业"。
1. 你将学到
<Transition>单元素进入/离开动画- 6 种 CSS class 钩子(v-enter-from / v-enter-active 等)
<TransitionGroup>列表动画- JavaScript 钩子(@before-enter / @enter 等)
- 第三方动画库集成(@vueuse/motion、GSAP)
- Animate.css 集成
- 5 大实战场景
2. 一个"切换 Tab"硬切硬换的尴尬
(1) 痛点:组件突然消失/出现,UI 闪烁
Alice 的后台有个 Tab 切换器:
用户体验
- Tab 切换时,突然出现/消失
- 没有过渡动画,感觉"生硬"
- 专业感不足
(2) Vue Transition 解法:1 个标签,6 个 class
用户体验
- Tab 切换时,平滑淡入淡出
- 0.3 秒过渡,专业感强
- 6 个 CSS class 自动应用
(3) 收益
加上过渡后:
- 用户体验:从"生硬"到"流畅"
- 专业感:显著提升
- 代码量:1 个
<Transition>标签 + 6 个 CSS class - 可定制:4 种 transition name + 3 种 mode
3. Transition 单元素
(1) 基本用法
(2) 6 种 CSS class 详解
| Class | 触发时机 | 用途 |
|---|---|---|
v-enter-from |
元素插入前 | 起始状态 |
v-enter-active |
元素插入整个过程 | 过渡过程 |
v-enter-to |
元素插入后 | 结束状态 |
v-leave-from |
元素离开前 | 起始状态 |
v-leave-active |
元素离开整个过程 | 过渡过程 |
v-leave-to |
元素离开后 | 结束状态 |
(3) 命名规则
VUE
<!-- v-bind:name="fade" → 6 个 class 是 .fade-xxx -->
<Transition name="fade">...</Transition>
<!-- 不写 name → 默认 v-xxx(6 个 class 是 .v-xxx)-->
<Transition>...</Transition>
<!-- 动态 name -->
<Transition :name="transitionName">...</Transition>
(4) 3 种模式
VUE
<!-- 1. 默认模式:进入和离开同时进行(重叠) -->
<Transition name="fade">
<div v-if="show">Content</div>
</Transition>
<!-- 2. out-in:先离开后进入(推荐,避免重叠) -->
<Transition name="fade" mode="out-in">
<div v-if="show">Content</div>
</Transition>
<!-- 3. in-out:先进入后离开(少见) -->
<Transition name="fade" mode="in-out">
<div v-if="show">Content</div>
</Transition>
▶ 示例:默认模式与 mode="out-in" 并排对比
▶ 试一试同一组
v-if卡片,上面不加 mode(进出重叠),下面加mode="out-in"(先出后进),切换时能直接看出差别。
输出:
TEXT
初始两块区域都显示绿色「A 卡片」,按钮文字是「切换卡片(当前 A)」。
点按钮后:上方「默认模式」区域旧卡片淡出与新卡片淡入同时进行,绿色和蓝色卡片短暂叠在一起,容器高度会抖一下。
下方 `mode="out-in"` 区域先等绿色卡片淡出(约 0.5 秒),再淡入蓝色「B 卡片」,任何时刻只看到一张卡片。
再点一次又切回 A,两块的差别同样清晰。
4. 5 种 CSS 动画
(1) 淡入淡出(最常用)
CSS
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
(2) 滑动
CSS
.slide-enter-active, .slide-leave-active {
transition: transform 0.3s;
}
.slide-enter-from {
transform: translateX(-100%);
}
.slide-leave-to {
transform: translateX(100%);
}
(3) 缩放
CSS
.scale-enter-active, .scale-leave-active {
transition: transform 0.3s;
}
.scale-enter-from, .scale-leave-to {
transform: scale(0);
}
(4) 旋转
CSS
.rotate-enter-active {
transition: transform 1s;
}
.rotate-enter-from {
transform: rotate(0deg);
}
.rotate-leave-active {
animation: bounce 0.5s;
}
@keyframes bounce {
0% { transform: scale(0); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
(5) 组合动画
CSS
.combo-enter-active, .combo-leave-active {
transition: all 0.3s;
}
.combo-enter-from {
opacity: 0;
transform: translateX(-50px) scale(0.8);
}
.combo-leave-to {
opacity: 0;
transform: translateX(50px) scale(0.8);
}
5. TransitionGroup 列表动画
(1) 基本用法
(2) 关键点:.list-move
CSS
/* 列表项位置变化时(如排序),自动应用 .list-move */
.list-move {
transition: transform 0.5s;
}
(3) 5 大列表动画场景
| 场景 | 动画 |
|---|---|
| 添加项 | 淡入 + 下滑 |
| 删除项 | 淡出 + 左滑 |
| 排序 | 平滑移动到新位置 |
| 筛选 | 离开的淡出,保留的平滑过渡 |
| 拖拽 | 实时跟随鼠标 |
▶ 示例:TransitionGroup 的添加 / 删除 / 移动
▶ 试一试点「添加一项」看淡入、点某行 × 看淡出后其余行上移、点「首项移到底部」看
.list-move让整行平滑滑动。
输出:
TEXT
列表初始 3 行:Vue / Router / Pinia,每行右侧有红色 × 按钮。
点「添加一项」:新行「新项 4」在末尾淡入并从右侧滑入(0.5 秒);再点得到「新项 5」。
点某一行的 × 该行淡出,其它行在 0.5 秒内平滑上移补位,不会瞬移。
点「首项移到底部」:第一行平滑下滑到末尾,其余行依次上移,整个过程是连续移动而不是跳变。
6. JavaScript 钩子
(1) 8 个 JS 钩子
VUE
<template>
<Transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
:css="false" <!-- 禁用 CSS class -->
>
<p v-if="show">Hello</p>
</Transition>
</template>
<script setup>
import gsap from 'gsap'
function beforeEnter(el) {
console.log('Before enter:', el)
}
function enter(el, done) {
gsap.to(el, {
opacity: 1,
y: 0,
duration: 0.5,
onComplete: done // 通知完成
})
}
function afterEnter(el) {
console.log('After enter:', el)
}
// leave / beforeLeave / afterLeave 类似
</script>
(2) GSAP 集成(推荐)
BASH
npm install gsap
VUE
<template>
<Transition :css="false" @enter="onEnter" @leave="onLeave">
<p v-if="show">Hello GSAP</p>
</Transition>
</template>
<script setup>
import gsap from 'gsap'
function onEnter(el, done) {
gsap.fromTo(el,
{ opacity: 0, y: 50 },
{ opacity: 1, y: 0, duration: 0.5, onComplete: done }
)
}
function onLeave(el, done) {
gsap.to(el, { opacity: 0, y: -50, duration: 0.3, onComplete: done })
}
</script>
7. 第三方动画库
(1) Animate.css 集成
BASH
npm install animate.css
JS
// main.js
import 'animate.css'
(2) @vueuse/motion
BASH
npm install @vueuse/motion
JS
// main.js
import { MotionPlugin } from '@vueuse/motion'
app.use(MotionPlugin)
(3) GSAP 推荐场景
| 库 | 推荐度 | 适用 |
|---|---|---|
| CSS 动画 | ⭐⭐⭐⭐⭐ | 简单过渡、淡入淡出 |
| Animate.css | ⭐⭐⭐⭐ | 大量预设动画 |
| @vueuse/motion | ⭐⭐⭐⭐ | 声明式、Vue 集成好 |
| GSAP | ⭐⭐⭐⭐⭐ | 复杂时间线、物理动画 |
8. 综合演练:5 种过渡动画一次跑通
▶ 示例:5 种 Vue Transition 动画(CDN 全支持)
▶ 展开运行示例(87 行)
输出:
TEXT
页面从上到下依次是 5 个区块:Fade、Slide、Scale Modal、TransitionGroup 列表、Combo Tab。
Fade 区默认显示「👋 Hello Fade」,点「隐藏」淡出、按钮变为「显示」,再点淡入。
Slide 区默认显示「👉 Slide 滑入」,点「切换」向右滑出、从左滑入;点「打开 Modal」弹窗缩放出现,点遮罩或「关闭」缩放消失。
列表初始有「项目 1」「项目 2」,输入名称点「添加」新项淡入并平滑移动,点 × 淡出后其余项上移。
Tab 默认 Home,点 Profile / Settings 时旧内容先淡出、新内容再淡入,显示「当前 Tab: Profile」等。
(1) 📊 6 种 CSS class 详解
| Class | 触发 |
|---|---|
.fade-enter-from |
元素插入前 |
.fade-enter-active |
元素插入过程 |
.fade-enter-to |
元素插入后 |
.fade-leave-from |
元素离开前 |
.fade-leave-active |
元素离开过程 |
.fade-leave-to |
元素离开后 |
(2) 📊 5 大第三方动画库(⚠️ 需 Vite)
| 库 | 命令 | 适用 |
|---|---|---|
| CSS | 无(内置) | 简单 |
| Animate.css | npm i animate.css |
预设动画 |
| @vueuse/motion | npm i @vueuse/motion |
声明式 |
| GSAP | npm i gsap |
复杂时间线 |
| Motion One | npm i motion |
轻量 Web 动画 |
(3) 📊 5 个常见错误速查
| 错误 | 现象 | 解决 |
|---|---|---|
| Transition 不生效 | 元素直接消失 | 加 <Transition> 包裹 + v-if |
| v-for 不动画 | 列表项没过渡 | 用 <TransitionGroup> |
| 排序没动画 | 移动突兀 | 加 .list-move class |
| 多个组件同时切换 | 重叠 | 加 key 区分 + mode="out-in" |
| CSS 动画太快/太慢 | 体验差 | 调整 transition duration 0.2-0.5s |
(4) 📊 5 大实战场景
| 场景 | 动画 |
|---|---|
| Tab 切换 | fade + out-in |
| Modal 弹窗 | scale + fade |
| 列表添加/删除 | list 动画 |
| 路由切换 | fade + slide |
| 通知提示 | slide from top + auto-dismiss |
❓ 常见问题
Q Transition 和 CSS @keyframes 怎么选?
A 简单过渡(opacity/transform)用 CSS transition。复杂动画(多步骤、物理)用 @keyframes 或 GSAP。Vue Transition 支持两种。
Q TransitionGroup 必须用 :key 吗?
A 是的。
<TransitionGroup> 子元素必须用 :key 唯一标识,否则动画不触发。Q 怎么实现"页面切换"动画?
A 用
<router-view v-slot="{ Component }"> + <Transition> 包裹。每次路由变化,Component 会变化,触发 Transition。Q mode="out-in" 有什么用?
A 让离开的组件先完成动画,再让进入的组件开始。避免两个组件同时显示(重叠)。
Q JS 钩子和 CSS class 能混用吗?
A 能。
<Transition :css="false" @enter="onEnter"> 禁用 CSS class,只用 JS 钩子。或两者都启用(用 CSS class 做样式,JS 钩子做回调)。Q 动画性能差怎么办?
A (1) 用 transform 和 opacity(GPU 加速);(2) 避免 layout 触发的属性(width/height/top);(3) 减少同时动画的元素数量。
Q Animate.css 怎么配合 Vue?
A 用
enter-active-class="animate__animated animate__fadeIn",把 Animate.css 的 class 名字映射到 Vue 的钩子。📖 小节
<Transition>单元素进入/离开,6 种 CSS class 自动管理<TransitionGroup>列表动画,.list-move处理排序- 3 种 mode:默认(重叠)/ out-in(推荐)/ in-out
- 5 种 CSS 动画:淡入淡出 / 滑动 / 缩放 / 旋转 / 组合
- JS 钩子 8 个:before-enter / enter / after-enter / before-leave / leave / after-leave
- 5 大第三方库:CSS / Animate.css / @vueuse/motion / GSAP / Motion One
- 5 大实战:Tab 切换 / Modal / 列表 / 路由 / 通知
📝 作业
-
基础题(难度⭐) 实现一个简单的淡入淡出 Modal:
- 点击按钮打开 Modal
- 淡入 0.3 秒
- 点击关闭淡出
- 背景遮罩同步淡入淡出
-
进阶题(难度⭐⭐) 实现一个 TransitionGroup 列表动画:
- 5 个待办事项
- 添加新项淡入(0.5 秒)
- 删除项淡出(0.5 秒 + position absolute)
- 排序时其他项平滑移动(.list-move)
-
挑战题(难度⭐⭐⭐) 实现完整的"页面切换动画"系统:
- 路由切换时 fade + slide 动画
- 列表添加/删除/排序的 3 种动画
- Modal 弹窗的 scale + fade 组合
- GSAP 集成的复杂时间线动画
- 5 个第三方动画库对比测试
- 性能优化(GPU 加速 + will-change)