Vue 在插入、更新或者移除 DOM 时,提供了多种不同方式的应用过渡效果。包括以下工具:
在文章底部有本博客演示代码的下载链接,大家可以边测试使用边学习。
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入 / 离开过渡:
v-if
)v-show
)<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1</title>
<style>
.fade-enter {
opacity: 0;
}
.fade-enter-active {
/* 指定当 opacity 的属性改变时执行 transition 过渡效果 */
transition: opacity 1s;
color: red;
}
.fade-leave-to {
opacity: 0;
}
.fade-leave-active {
transition: opacity 1s;
color: green;
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<h2 v-if="show">{{message}}</h2>
</transition>
<button @click="btnClick">切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message:"hello",
show: true
},
methods: {
btnClick() {
this.show = !this.show;
}
}
});
</script>
</body>
</html>
复制代码
解释: 当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:
transition
是 vue 内置的一个组件,我们可以直接使用。
<transition>
元素作为单个元素 / 组件的过渡效果。<transition>
只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中。
注意:<transition>
只能用来包裹单个元素,如果包裹了多个元素,会报错:
<!-- 会报错 -->
<transition name="fade">
<h2 v-if="show">hello</h2>
<h2 v-if="show">webchang</h2>
</transition>
复制代码
<!-- 这样就不会报错了 -->
<transition name="fade">
<h2 v-if="show">hello</h2>
<h2 v-else>webchang</h2>
</transition>
<!-- 或者这样 -->
<transition name="fade">
<div>
<h2 v-if="show">hello</h2>
<h2>world</h2>
</div>
</transition>
复制代码
如果要包裹多个元素,需要使用 <transition-group>
name
:string类型,用于自动生成 CSS 过渡类名。例如:name: 'fade'
将自动拓展为 .fade-enter
,.fade-enter-active
等。如果没有定义 name ,默认的类名前缀为 "v"
,例如 .v-enter
appear
:boolean类型,表示是否在初始渲染时使用过渡。默认为 false。type
:string类型,指定过渡事件类型,侦听过渡何时结束。有效值为 "transition"
和 "animation"
。默认 Vue.js 将自动检测出持续时间长的为过渡事件类型。mode
:string类型,控制离开/进入过渡的时间序列。有效的模式有 "out-in"
和 "in-out"
;默认同时进行。duration
:number类型或者对象类型:{ enter: number, leave: number } ,用来指定过渡的持续时间。默认情况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件。下边几个 prop 都是string类型,用来自定义过渡的类名:
以上介绍的 prop 和 事件在下文进行讲解。
<!-- 简单使用 -->
<transition>
<div v-if="ok">toggled content</div>
</transition>
<!-- 使用动态组件实现组件过渡 -->
<transition name="fade" mode="out-in" appear>
<component :is="view"></component>
</transition>
<!-- 使用事件钩子 -->
<div id="transition-demo">
<transition @after-enter="transitionComplete">
<div v-show="ok">toggled content</div>
</transition>
</div>
复制代码
在进入 / 离开的过渡中,会有 6 个 class 切换。
进入时:
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡 / 动画完成之后移除。 这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:vue 2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter
被移除),在过渡 / 动画完成之后移除。离开时:
v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡 / 动画完成之后移除。 这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:vue 2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave
被删除),在过渡 / 动画完成之后移除。对于这些在过渡中切换的类名来说,如果使用了一个没有名字(没有 name 属性)的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="fade">
,那么 v-enter
会替换为 fade-enter
。
所以实现过渡的原理就是通过在某一时刻给 transition
包裹的的元素上动态添加和删除 class 类名的方式来实现。
我们可以通过在 transition
上添加以下 attribute
来自定义过渡类名:
enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。如果某个过渡时期的类名没有自定义,那么用的还是原来的类名;如果被自定义了,添加动画和样式时要使用改动后的类名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2</title>
<style>
.fade-enter {
opacity: 0;
}
/* 使用自定义的名字 */
.active {
transition: opacity 1s;
color: red;
}
.fade-leave-to {
opacity: 0;
}
/* 使用自定义的名字 */
.leave {
transition: opacity 1s;
color: green;
}
</style>
</head>
<body>
<div id="app">
<!-- 自定义class名字 -->
<transition name="fade"
enter-active-class="active"
leave-active-class="leave">
<h2 v-if="show">{{message}}</h2>
</transition>
<button @click="btnClick">切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "hello",
show: true
},
methods: {
btnClick() {
this.show = !this.show;
}
}
});
</script>
</body>
</html>
复制代码
<link href="https://cdn.bootcdn.net/ajax/libs/animate.css/3.5.2/animate.css" rel="stylesheet">
<div id="app">
<transition name="fade"
enter-active-class="animated bounceInDown"
leave-active-class="animated bounceOutUp">
<h2 v-if="show">hello webchang</h2>
</transition>
<button @click="show = !show">切换</button>
</div>
复制代码
要在 transition
上使用自定义类名的形式
第一步,引入 animate.css
文件
第二步,给指定的元素加上指定的动画样式名。第一个 animated
是必须添加的样式名,第二个是指定的动画样式名。
<div class="animated fadeInUp"></div>
复制代码
具体想要实现什么效果的动画,可以去官网查看:Animate.css 官网
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4</title>
<link href="https://cdn.bootcdn.net/ajax/libs/animate.css/3.5.2/animate.css" rel="stylesheet">
<style>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 2s;
}
</style>
</head>
<body>
<div id="app">
<!-- 自定义class名字 -->
<!-- type="transition"指的是以 transition 的动画时长为总时长 -->
<transition name="fade"
type="transition"
enter-active-class="animated bounceInDown fade-enter-active"
leave-active-class="animated bounceOutUp fade-leave-active">
<h2 v-if="show">{{message}}</h2>
</transition>
<button @click="show = !show">切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "hello",
show: true
}
});
</script>
</body>
</html>
复制代码
在一些场景中,我们需要给同一个元素同时设置两种过渡动效,比如 animation
指定的动画很快的被触发并完成了,而 transition
的过渡效果还没结束。此时,我们可以使用 type
属性,设置 animation
或 transition
来明确声明你需要 Vue 监听的类型。上边案例中的 type="transition"
表示以 transition 的过渡时长为总的动画时长,虽然 animation 早早的就结束了,但是 transition 还没有结束,所以绑定在元素上的类名没有立马消失,而是停留了一段时间,等待 transition 过渡的执行结束。
可以用 <transition>
组件上的 duration
属性定制一个显性的过渡持续时间 (以毫秒计):
<!-- 表示进入和离开时的持续时间都是 1 秒 -->
<transition :duration="1000">...</transition>
复制代码
可以对进入和移出设置不同的持续时间:
<!-- 表示进入时持续 0.5 秒,离开时持续 0.8 秒 -->
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
复制代码
我们可以在 transition
的属性中声明 JavaScript 钩子,这样就可以通过编写JS代码来实现动画。
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
复制代码
// ...
methods: {
// --------
// 进入中
// --------
beforeEnter: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 离开时
// --------
beforeLeave: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
复制代码
使用案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>5</title>
</head>
<body>
<div id="app">
<!-- 使用 JS 编写动画,本案例只展示入场 -->
<!-- 入场对应的是 before-enter enter 和 after-enter -->
<!-- 相应的,出场对应的是 before-leave leave 和 after-leave -->
<transition
name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter">
<h2 v-if="show">{{message}}</h2>
</transition>
<button @click="show = !show">切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "hello",
show: true
},
methods: {
btnClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
// el 参数是 transition 组件包裹的那个元素
// 动画执行之前把元素的字体颜色改成红色
el.style.color = 'red';
},
handleEnter(el, done) {
// 当 before-enter 被触发结束后,下一步就是真正触发运行动画,动画一般写在 @enter 对应的方法中
// 第二个参数 done 是一个回调函数
setTimeout(() => {
el.style.color = 'green'; // 动画真正开始执行后,把元素的字体颜色改成绿色
done(); // 告诉 vue,我们的动画执行完了。之后会去触发 after-enter 对应的方法
}, 1000)
},
handleAfterEnter(el){
setTimeout(() => {
el.style.color = 'blue'
},1000)
}
}
});
</script>
</body>
</html>
复制代码
上边的代码中仅使用 JavaScript 的过渡,vue官方推荐对于仅使用 JavaScript 过渡的元素,在 transition 上添加 v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。
上边的代码演示,在首次加载的过程中并没有一个过渡效果,我们可以在 transition
上添加 appear
属性设置节点在初始渲染时就有一个过渡的效果。
<transition appear>
<!-- ... -->
</transition>
复制代码
也可以自定义 CSS 类名,下边的案例中我们实现了在页面首次加载的时候就有一个动画效果:
<style>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 2s;
}
</style>
<div id="app">
<transition name="fade"
appear
enter-active-class="animated bounceInDown fade-enter-active"
leave-active-class="animated bounceOutUp fade-leave-active"
appear-active-class="fade-enter-active">
<h2 v-if="show">{{message}}</h2>
</transition>
<button @click="show != show">切换</button>
</div>
复制代码
有 appear 和 没有 appear 的效果如图所示:
对于原生标签可以使用 v-if
/ v-else
。最常见的多标签过渡是一个列表和描述这个列表为空消息的元素:
<transition>
<table v-if="items.length > 0">
<!-- ... -->
</table>
<p v-else>Sorry, no items found.</p>
</transition>
复制代码
案例1:
一个 h1 标签,一个 h2 标签
<style>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 1s;
}
</style>
<!-- 下边的案例中用的都是这样一份样式代码,不再重复写 -->
<div id="app">
<transition>
<h1 v-if="show">hello</h1>
<h2 v-else>webchang</h2>
</transition>
<button @click="show = !show">切换</button>
</div>
复制代码
我们看到这两个元素实现了过渡效果,并且其中一个元素一边隐藏,另一个元素一边显示,进入和离开同时发生了。
案例2:
transition 中的元素都改成 h1 标签:
<div id="app">
<transition name="fade">
<h1 v-if="show">hello</h1>
<h1 v-else>webchang</h1>
</transition>
<button @click="show = !show">切换</button>
</div>
复制代码
我们看到没有过渡效果了,为什么呢?
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做使 Vue 变得非常快,所以当有相同标签名的元素切换时,需要通过 key
属性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。 即使在技术上没有必要,给在 <transition>
组件中的多个元素设置 key
是一个更好的实践。
<!-- 加上 key 属性就可以正常的显示过渡效果了 -->
<transition name="fade">
<h1 v-if="show" key="name1">hello</h1>
<h1 v-else key="name2">webchang</h1>
</transition>
复制代码
上边的案例1中我们看到过渡效果是这样的:一个离开过渡的时候另一个开始进入过渡。这是 <transition>
的默认行为 - 进入和离开同时发生。 但是有时我们并不想要这样的效果,所以 Vue 提供了过渡模式
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。out-in
:当前元素先进行过渡,完成之后新元素过渡进入。在案例1的代码中加入 out-in 模式:可以看到老元素先离开,之后新元素再进入
<transition name="fade" mode="out-in">
<h1 v-if="show" key="hello">hello</h1>
<h1 v-else key="webchang">webchang</h1>
</transition>
复制代码
(1)可以使用 v-if
和 v-else
的方式
<div id="app">
<transition name="fade" mode="out-in">
<child-one v-if="show"></child-one>
<child-two v-else></child-two>
</transition>
<button @click="show = !show">切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 定义两个组件
Vue.component('child-one', {
template: `<div><h1>child-one</h1></div>`
})
Vue.component('child-two', {
template: `<div><h1>child-two</h1></div>`
})
let vm = new Vue({
el: "#app",
data: {
show: true
}
});
</script>
复制代码
(2)可以使用动态组件的方式
关于动态组件的讲解和使用可以查看这篇博客:vue中的动态组件
<div id="app">
<transition name="fade" mode="out-in">
<component :is="currentView"></component>
</transition>
<button @click="btnClick">切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child-one', {
template: `<div><h1>child-one</h1></div>`
})
Vue.component('child-two', {
template: `<div><h1>child-two</h1></div>`
})
let vm = new Vue({
el: "#app",
data: {
currentView: 'child-one'
},
methods: {
btnClick() {
this.currentView = this.currentView === 'child-one' ? 'child-two' : 'child-one';
}
}
});
</script>
复制代码
两种方式的最终效果是一样的:
怎么同时渲染整个列表,比如使用 v-for
?在这种场景中,要使用 <transition-group>
组件。该组件有以下几个特点:
<transition-group>
组件会以一个真实元素呈现:默认为一个 <span>
。也可以通过 tag
属性更换为其他元素。而<transition>
组件不会额外渲染 DOM 元素。key
属性值。<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>8</title>
<style>
.v-enter,
.v-lever-to {
opacity: 0;
}
.v-enter-active,
.v-lever-active {
transition: opacity 1s;
}
</style>
</head>
<body>
<div id="app">
<!-- 列表过渡 -->
<transition-group>
<h1 v-for="item in list" :key="item.id">{{item.title}}</h1>
</transition-group>
<button @click="btnClick">增加</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "hello",
count: 0,
list: []
},
methods: {
btnClick() {
this.list.push({
id: this.count++,
title: 'hello webchang'
})
}
}
});
</script>
</body>
</html>
复制代码
原理: 使用 transition-group
的时候,相当于我们在每一个列表项的外层都加了一个 transition
,这样就把列表元素的过渡转为单个元素的过渡了。单个元素过渡的时候,vue 会在这个元素隐藏和显示的时候动态的增加和删除相应的class类名,而我们已经提前在 style 标签中定义好了相应的class。 transition-group
组件有一个特点:CSS 过渡的类将会应用在内部的元素中,而不是这个容器本身。 从上边展示的动图中我们也可以印证这一点。
我们创建一个组件,将过渡效果进行封装复用。需要做的就是将 <transition>
或者 <transition-group>
作为根组件,然后将任何子组件放置在其中就可以了。
(1)不封装样式,样式单独写在外边
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>9</title>
<style>
.v-enter {
opacity: 0;
}
.v-enter-active {
transition: opacity 1s;
color: red;
}
.v-leave-to {
opacity: 0;
}
.v-leave-active {
transition: opacity 1s;
color: green;
}
</style>
</head>
<body>
<div id="app">
<fade :show="show">
<h2>{{message}}</h2>
</fade>
<button @click="btnClick">切换</button>
</div>
<template id="fade">
<transition>
<!-- 此处要用 v-if ,使用 v-show 没有效果 -->
<slot v-if="show"></slot>
</transition>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('Fade', {
template: '#fade',
props: ['show']
})
let vm = new Vue({
el: "#app",
data: {
message: "hello",
show: true
},
methods: {
btnClick() {
this.show = !this.show;
}
}
});
</script>
</body>
</html>
复制代码
(2)把样式也封装在组件里边,使用JavaScript钩子,在JS中编写动画代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>10</title>
</head>
<body>
<div id="app">
<fade-pro :show="show">
<h2>{{message}}</h2>
</fade-pro>
<button @click="btnClick">切换</button>
</div>
<template id="pro">
<transition
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter">
<div v-if="show">
<slot></slot>
</div>
</transition>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('FadePro', {
template: '#pro',
props: ['show'],
methods: {
handleBeforeEnter(el) {
el.style.color = 'red';
},
handleEnter(el, done) {
setTimeout(() => {
el.style.color = 'green';
done() // 动画执行完之后一定要手动调用一下 done()
},1000)
},
handleAfterEnter(el) {
setTimeout(() => {
el.style.color = 'blue';
},1000)
}
}
})
let vm = new Vue({
el: "#app",
data: {
message: "hello",
show: true
},
methods: {
btnClick() {
this.show = !this.show;
}
}
});
</script>
</body>
</html>
复制代码
演示代码下载链接:webchang.lanzous.com/ix3kpl82xwb 密码:e0kf
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。