这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战
系列文章
本文介绍 Vue 3 的元素进入(显示)/离开(隐藏)和列表(重排)的过渡动效实现方法,主要针对 🎉 与 Vue 2 的不同点。
Vue 支持的过渡动效主要是针对元素素进入(显示)/离开(隐藏)和列表(重排)的情况,基于 CSS3 的 transition
和 animation
属性实现的。
💡 对于其他过渡动效,以及在 web 上创建流畅的动画所需考虑的性能因素,可以参考官方文章这一章。
Vue 中常见的触发动画的情况:
- 条件渲染
v-if
或v-show
切换,动态组件属性is
切换 - 列表更新
- 状态更新
元素进入/离开过渡
Vue 提供内置组件 <transition name="animationType"></transition>
作为容器,然后就可以有多种方法为包裹在其中的(直接子元素)元素设置进入/离开时的动画:
- 自动为发生过渡的元素添加特定的
class
(并在结束后移除相应的class
属性),可以基于这些class
属性使用 CSS 实现动画。而且可以定制class
的属性值,便于集成第三方 CSS 动画库,如 animate.css - 在发生过渡同时触发相应的过渡钩子函数,便于通过 JS 实现动画,也可以集成第三方 JavaScript 动画库
CSS 过渡
Vue 会在过渡的不同阶段,为元素插入 6 种不同后缀的 class
属性,可以使用这些 class
作为选择器,更精细地设置元素的在不同过渡阶段的样式:
- 🎉
v-enter-from
定义进入过渡的开始状态,在元素被插入之前生效,在元素被插入之后的下一帧移除。 v-enter-active
在整个进入过渡的阶段中应用,在过渡/动画完成之后移除。可以使用这个类名作为 CSS 选择器设置过渡效果,例如通过设置 CSS 属性transition
定义进入过渡的过程时间,延迟或曲线函数。v-enter-to
定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter-from
被移除),在过渡/动画完成之后移除。- 🎉
v-leave-from
定义离开过渡的开始状态,在离开过渡被触发时立刻生效,下一帧被移除。 v-leave-active
在整个离开过渡的阶段中应用,在过渡/动画完成之后移除。可以使用这个类名作为 CSS 选择器设置过渡效果,例如通过设置 CSS 属性transition
定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave-from
被删除),在过渡/动画完成之后移除。
💡 以上 6 种不同后缀的 class
属性都使用了 v-
作为默认前缀,如果容器标签设置了属性 name
即 <transition name="animationType">
,则动态插入的 class
属性名称就会以 animationType-
为前缀,即 v-enter
会替换为 animationType-enter
,记得设置样式时使用相应的前缀,而不是默认前缀 v-
。
<div id="demo">
<button @click="show = !show">
Toggle show
</button>
<transition name="slide-fade">
<p v-if="show">hello</p>
</transition>
</div>
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
/* 可以设置不同的进入和离开动画 */
/* 设置持续时间和动画函数 */
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
如果要用第三方的 CSS 动画库,如 Animate.css,一般需要在 DOM 元素上插入特定的类名来应用相应的 CSS 样式,可以在标签 <transition></transition>
上使用如下列出的 attribute,这样 Vue 会在过渡的相应阶段为容器内的变更的(直接子元素)元素添加相应的自定义过渡类名:
- 🎉
enter-from-class="custom-class-name"
在元素进入页面前添加的类名 enter-active-class="custom-class-name"
enter-to-class="custom-class-name"
- 🎉
leave-from-class="custom-class-name"
leave-active-class="custom-class-name"
leave-to-class="custom-class-name"
<link
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
rel="stylesheet"
type="text/css"
/>
<div id="demo">
<button @click="show = !show">
Toggle render
</button>
<transition
name="custom-classes-transition"
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
💡 如果使用 animation
属性实现动效,而不是 transition
控制过渡,则不需要指定 v-enter-from
/v-leave-from
或 v-enter-to
/v-leave-to
的状态,只需要在 v-enter-active/v-leave-active
过渡中设定 CSS 属性 animation
即可,其中在关键帧中指定了 0%
和 100%
状态时的样式就是开始和结束的状态
<div id="demo">
<button @click="show = !show">
Toggle show
</button>
<transition name="bounce">
<p v-if="show">hello</p>
</transition>
</div>
const Demo = {
data() {
return {
show: true
}
}
}
Vue.createApp(Demo).mount('#demo')
.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);
}
}
💡 Vue 可以自动得出过渡效果的完成时机,以「拔除」添加到元素上的相关的 class
类名,但是如果(不推荐)同时使用了 CSS 的属性 trasition
和 animation
设置动画,可能会因为两种过渡时间不同,比如 animation
很快的被触发并完成了,而 transition
效果还没结束,而出现一些意想不到的动画 Bug,这时候可以为容器添加选项 type
并设置值为 animation
或 transition
来显式地声明你需要 Vue 监听的过渡动画类型;也可以在容器元素上通过 prop 显式地指定持续时间(以毫秒计) <transition :duration="1000">
。
💡 动效如果在初次加载页面时不生效,可以为元素 <transition>
添加属性 appear
设置节点初始渲染的过渡。
钩子函数
Vue 会在过渡的不同阶段同时触发相应事件,可以在内置组件 <transition>
监听这些事件,然后通过 JS 在事件处理函数中「手动」控制动画,一般是设置元素的 attribute 样式,或「对接」其他动画框架,例如 gsap:
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
:css="false"
>
<!-- ... -->
</transition>
// ...
methods: {
/*
* 元素进入
*/
beforeEnter(el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter(el, done) {
// ...
done()
},
afterEnter(el) {
// ...
},
enterCancelled(el) {
// ...
},
/*
* 元素离开
*/
beforeLeave(el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave(el, done) {
// ...
done()
},
afterLeave(el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled(el) {
// ...
}
}
@before-enter="beforeEnter(el)"
可以传递执行动画的元素@enter="enter(el, done)"
需要在回调函数中,在动画执行执行完成时调用done()
告知 Vue 动画结束,必须执行一次done()
,否则它们将被同步调用,过渡会立即完成。@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
元素离开页面触发的事件@before-leave="beforeLeave"
@leave="leave"
需要在回调函数中,在动画执行执行完成时调用done()
告知 Vue 动画结束,必须执行一次done()
,否则它们将被同步调用,过渡会立即完成。@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
💡 虽然这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。如果仅使用 JS 过渡,推荐为 <transition>
添加 v-bind:css="false"
属性绑定,Vue 会跳过 CSS 的检测,这也可以避免过渡过程中受 CSS 的影响。
多个元素间过渡
在多元素过渡时,默认模式是两个元素的进入&离开是同时进行的,可能会导致页面布局乱掉,可以将过渡模式改成 <transition mode="out-in"></transition>
让当前元素先进行过渡离开,完成之后新元素过渡进入。
<transition name="fade" mode="out-in">
<!-- ... the buttons ... -->
</transition>
💡 对于使用 v-if
/v-else
实现的多元素之间进行切换时,🎉 Vue 3 已经默认为 v-if
/v-else
/v-else-if
的各分支项自动生成唯一的 key
,因此这些元素的 key
属性不再必要了,如果自己添加 key
属性时需要设置唯一可区分的值,否则相同标签名的元素之间切换时只会替换内部的内容,而不会进行触发整个元素切换过渡。
列表重排过渡
使用组件 <transition-group></transition-group>
作为容器,其内可以有多个节点(而 <transition>
只允许一个根节点,虽然可以包裹多个元素,但是每次 v-if
或 v-show
切换后,都只能渲染一个根节点),要记得其内部的各个节点总是需要提供属性 key
作为唯一标识。
<transition-group name="list" tag="p">
<span v-for="item in items" :key="item" class="list-item">
{{ item }}
</span>
</transition-group>
Vue 也是在过渡的不同阶段,为元素插入以上所述的 6 个不同后缀 class
,可以使用这些 class
作为选择器设置样式。
- 🎉
v-enter-from
v-enter-active
v-enter-to
- 🎉
v-leave-from
v-leave-active
v-leave-to
此外还特别针对性地新增了后缀为 v-move
的 class
类属性(如果设置了 name
属性 <transition-group name="animationType"></transition-group>
,则列表重排时插入到列表项的 class
类属性的前缀就会改变,即为 animationType-move
;还可以像之前的类名一样,即通过 move-class
attribute 手动设置)。一般会通过 v-move
这个选择器设置 CSS 属性 transform
,指定过渡 timing 和 easing 曲线,然后 Vue 会将元素从之前的位置平滑过渡新的位置,实现一个称为 FLIP 的动画效果。
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<div id="flip-list-demo">
<button @click="shuffle">Shuffle</button>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</transition-group>
</div>
const Demo = {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
},
methods: {
shuffle() {
// 使用 lodash 的 shuffle 对列表元素进行重排
this.items = _.shuffle(this.items)
}
}
}
Vue.createApp(Demo).mount('#flip-list-demo')
.flip-list-move {
transition: transform 0.8s ease;
}
💡 🎉 在 Vue 3 中,<transition-group>
默认不再渲染根节点,但可以通过属性 tag
来设置,例如 tag="div"
将使用 <div>
元素作为容器包装列表。
💡 FLIP 动效对于 display: inline
元素无效,对于行内元素,如 <span>
,可以设置为 display: inline-block
动态过渡
Vue 是数据驱动画面的,因此过渡动效的类型也是可以基于数据进行变化的,如通过 <transition :name="transitionName">
将属性 name 绑定到变量,就可以实时操作动效的类型,因为事件钩子是方法,它们可以访问任何上下文中的数据,这意味着根据组件的状态不同,过渡可以有不同的表现,例如实现轮播图点击左右箭头时可以实现切换方向和过渡的不同。
状态过渡
对于元素的内容/数据本身的改变也可以设置过渡动效,包括数字和运算、颜色的显示、SVG 节点的位置、元素的大小和其他的 property。
一般以数值表示的样式属性都可以设置动效,可以结合 Vue 的响应式和组件系统,使用第三方库来实现切换元素的过渡状态,如 tween.js、gsap
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: Benbinbin 原文链接:https://juejin.im/post/6992476452721524750