Vue 3.0 的 Transition 组件提供了一种简单的方式来为元素或组件的进入/离开添加动画效果。下面是使用<script setup>语法糖的实现方式。
1. 基本用法
使用场景:当需要为元素的显示/隐藏添加简单的淡入淡出效果时,这是最基础的过渡实现方式。
<template>
<!-- 按钮用于触发显示/隐藏状态切换 -->
<button @click="show = !show">Toggle</button>
<!-- Transition包裹需要动画的元素 -->
<Transition>
<p v-if="show">Hello Vue 3 Transition!</p>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<style>
/*
* 定义过渡效果:
* - v-enter-active/v-leave-active 定义过渡持续时间和缓动函数
* - v-enter-from/v-leave-to 定义起始和结束状态
*/
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>
实现原理:当元素插入或删除时,Vue会自动在适当的时机添加/移除对应的CSS类名,从而触发CSS过渡效果。这种实现方式性能较好,适合大多数简单场景。
2. 自定义类名前缀
使用场景:当项目中存在多个不同的过渡效果时,为了避免类名冲突,或者需要更语义化的类名时使用。
<template>
<button @click="show = !show">Toggle</button>
<!-- 使用name属性定义前缀 -->
<Transition name="fade">
<p v-if="show">Hello with custom name</p>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<style>
/* 类名前缀变为fade- */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
最佳实践:建议为每个独立的过渡效果都定义特定的前缀,这样既避免了样式冲突,也提高了代码可读性。特别是当项目中使用多种动画库时,这种隔离尤为重要。
3. JavaScript 钩子
使用场景:当需要实现复杂的动画逻辑,或者需要与第三方JavaScript动画库(如GSAP)集成时使用。
<template>
<button @click="show = !show">Toggle</button>
<!-- 绑定各个动画阶段的钩子函数 -->
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<div v-if="show" class="box">Content</div>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
// 进入动画开始前的准备工作
const onBeforeEnter = (el) => {
el.style.opacity = 0
el.style.transform = 'scale(0.5)'
}
// 执行进入动画
const onEnter = (el, done) => {
// 使用requestAnimationFrame确保动画流畅
requestAnimationFrame(() => {
el.style.transition = 'all 0.5s ease'
el.style.opacity = 1
el.style.transform = 'scale(1)'
// 监听过渡结束事件
el.addEventListener('transitionend', done, { once: true })
})
}
// 其他钩子函数...
</script>
<style>
.box {
width: 100px;
height: 100px;
background-color: #42b983;
border-radius: 4px;
}
</style>
注意事项:使用JavaScript钩子时,必须确保在适当的时机调用done回调函数,否则过渡将无法正常完成。这种方式虽然灵活,但性能不如纯CSS实现,应谨慎使用。
4. 过渡模式
使用场景:当两个元素需要交替显示时(如标签切换、轮播图等),避免新旧元素同时存在导致的布局问题。
<template>
<button @click="toggleView">Toggle View</button>
<!-- 使用out-in模式确保当前元素先离开,新元素再进入 -->
<Transition mode="out-in">
<component :is="currentView" :key="currentView.name"></component>
</Transition>
</template>
<script setup>
import { ref, shallowRef } from 'vue'
import CompA from './CompA.vue'
import CompB from './CompB.vue'
const currentView = shallowRef(CompA)
const toggleView = () => {
currentView.value = currentView.value === CompA ? CompB : CompA
}
</script>
<style>
.v-enter-active,
.v-leave-active {
transition: opacity 0.3s ease, transform 0.3s ease;
}
.v-enter-from {
opacity: 0;
transform: translateX(30px);
}
.v-leave-to {
opacity: 0;
transform: translateX(-30px);
}
</style>
模式选择:
in-out:新元素先进入,完成后当前元素离开,使用较少;
out-in:当前元素先离开,完成后新元素进入,更符合用户预期,使用较多;
5. 列表过渡
使用场景:当需要对v-for渲染的列表元素添加排序、添加、删除动画时使用。
<template>
<div class="demo">
<button @click="addItem">Add Item</button>
<button @click="removeItem">Remove Item</button>
<button @click="shuffleItems">Shuffle</button>
<!-- TransitionGroup管理列表过渡 -->
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id" class="item">
{{ item.text }}
</li>
</TransitionGroup>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { shuffle } from 'lodash-es'
let id = 3
const items = ref([
{ id: 0, text: 'Item 0' },
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' }
])
const addItem = () => {
items.value.push({ id: id++, text: `Item ${id}` })
}
const removeItem = () => {
if (items.value.length > 0) {
const randomIndex = Math.floor(Math.random() * items.value.length)
items.value.splice(randomIndex, 1)
}
}
const shuffleItems = () => {
items.value = shuffle(items.value)
}
</script>
<style>
/* 进入/离开动画 */
.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;
}
/* 排序动画 */
.list-move {
transition: transform 0.5s ease;
}
/* 基础样式 */
.demo {
max-width: 300px;
margin: 20px auto;
}
.item {
padding: 8px 16px;
margin: 4px 0;
background-color: #f3f3f3;
border-radius: 4px;
}
ul {
position: relative;
padding: 0;
list-style: none;
}
</style>
关键点:
1. 必须为每个元素设置唯一的key;
2. 使用list-move类处理排序动画;
3. 离开动画需要设置position: absolute避免布局问题;
4. 容器需要设置position: relative;
6. 与 CSS 动画库集成
使用场景:当需要快速实现复杂动画效果,或项目中使用专业动画库时。
<template>
<button @click="show = !show">Toggle</button>
<!-- 直接使用动画库的类名 -->
<Transition
enter-active-class="animate__animated animate__bounceIn"
leave-active-class="animate__animated animate__hinge"
>
<p v-if="show" class="demo-text">Using Animate.css</p>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
import 'animate.css'
const show = ref(true)
</script>
<style>
.demo-text {
font-size: 24px;
margin: 20px 0;
color: #42b983;
}
</style>
优势:
1. 无需手动编写复杂动画;
2. 可以轻松实现专业级动画效果;
3. 跨浏览器兼容性好;
推荐库:
1. Animate.css:提供大量预设动画;
2. Motion One:轻量级高性能动画库;
3. GSAP:专业级动画解决方案;
7. 性能优化建议
1. 优先使用CSS过渡:浏览器对CSS动画的优化更好,性能更高;
2. 使用transform和opacity:这些属性不会触发重排,动画更流畅;
3. 避免使用height/margin等属性:这些属性会导致布局重计算;
4. 合理使用will-change:提前告知浏览器哪些属性会变化;
5. 减少同时运行的动画数量:过多动画会消耗大量资源;
6. 使用硬件加速:对移动设备特别重要;
.animated-element {
transform: translateZ(0);
}
通过合理使用这些技术,可以在保证用户体验的同时,确保应用的性能表现。