在 Vue 中,为了方便操作 DOM 的过渡和动画,Vue 封装了一个 transition 组件,以 Vue 专属标签的形式使用,在下列情形中,可以给任意元素或组件添加过渡/动画
v-if
)v-show
)当插入或删除在 transition
组件中的元素时,Vue 会做以下处理:
transition
元素应用了 CSS 过渡 / 动画,在适当的时机添加或移除类名一般 transition
的使用,可以分为两个状态:
v-if
/ v-show
的值从 false
变为 true
的过程v-if
/ v-show
的值从 true
变为 false
的过程根据官方文档,过渡的类名一共有 6 个:
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧被移除v-enter-active
:定义进入过渡生效时的状态。再整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成后被移除。这个类可以定义进入过渡的时间、延迟和速度曲线v-enter-to
:定义进入过渡的结束状态,在元素被插入的下一帧生效(此时 v-enter
被移除),在过渡/动画完成后被移除v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成后被移除。这个类可以定义离开过渡的时间、延迟和速度曲线v-leave-to
:定义离开过渡的结束状态。在离开过渡被触发下一帧生效(此时 v-leave
被移除),在过渡/动画完成后移除前三项属于过渡进入,后三项属于过渡离开。
官方已经明确了这 6 个类名的执行时间,下方的图可以更好地方便理解:
对于这些过渡的类名,如果 transition
标签没有名字,则 v-
是这些类名的前缀,如果 transition
有名字,例如:name="my-transition"
,那么 v-
将会被替换为 my-transition-
,例如:my-transition-enter
。
举个简单的案例:
<template>
<div>
<button type="button" @click="show = !show">往返动画按钮button>
<transition name="my-transition">
<p v-show="show">我是要进行往返动画的文字p>
transition>
div>
template>
<script>
export default {
data() {
return {
show: true
};
}
};
script>
<style>
.my-transition-leave-to,
.my-transition-enter {
opacity: 0;
}
.my-transition-enter-active,
.my-transition-leave-active {
transition: opacity 1s;
}
style>
使用 JavaScript 钩子函数,首先需要在 transition
元素上绑定一些 attribute
:
v-on:before-enter
/ @before-enter
表示进入动画开始之前,此时,动画尚未开始,可以在此处设置动画的初始样式
v-on:enter
/ @enter
表示进入动画开始之后,在这里设置动画开始之后的结束状态,还可以调用动画结束之后的回调函数
v-on:after-enter
/ @after-enter
表示进入动画结束之后的回调函数
v-on:enter-cancalled
/ @enter-cancalled
v-on:before-leave
/ @before-leave
表示离开动画开始之前
v-on:leave
/ @leave
表示离开动画开始之后
v-on:after-leave
/ @after-leave
离开动画结束后得回调
v-on:leave-cancalled
/ @leave-cancalled
只适用于 v-show
,过渡停止
前 4 项属于过渡进入,后 4 项属于过渡离开。
举个实例:
<template>
<div>
<button type="button" @click="show = !show">动画按钮button>
<transition
name="my-transition"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
>
<p v-show="show">我是要进行半程动画的文字p>
transition>
div>
template>
<script>
export default {
data() {
return {
show: false
};
},
methods: {
// 参数 el 是绑定对象的 DOM
beforeEnter(el) {
console.log("开始");
el.style.opacity = 0;
el.style.transform = "translateX(0)";
},
// done 其实就是 afterEnter,这里的 done 函数是对 afterEnter 的引用
enter(el, done) {
console.log("进行中");
// el.offsetWidth 会强制动画刷新,没有实际的作用,但是,如果不写,出不来动画效果;
el.offsetWidth;
el.style.opacity = 1;
el.style.transform = "translateX(100px)";
el.style.transition = "all 1s";
el.addEventListener("transitionend", done);
},
afterEnter(el) {
console.log("结束-并恢复");
this.show = false;
}
}
};
script>
<style>
style>
那么这里就会有一个问题,问什么没有 el.offsetWidth
就没有动画效果呢?从网上查阅到资料可知,根本原因是因为浏览器在更新视图的时候,会在当前 js 对 style
进行修改之后,对所有更改统一进行更新,这时,v-show
的 display: none
(不支持 transition
效果)会对其他支持 transition
效果的属性有影响,导致过渡效果失效。解决方法有两种:
el.offsetWidth
,因为这会引起 CSS 的重绘,导致被操作元素的 display
属性先一步变为 block
。setTimeout
中,利用异步使 display
属性变化先生效。transition
的过渡类名,支持我们用自定义的方式进行声明。我们可以通过以下这些 transition
标签的 attribute
来自定义过渡的类名:
enter-class
enter-alive-class
enter-to-class
leave-class
leave-alive-class
leave-to-class
他们的优先级高于普通类名,这对于 Vue.js 的过渡系统,和其他第三方 css 动画库结合使用十分方便,例如 Animate.css 。
以上说到的几种动画,都是需要执行某种操作之后,动画效果才能出发,那么有没有页面加载完毕后就能立即出发的动画呢?除了 animation
之外,还可以使用 transition
标签的 appear
属性。可以使用自定义类名和 JavaScript 钩子函数两种设置方式。
自定义类名:
<template>
<div>
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class"
appear-active-class="custom-appear-active-class"
>
<p>我是初始化动画文字p>
transition>
div>
template>
<script>
export default {
};
script>
<style>
.custom-appear-class {
transform: translateX(100px);
}
.custom-appear-active-class {
transition: all 0.5s;
}
style>
JavaScript 钩子函数:
<template>
<div>
<transition
appear
@before-appear="beforeAppear"
@appear="appear"
@after-appear="afterAppear"
@appear-cancelled="appearCancelled"
>
<p>我是初始化动画文字p>
transition>
div>
template>
<script>
export default {
methods: {
beforeAppear(el) {
},
appear(el, done) {
el.offsetWidth;
el.style.transform = "translateX(100px)";
el.style.transition = "all 1s";
el.addEventListener("transitionend", done);
},
afterAppear(el) {
},
appearCancelled(el) {
}
}
};
script>
<style>style>
对于在多个元素之间相互切换的过程中,也可以使用 transition 设置过渡动画:
<transition name="my-transition">
<p @click="show = !show" v-if="show">onbutton>
<button @click="show = !show" v-else>offbutton>
transition>
值得注意的是,如果进行切换的几个标签名相同,需要利用 key
属性来让 Vue 区分它们,否则,Vue 为了效率只会替换相同标签内部的内容:
<transition name="my-transition">
<button @click="show = !show" v-if="show" key="on">onbutton>
<button @click="show = !show" v-else key="off">offbutton>
transition>
在切换过程中,还会引出另一个问题,两个元素在切换过程中,一个离开过渡的时候另一个开始进入过渡。这是因为 transition
的默认行为 - 进入和离开同时发生。显然,同时生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了过渡模式 mode
属性:
in-out
:先进行过渡进入,完成之后再进行过渡离开。out-in
:先进行过渡离开,完成之后再进行过渡进入。例如:
<transition name="my-transition" mode="out-in">
<button @click="show = !show" v-if="show" key="on">onbutton>
<button @click="show = !show" v-else key="off">offbutton>
transition>
相对于多元素过渡,多组件过渡就显得简单很多,我们不需要使用 key
属性进行区分,可以使用动态组件:
<transition name="my-transition" mode="out-in">
<component :is="flag ? component1 : component2">component>
transition>
目前为止,关于过渡我们已经讨论了:
那么怎么同时渲染整个列表,比如使用 v-for
?在这种场景中,要使用 transition-group
。使用之前,要先注意以下几点:
transition
,它会以一个真实元素呈现:默认为一个 span
。可以通过 tag
属性更换为其他元素。在列表过渡中,进入和离开的过渡依旧使用之前一样的 CSS 类名。例如:
<template>
<div>
<button @click="add">Addbutton>
<button @click="remove">Removebutton>
<transition-group name="list" tag="p">
<span v-for="item in items" :key="item" class="list-item">{
{ item }}span>
transition-group>
div>
template>
<script>
export default {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
};
},
methods: {
randomIndex: function() {
return Math.floor(Math.random() * this.items.length);
},
add: function() {
this.items.splice(this.randomIndex(), 0, this.nextNum++);
},
remove: function() {
this.items.splice(this.randomIndex(), 1);
}
}
};
script>
<style>
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
.list-enter-active,
.list-leave-active {
transition: all 0.5s;
}
style>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move
类名,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name
属性来自定义前缀,也可以通过 move-class
属性手动设置。
v-move
对于设置过渡的切换时机和过渡曲线非常有用,你会看到如下的例子:
<template>
<div>
<button v-on:click="shuffle">shufflebutton>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item" class="list-item">{
{ item }}li>
transition-group>
div>
template>
<script>
export default {
data() {
return {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9]
};
},
methods: {
shuffle: function() {
this.items = this.items.sort(() => Math.random() - 0.5);
}
}
};
script>
<style>
.list-move {
transition: transform 1s;
}
style>