Vue.js: 过渡与动画

最后更新:2026-10-20

过渡与动画让你的 Vue 应用更生动——元素进入/离开时平滑过渡,列表变化时优雅重排。Vue 内置了 <Transition> 和 <TransitionGroup> 组件,配合 CSS 类名或 JS 钩子实现各种动画效果。

掌握过渡动画是提升用户体验的关键——好的动画让应用感觉"流畅、响应式、专业"。

相关指令:v-if

1. 你将学到


2. 一个"切换 Tab"硬切硬换的尴尬

(1) 痛点:组件突然消失/出现,UI 闪烁

Alice 的后台有个 Tab 切换器:

用户体验

(2) Vue Transition 解法:1 个标签,6 个 class

用户体验

(3) 收益

加上过渡后:


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

相关指令:v-if

❓ 常见问题

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 的钩子。

📖 小节


📝 作业

  1. 基础题(难度⭐) 实现一个简单的淡入淡出 Modal:

    • 点击按钮打开 Modal
    • 淡入 0.3 秒
    • 点击关闭淡出
    • 背景遮罩同步淡入淡出
  2. 进阶题(难度⭐⭐) 实现一个 TransitionGroup 列表动画:

    • 5 个待办事项
    • 添加新项淡入(0.5 秒)
    • 删除项淡出(0.5 秒 + position absolute)
    • 排序时其他项平滑移动(.list-move)
  3. 挑战题(难度⭐⭐⭐) 实现完整的"页面切换动画"系统:

    1. 路由切换时 fade + slide 动画
    2. 列表添加/删除/排序的 3 种动画
    3. Modal 弹窗的 scale + fade 组合
    4. GSAP 集成的复杂时间线动画
    5. 5 个第三方动画库对比测试
    6. 性能优化(GPU 加速 + will-change)
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏