VueJS - 过渡与动画
VueJS - 过渡与动画
Section titled “VueJS - 过渡与动画”在本章中,我们将探讨如何在元素被添加到 DOM(文档对象模型)、更新或移除时,为你的 VueJS 应用中的元素应用吸引人的过渡和动画效果。
使用 <transition> 组件
Section titled “使用 <transition> 组件”Vue 提供了一个内置的 <transition> 包装组件。你可以将这个组件包裹在你希望在进入或离开 DOM 时(通常使用像 v-if、v-show 或动态组件等指令)应用过渡效果的任何元素或组件外部。
<transition name="my-transition"> <p v-if="show">Hello</p></transition>当一个被 <transition> 包裹的元素插入或移除时,Vue 会在过渡的不同阶段自动添加/移除 CSS 类。name 属性(例如 my-transition)用作这些类的前缀。如果未提供 name,则前缀默认为 v-。
进入/离开过渡会应用以下六个类:
- 进入阶段:
v-enter-from: 进入的起始状态。在元素被插入之前添加,在插入后一帧移除。v-enter-active: 进入的活跃状态。在整个进入阶段应用。用于定义持续时间、延迟和缓动曲线。当过渡完成时移除。v-enter-to: 进入的结束状态。在插入后一帧添加(与v-enter-from移除同时),当过渡完成时移除。- 离开阶段:
v-leave-from: 离开的起始状态。在触发离开过渡时立即添加,在一帧后移除。v-leave-active: 离开的活跃状态。在整个离开阶段应用。用于定义持续时间、延迟和缓动曲线。当过渡完成时移除。v-leave-to: 离开的结束状态。在触发离开过渡后一帧添加(与v-leave-from移除同时),当过渡完成时移除。
如果你使用了像 name="fade" 这样的 name 属性,则类名将是 .fade-enter-from、.fade-enter-active 等。
示例:淡入淡出过渡
Section titled “示例:淡入淡出过渡”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Transition Example</title> <script src="https://unpkg.com/vue@3"></script> <style> /* 元素的基本样式 */ .demo-p { font-size: 20px; color: darkcyan; }
/* 定义过渡状态 */ .fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
/* 可选:如果需要,定义 enter-to 和 leave-from 状态, 通常 opacity: 1 是默认值 */ .fade-enter-to, .fade-leave-from { opacity: 1; } </style></head><body> <div id="app"> <button @click="show = !show">Toggle Text</button> <transition name="fade"> <p class="demo-p" v-if="show">Animation Example</p> </transition> </div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const show = ref(true); return { show }; } }).mount('#app'); </script></body></html>在此示例中:
- 我们使用了 Vue 3 的 Composition API(
createApp、ref)。 <p>标签使用v-if="show"进行条件渲染。- 它被包裹在
<transition name="fade">中。 - CSS 类
.fade-enter-active和.fade-leave-active定义了过渡持续时间和缓动。 .fade-enter-from和.fade-leave-to定义了元素不可见的状态(opacity 为 0)。
当点击 ‘Toggle Text’ 按钮时,show ref 会改变。如果 show 变为 true,段落会在 0.5 秒内淡入。如果 show 变为 false,它会淡出。
CSS 动画
Section titled “CSS 动画”你也可以使用 CSS 动画代替过渡。与 <transition> 组件的使用方式类似,但 Vue 会监听 animationend 事件而不是 transitionend 事件。通常你在 *-enter-active 或 *-leave-active 类中定义动画。
示例:弹跳动画
Section titled “示例:弹跳动画”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Animation Example</title> <script src="https://unpkg.com/vue@3"></script> <style> .bounce-enter-active { animation: bounce-in 0.5s; } .bounce-leave-active { animation: bounce-in 0.5s reverse; /* 离开时使用反向动画 */ }
@keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(1.25); } 100% { transform: scale(1); } }
.demo-div { width: 100px; height: 100px; background-color: tomato; border-radius: 50%; margin-top: 20px; } </style></head><body> <div id="app"> <button @click="show = !show">Toggle Ball</button> <transition name="bounce"> <div class="demo-div" v-if="show"></div> </transition> </div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const show = ref(true); return { show }; } }).mount('#app'); </script></body></html>这里,我们定义了一个名为 bounce-in 的 @keyframes 动画。我们将此动画应用于 .bounce-enter-active 类。对于离开动画(.bounce-leave-active),我们应用相同的动画,但方向 reverse。当按钮切换时,红色圆圈会以弹跳效果出现或以反向弹跳消失。
使用动画库(例如 Animate.css)
Section titled “使用动画库(例如 Animate.css)”你可以轻松集成第三方 CSS 动画库,例如 Animate.css。你可以直接使用特定属性向 <transition> 组件提供自定义类名,而无需自己定义过渡类。
enter-from-classenter-active-classenter-to-classleave-from-classleave-active-classleave-to-class
这些属性的优先级高于常规类名,对于将 Vue 的过渡与 Animate.css 等库结合使用特别有用。
使用 Animate.css 的示例
Section titled “使用 Animate.css 的示例”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Animate.css Example</title> <!-- 使用较新版本的 Animate.css --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> <script src="https://unpkg.com/vue@3"></script> <style> .demo-p { font-size: 24px; font-weight: bold; color: steelblue; } </style></head><body> <div id="app"> <button @click="show = !show">Toggle with Animate.css</button>
<transition enter-active-class="animate__animated animate__fadeInDown" leave-active-class="animate__animated animate__fadeOutUp" > <p class="demo-p" v-if="show">Using Animate.css!</p> </transition> </div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const show = ref(true); return { show }; } }).mount('#app'); </script></body></html>在这段代码中,我们引入了 Animate.css 库。然后,我们在 <transition> 组件上使用了 enter-active-class 和 leave-active-class 属性。我们提供了必要的 Animate.css 类(animate__animated 是必需的,加上特定的动画类如 animate__fadeInDown)。现在,切换段落会使用库中预定义的动画。
显式指定过渡持续时间
Section titled “显式指定过渡持续时间”Vue 通过监听 transitionend 或 animationend 事件自动检测过渡或动画何时结束。但是,如果你有复杂的过渡(例如,CSS 过渡与持续时间不同的 CSS 动画混合),或者 Vue 无法正确检测到持续时间,你可以使用 duration prop 指定显式持续时间。
<!-- 进入和离开使用相同的持续时间 --><transition :duration="1000">...</transition>
<!-- 进入和离开使用不同的持续时间 --><transition :duration="{ enter: 500, leave: 800 }">...</transition>持续时间以毫秒为单位指定。
JavaScript 钩子
Section titled “JavaScript 钩子”对于更复杂的动画或 CSS 过渡/动画不足以处理的场景(例如,动画化 CSS 无法直接处理的元素属性,协调多个元素),你可以使用 JavaScript 钩子。你定义方法并将它们绑定到 <transition> 组件的事件上。
@before-enter(el)@enter(el, done): 重要: 必须调用done()来表示进入动画结束。@after-enter(el)@enter-cancelled(el)@before-leave(el)@leave(el, done): 重要: 必须调用done()来表示离开动画结束。@after-leave(el)@leave-cancelled(el)(仅在使用v-show时可用)
仅使用 JavaScript 钩子(不使用 CSS 过渡/动画)时,一个好的做法是向 <transition> 组件添加 :css="false"。这会告诉 Vue 跳过 CSS 检测,可能会稍微提高性能并防止 CSS 规则干扰。
示例:使用 JS 钩子实现简单的透明度变化
Section titled “示例:使用 JS 钩子实现简单的透明度变化”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 JS Hooks Example</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <button @click="show = !show">Toggle with JS</button> <transition @before-enter="onBeforeEnter" @enter="onEnter" @leave="onLeave" :css="false" /* 告诉 Vue 我们通过 JS 处理动画 */ > <p style="font-size: 20px; color: green;" v-if="show"> Animating with JavaScript! </p> </transition> </div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const show = ref(false);
const onBeforeEnter = (el) => { // 在进入前设置初始状态 el.style.opacity = 0; el.style.transform = 'translateY(20px)'; };
const onEnter = (el, done) => { // 开始动画 // 使用简单的 requestAnimationFrame 进行演示 let start; const duration = 500; // ms
function step(timestamp) { if (start === undefined) start = timestamp; const elapsed = timestamp - start; const progress = Math.min(elapsed / duration, 1);
el.style.opacity = progress; el.style.transform = `translateY(${20 * (1 - progress)}px)`;
if (progress < 1) { requestAnimationFrame(step); } else { done(); // 标志动画完成 } } requestAnimationFrame(step); };
const onLeave = (el, done) => { // 开始离开动画(逻辑类似,反转透明度和 transform) let start; const duration = 300; // ms
function step(timestamp) { if (start === undefined) start = timestamp; const elapsed = timestamp - start; const progress = Math.min(elapsed / duration, 1);
el.style.opacity = 1 - progress; el.style.transform = `translateY(${20 * progress}px)`;
if (progress < 1) { requestAnimationFrame(step); } else { done(); // 标志动画完成 } } requestAnimationFrame(step); };
return { show, onBeforeEnter, onEnter, onLeave }; } }).mount('#app'); </script></body></html>此示例使用 requestAnimationFrame 进行了一个基本的 JavaScript 动画,控制元素的透明度和位置。像 GSAP(GreenSock Animation Platform)这样的库通常用于更复杂的 JS 动画,提供强大的补间功能和时间线控制。请记住,在 @enter 和 @leave 钩子中动画完成时调用 done 回调函数。
常见陷阱:在 @enter 和 @leave 钩子中忘记调用 done 回调函数将导致过渡无限期地挂起。
初始渲染时的过渡
Section titled “初始渲染时的过渡”要在组件首次挂载(初始渲染)时应用过渡,请在 <transition> 组件上添加 appear 属性。
<transition name="fade" appear> <p v-if="true">加载时淡入</p></transition>你也可以使用 appear-active-class、appear-from-class、appear-to-class 等属性,或像 @before-appear、@appear、@after-appear、@appear-cancelled 等 JS 钩子,专门为初始渲染过渡指定自定义 CSS 类或 JavaScript 钩子。
元素/组件之间的过渡
Section titled “元素/组件之间的过渡”当使用 v-if/v-else-if/v-else 在元素之间切换,或通过 <component :is="..."> 切换动态组件时,<transition> 组件也可以处理过渡。但是,一个常见的要求是两个元素必须具有唯一的 key 属性,以便 Vue 能正确地追踪并为其应用过渡。
<transition name="fade" mode="out-in"> <button v-if="isEditing" key="edit">Edit</button> <button v-else key="save">Save</button></transition>mode 属性控制 timing:out-in(当前元素先过渡离开,然后新元素过渡进入)或 in-out(新元素先过渡进入,然后当前元素过渡离开)。通常建议使用 out-in 以获得更流畅的视觉效果。
列表过渡:使用 <TransitionGroup>
Section titled “列表过渡:使用 <TransitionGroup>”为了动画化使用 v-for 渲染的列表,Vue 提供了 <TransitionGroup> 组件。它会渲染一个真实的 DOM 元素(默认为 <span>,可通过 tag prop 配置),并将过渡应用于其内部的元素。
与 <transition> 的主要区别:
- 它会渲染一个实际的元素(例如
<ul>、<div>),而不是抽象的。 - 过渡类应用于内部元素,而不是 group 元素本身。
- 列表中的每个项都必须具有唯一的
key属性。 - 它支持移动过渡(
v-move类),用于列表顺序改变时,这需要使用 CSStransform。
示例:列表淡入淡出
Section titled “示例:列表淡入淡出”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 TransitionGroup Example</title> <script src="https://unpkg.com/vue@3"></script> <style> .list-item { display: inline-block; margin-right: 10px; } .list-enter-active, .list-leave-active { transition: all 0.5s ease; } .list-enter-from, .list-leave-to { opacity: 0; transform: translateY(30px); } /* 确保离开的元素脱离文档流,以便正确计算移动动画。 */ .list-leave-active { position: absolute; } </style></head><body> <div id="app"> <button @click="addItem">Add Item</button> <button @click="removeItem">Remove Item</button> <transition-group name="list" tag="ul"> <li v-for="item in items" :key="item.id" class="list-item"> {{ item.text }} </li> </transition-group> </div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const items = ref([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' } ]); let nextId = 4;
const addItem = () => { const randomIndex = Math.floor(Math.random() * items.value.length); items.value.splice(randomIndex, 0, { id: nextId++, text: `Item ${nextId -1}` }); };
const removeItem = () => { if (items.value.length > 0) { const randomIndex = Math.floor(Math.random() * items.value.length); items.value.splice(randomIndex, 1); } };
return { items, addItem, removeItem }; } }).mount('#app'); </script></body></html>此示例演示了如何通过淡入淡出和滑动过渡来添加和移除列表项。注意每个 li 上的 key 使用以及 <transition-group> 上的 tag="ul" prop。.list-leave-active { position: absolute; } 样式对于在移除项目时实现平滑的移动动画非常重要。
进一步阅读:Vue 官方文档提供了关于过渡的深入细节:https://vuejs.org/guide/built-ins/transition.html 以及列表过渡:https://vuejs.org/guide/built-ins/transition-group.html