各位观众老爷,大家好!我是今天的主讲人,一个和BUG打了半辈子交道的码农老司机。今天咱们聊聊Vue里那些让界面动起来的小魔法——动画和过渡效果。
引子:静态页面,要你何用?
想想看,如果所有的网站都像PPT一样,一点就蹦出来,毫无缓冲,你会不会觉得很枯燥?动画和过渡效果就像是给冰冷的程序代码注入了灵魂,让用户体验蹭蹭上涨。Vue提供了非常方便的方式来实现这些效果,让咱们用起来事半功倍。
正文:Transition 组件——单元素动画的秘密武器
首先,隆重介绍我们的主角:<transition>
组件。这家伙专门负责处理单个元素的进入、离开和改变状态时的动画。
1. 基本用法:给元素穿上“动画衣裳”
最简单的用法就是把你想加动画的元素用<transition>
包裹起来:
<template>
<div>
<button @click="show = !show">Toggle</button>
<transition name="fade">
<p v-if="show">Hello, Animation!</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: false
};
},
};
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>
这段代码做了什么?
- 点击按钮,
show
变量在true
和false
之间切换。 <transition name="fade">
告诉Vue,当p
元素出现或消失时,使用fade
这个名称来查找相关的CSS类。- CSS部分定义了动画的具体效果:
.fade-enter-active
和.fade-leave-active
:在元素进入/离开的整个过程中,这个类都会被应用。这里我们定义了opacity
的过渡效果。.fade-enter-from
:元素进入之前的状态。这里我们设置opacity: 0
,表示元素一开始是完全透明的。.fade-leave-to
:元素离开之后的状态。同样,我们设置opacity: 0
,表示元素最终会完全透明。
2. Transition 组件的生命周期钩子:动画的精细控制
<transition>
组件还提供了一系列的JavaScript钩子,让你在动画的不同阶段执行自定义逻辑。这些钩子函数在元素的进入和离开过程中会被调用:
钩子名称 | 调用时机 | 参数 |
---|---|---|
before-enter |
元素插入到DOM之前 | el: Element – 被过渡的元素 |
enter |
元素插入到DOM之后 | el: Element, done: Function – 被过渡的元素,done 是可选的回调函数,用于手动控制过渡完成。如果使用了done ,那么你需要手动调用done() 来结束过渡。 |
after-enter |
元素进入过渡完成之后 | el: Element – 被过渡的元素 |
enter-cancelled |
进入过渡被取消时 | el: Element – 被过渡的元素 |
before-leave |
离开过渡开始之前 | el: Element – 被过渡的元素 |
leave |
离开过渡开始时 | el: Element, done: Function – 被过渡的元素,done 是可选的回调函数,用于手动控制过渡完成。如果使用了done ,那么你需要手动调用done() 来结束过渡。 |
after-leave |
元素离开过渡完成之后 | el: Element – 被过渡的元素 |
leave-cancelled |
离开过渡被取消时 | el: Element – 被过渡的元素 |
举个例子,我们可以利用before-enter
钩子来在元素进入之前设置一些初始状态:
<template>
<div>
<button @click="show = !show">Toggle</button>
<transition
name="slide"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<p v-if="show">Hello, Slide Animation!</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: false
};
},
methods: {
beforeEnter(el) {
el.style.transform = 'translateX(-100%)'; // 元素初始位置在屏幕左侧
},
enter(el, done) {
// 强制重绘,触发过渡
el.offsetWidth;
el.style.transition = 'transform 0.5s';
el.style.transform = 'translateX(0)'; // 移动到屏幕中央
el.addEventListener('transitionend', done); // 过渡结束后调用done()
},
leave(el, done) {
el.style.transition = 'transform 0.5s';
el.style.transform = 'translateX(-100%)';
el.addEventListener('transitionend', done);
}
}
};
</script>
这段代码实现了一个从左侧滑入滑出的动画:
beforeEnter
:在元素插入DOM之前,将其移动到屏幕左侧。enter
:el.offsetWidth;
这一行非常重要,它强制浏览器重绘,从而触发过渡效果。这是因为在beforeEnter
中设置了元素的初始位置,如果不强制重绘,浏览器可能会优化掉这个初始状态,导致没有过渡效果。- 设置
transition
属性,定义过渡效果。 - 将元素移动到屏幕中央。
- 监听
transitionend
事件,在过渡结束后调用done()
,告诉Vue过渡已经完成。
3. Transition 的工作原理:状态机的秘密
<transition>
组件实际上维护了一个简单的状态机,它会根据元素的进入和离开状态,自动添加和移除CSS类名。这些类名遵循一定的命名规范:
v-enter-from
: 定义进入过渡的开始状态。在元素插入DOM之前添加,在元素插入DOM之后下一帧移除。v-enter-active
: 定义进入过渡生效时的状态。在整个进入过渡的阶段都应用,在元素插入DOM之前添加,在过渡完成之后移除。 这个类可以被用来定义过渡的过程时间,延迟和曲线函数。v-enter-to
: 定义进入过渡的结束状态。在元素插入DOM之后下一帧添加 (与此同时v-enter-from
被移除),在过渡完成之后移除。
类似的,离开过渡也有对应的类名:
v-leave-from
: 定义离开过渡的开始状态。在离开过渡被触发时立刻添加,下一帧被移除。v-leave-active
: 定义离开过渡生效时的状态。在整个离开过渡的阶段都应用,在离开过渡被触发时立刻添加,在过渡完成之后移除。 这个类可以被用来定义过渡的过程时间,延迟和曲线函数。v-leave-to
: 定义离开过渡的结束状态。在一个离开过渡被触发后下一帧添加 (与此同时v-leave-from
被移除),在过渡完成之后移除。
如果指定了name
属性,例如name="fade"
,那么这些类名会变成fade-enter-from
、fade-enter-active
、fade-enter-to
等等。
TransitionGroup 组件——多元素动画的舞台
当我们需要对多个元素进行动画时,<transition>
组件就有点力不从心了。这时候,就需要<transition-group>
组件出场了。
1. 基本用法:列表动画的福音
<transition-group>
组件可以用于包裹一个列表,当列表中的元素发生增删改时,可以为这些元素添加动画。
<template>
<div>
<button @click="addItem">Add Item</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
],
nextId: 3,
};
},
methods: {
addItem() {
this.items.push({ id: this.nextId++, text: 'Item ' + this.nextId });
}
}
};
</script>
<style>
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter-from, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
.list-move {
transition: transform 1s;
}
</style>
这段代码实现了一个简单的列表添加动画:
<transition-group name="list" tag="ul">
:name="list"
:指定动画名称。tag="ul"
:指定<transition-group>
渲染成哪个HTML标签。如果不指定,默认渲染成<span>
。
v-for
循环渲染列表项,必须指定key
属性,这是Vue区分不同元素的关键。- CSS部分:
.list-enter-active
和.list-leave-active
:定义进入和离开动画的过渡效果。.list-enter-from
和.list-leave-to
:定义进入和离开动画的初始和结束状态。.list-move
:这个类非常重要,它定义了元素在列表中的位置发生变化时的动画效果。 当列表中的元素因为其他元素的增删而需要移动位置时,Vue会自动应用这个类。
2. TransitionGroup 的工作原理:维护一个虚拟DOM diff
<transition-group>
组件的原理稍微复杂一些,它主要做了以下几件事:
- 创建一个虚拟DOM diff: 当列表中的元素发生变化时,
<transition-group>
会创建一个新的虚拟DOM,并与之前的虚拟DOM进行比较,找出哪些元素被添加、删除或移动了位置。 - 应用CSS类: 根据diff的结果,
<transition-group>
会为不同的元素应用不同的CSS类:- 对于新增的元素,应用
v-enter-from
、v-enter-active
和v-enter-to
类。 - 对于删除的元素,应用
v-leave-from
、v-leave-active
和v-leave-to
类。 - 对于移动了位置的元素,应用
v-move
类。
- 对于新增的元素,应用
- 使用
transform
进行动画: 为了实现平滑的移动动画,<transition-group>
会使用transform
属性来改变元素的位置。这是因为直接改变元素的top
、left
等属性可能会导致回流,影响性能。transform
属性可以利用GPU加速,性能更好。
3. 列表排序动画:进阶用法
<transition-group>
组件还可以实现列表排序动画。例如,我们可以实现一个拖拽排序的列表:
<template>
<div>
<ul class="list" @dragstart="dragStart" @dragover.prevent @drop="drop">
<transition-group name="list" tag="ul">
<li
v-for="(item, index) in items"
:key="item.id"
draggable="true"
:data-index="index"
>
{{ item.text }}
</li>
</transition-group>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' },
],
draggingIndex: null,
};
},
methods: {
dragStart(event) {
this.draggingIndex = event.target.dataset.index;
},
drop(event) {
const dropIndex = event.target.dataset.index;
if (this.draggingIndex === dropIndex) {
return;
}
const draggedItem = this.items[this.draggingIndex];
this.items.splice(this.draggingIndex, 1);
this.items.splice(dropIndex, 0, draggedItem);
this.draggingIndex = null;
}
}
};
</script>
<style>
.list {
list-style: none;
padding: 0;
}
.list li {
padding: 10px;
border: 1px solid #ccc;
margin-bottom: 5px;
cursor: move;
}
.list-move {
transition: transform 0.5s;
}
</style>
这段代码实现了一个可拖拽排序的列表:
- 给
li
元素添加draggable="true"
属性,使其可以被拖拽。 - 监听
dragstart
事件,记录被拖拽元素的索引。 - 监听
drop
事件,获取放置元素的索引,并更新items
数组,实现排序。 .list-move
类定义了元素移动时的动画效果。
总结:动画的艺术,在于细节
Vue的<transition>
和<transition-group>
组件为我们提供了强大的动画和过渡效果支持。掌握它们,可以为你的应用增添活力,提升用户体验。
记住,动画的艺术在于细节。精心设计的动画可以给人留下深刻的印象,而粗糙的动画则会让人感到不适。因此,在设计动画时,要多考虑用户的感受,力求做到自然、流畅、易于理解。
好了,今天的讲座就到这里。希望大家有所收获!下次再见!