Vue 的过渡系统提供了多种方式来实现动画效果。
展示 Vue 的基本过渡效果。
<template>
<div>
<button @click="show = !show">切换</button>
<transition name="fade">
<p v-if="show">Hello Vue!</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
Vue 过渡系统提供了 6 个类名用于定义过渡效果。
展示所有过渡类名的使用。
<template>
<div>
<button @click="show = !show">切换</button>
<transition name="slide">
<p v-if="show">滑动效果</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
/* 进入前 */
.slide-enter-from {
transform: translateX(-100%);
}
/* 进入过程 */
.slide-enter-active {
transition: transform 0.5s ease;
}
/* 进入后 */
.slide-enter-to {
transform: translateX(0);
}
/* 离开前 */
.slide-leave-from {
transform: translateX(0);
}
/* 离开过程 */
.slide-leave-active {
transition: transform 0.5s ease;
}
/* 离开后 */
.slide-leave-to {
transform: translateX(100%);
}
</style>
使用 CSS 动画实现更复杂的过渡效果。
展示 CSS 动画的使用。
<template>
<div>
<button @click="show = !show">切换</button>
<transition name="bounce">
<p v-if="show">弹跳效果</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</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);
}
}
</style>
使用自定义类名实现第三方动画库的集成。
展示自定义过渡类名的使用。
<template>
<div>
<button @click="show = !show">切换</button>
<transition
enter-active-class="animate__animated animate__bounceIn"
leave-active-class="animate__animated animate__bounceOut"
>
<p v-if="show">Animate.css 效果</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
@import 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css';
</style>
使用 JavaScript 钩子实现更复杂的动画效果。
展示 JavaScript 钩子的使用。
<template>
<div>
<button @click="show = !show">切换</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<p v-if="show">JavaScript 动画</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
},
methods: {
beforeEnter(el) {
el.style.opacity = 0
el.style.transform = 'translateY(100px)'
},
enter(el, done) {
const animation = el.animate([
{ opacity: 0, transform: 'translateY(100px)' },
{ opacity: 1, transform: 'translateY(0)' }
], {
duration: 1000,
easing: 'ease-in-out'
})
animation.onfinish = done
},
afterEnter(el) {
el.style.opacity = ''
el.style.transform = ''
},
enterCancelled(el) {
el.style.opacity = ''
el.style.transform = ''
},
beforeLeave(el) {
el.style.opacity = 1
el.style.transform = 'translateY(0)'
},
leave(el, done) {
const animation = el.animate([
{ opacity: 1, transform: 'translateY(0)' },
{ opacity: 0, transform: 'translateY(100px)' }
], {
duration: 1000,
easing: 'ease-in-out'
})
animation.onfinish = done
},
afterLeave(el) {
el.style.opacity = ''
el.style.transform = ''
},
leaveCancelled(el) {
el.style.opacity = ''
el.style.transform = ''
}
}
}
</script>
集成第三方动画库 Velocity.js。
展示 Velocity.js 的使用。
<template>
<div>
<button @click="show = !show">切换</button>
<transition
@enter="enter"
@leave="leave"
:css="false"
>
<p v-if="show">Velocity.js 动画</p>
</transition>
</div>
</template>
<script>
import Velocity from 'velocity-animate'
export default {
data() {
return {
show: true
}
},
methods: {
enter(el, done) {
Velocity(el,
{ opacity: 1, translateY: 0 },
{
duration: 1000,
easing: 'ease-in-out',
complete: done
}
)
},
leave(el, done) {
Velocity(el,
{ opacity: 0, translateY: '100px' },
{
duration: 1000,
easing: 'ease-in-out',
complete: done
}
)
}
}
}
</script>
使用 transition-group 实现列表项的过渡效果。
展示列表过渡的基本使用。
<template>
<div>
<button @click="addItem">添加项目</button>
<button @click="removeItem">删除项目</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
items: [1, 2, 3, 4, 5],
nextNum: 6
}
},
methods: {
addItem() {
this.items.push(this.nextNum++)
},
removeItem() {
this.items.pop()
}
}
}
</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-move {
transition: transform 0.5s ease;
}
</style>
实现列表项的排序动画效果。
展示列表排序动画的实现。
<template>
<div>
<button @click="shuffle">随机排序</button>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
methods: {
shuffle() {
this.items = _.shuffle(this.items)
}
}
}
</script>
<style>
.flip-list-move {
transition: transform 0.8s ease;
}
</style>
实现数字的平滑过渡效果。
展示数字过渡的实现。
<template>
<div>
<input v-model.number="number" type="number" step="20">
<p>{{ animatedNumber }}</p>
</div>
</template>
<script>
import gsap from 'gsap'
export default {
data() {
return {
number: 0,
tweenedNumber: 0
}
},
computed: {
animatedNumber() {
return this.tweenedNumber.toFixed(0)
}
},
watch: {
number(newValue) {
gsap.to(this, {
duration: 0.5,
tweenedNumber: newValue
})
}
}
}
</script>
实现颜色的平滑过渡效果。
展示颜色过渡的实现。
<template>
<div>
<input v-model="colorQuery" placeholder="输入颜色">
<div class="color-box" :style="{ backgroundColor: tweenedCSSColor }"></div>
</div>
</template>
<script>
import gsap from 'gsap'
import Color from 'color'
export default {
data() {
return {
colorQuery: '',
color: {
red: 0,
green: 0,
blue: 0,
alpha: 1
},
tweenedColor: {}
}
},
computed: {
tweenedCSSColor() {
return new Color({
red: this.tweenedColor.red,
green: this.tweenedColor.green,
blue: this.tweenedColor.blue,
alpha: this.tweenedColor.alpha
}).rgb().string()
}
},
watch: {
colorQuery(newValue) {
const color = Color(newValue)
if (color.isValid()) {
this.color = {
red: color.red(),
green: color.green(),
blue: color.blue(),
alpha: color.alpha()
}
}
},
color: {
handler(newValue) {
gsap.to(this.tweenedColor, {
duration: 0.5,
...newValue
})
},
deep: true
}
},
created() {
this.tweenedColor = { ...this.color }
}
}
</script>
<style>
.color-box {
width: 100px;
height: 100px;
margin-top: 10px;
}
</style>
优化动画性能的技巧和方法。
展示动画性能优化的实现。
<template>
<div>
<button @click="show = !show">切换</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
:css="false"
>
<div v-if="show" class="box"></div>
</transition>
</div>
</template>
<script>
import gsap from 'gsap'
export default {
data() {
return {
show: true
}
},
methods: {
beforeEnter(el) {
el.style.transform = 'translateX(-100px)'
el.style.opacity = 0
},
enter(el, done) {
gsap.to(el, {
x: 0,
opacity: 1,
duration: 0.5,
ease: 'power2.out',
onComplete: done,
// 使用 will-change 优化性能
onStart: () => {
el.style.willChange = 'transform, opacity'
},
onComplete: () => {
el.style.willChange = 'auto'
done()
}
})
},
leave(el, done) {
gsap.to(el, {
x: 100,
opacity: 0,
duration: 0.5,
ease: 'power2.in',
onComplete: done
})
}
}
}
</script>
<style>
.box {
width: 100px;
height: 100px;
background-color: #42b983;
/* 使用 transform 代替位置属性 */
transform: translateZ(0);
/* 启用硬件加速 */
backface-visibility: hidden;
perspective: 1000px;
}
</style>
实现复杂的动画组合效果。
展示动画组合的实现。
<template>
<div>
<button @click="show = !show">切换</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
:css="false"
>
<div v-if="show" class="card">
<div class="card-content">
<h3>标题</h3>
<p>内容</p>
</div>
</div>
</transition>
</div>
</template>
<script>
import gsap from 'gsap'
export default {
data() {
return {
show: true
}
},
methods: {
beforeEnter(el) {
gsap.set(el, {
scale: 0.8,
opacity: 0
})
},
enter(el, done) {
const tl = gsap.timeline({
onComplete: done
})
tl.to(el, {
scale: 1,
opacity: 1,
duration: 0.5,
ease: 'back.out(1.7)'
})
.to('.card-content', {
y: 0,
opacity: 1,
duration: 0.3,
ease: 'power2.out'
}, '-=0.2')
},
leave(el, done) {
const tl = gsap.timeline({
onComplete: done
})
tl.to('.card-content', {
y: 20,
opacity: 0,
duration: 0.2,
ease: 'power2.in'
})
.to(el, {
scale: 0.8,
opacity: 0,
duration: 0.3,
ease: 'back.in(1.7)'
}, '-=0.1')
}
}
}
</script>
<style>
.card {
width: 300px;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform-origin: center;
}
.card-content {
transform: translateY(20px);
opacity: 0;
}
h3 {
margin: 0 0 10px 0;
}
p {
margin: 0;
color: #666;
}
</style>
建议按照章节顺序学习,每完成一个练习后再进行下一个。可以通过创建实际项目来实践所学知识。特别注意动画性能优化和用户体验。