Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。
包括以下工具:
- 在 CSS 过渡和动画中自动应用 class;
- 可以配合使用第三方 CSS 动画库,如 Animate.css;
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM;
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js;
Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡动画:
- 条件渲染 (使用 v-if)
- 条件展示 (使用 v-show)
- 动态组件
- 组件根节点
1、过渡类名实现动画
在进入/离开的过渡中,会有 6 个 class 切换。
-
v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
-
v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
-
v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
-
v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
-
v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
-
v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
如果 <transition>中没有使用 name 属性,则 v- 是这些类名的默认前缀。如果使用了 <transition name=“my”>,那么 v-enter 会替换为 my-enter。
- 示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="../vue.min.js"></script>
<style>
.v-enter,.v-leave-to {
opacity: 0;
transform: translateX(80px);
}
.v-enter-active, .v-leave-active{
transition: all 0.4s ease;
}
/** 自定义v-前缀 **/
.my-enter,.my-leave-to {
opacity: 0;
transform: translateY(150px);
}
.my-enter-active, .my-leave-active{
transition: all 0.4s ease;
}
</style>
</head>
<body>
<div id="app">
<input type="button" value="v-前缀" @click="flag=!flag">
<transition>
<p v-if="flag">This is a Content1.</p>
</transition>
<input type="button" value="my-前缀" @click="flag2=!flag2">
<transition name="my">
<p v-if="flag2">This is a Content2.</p>
</transition>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
flag: true,
flag2: true,
}
})
</script>
</body>
</html>
2、第三方类库实现动画
可以通过以下特性来自定义过渡类名:
- enter-class
- enter-active-class
- enter-to-class (2.1.8+)
- leave-class
- leave-active-class
- leave-to-class (2.1.8+)
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。
animate.css 是一个使用CSS3的animation制作的动画效果的CSS集合,里面预设了很多种常用的动画,且使用非常简单。
- 示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="../vue.min.js"></script>
<link rel="stylesheet" href="../animate.css">
<body>
<div id="app">
<input type="button" value="show" @click="flag=!flag">
<!-- 在duration属性可以一起设置动画时长,也可以使用对象分别设置 -->
<transition
enter-active-class="bounceIn"
leave-active-class="bounceOut"
:duration="{enter: 200, leave: 400}">
<!-- 在引用animate.css里的动画样式时,需要增加一个class--animated -->
<p v-if="flag" class="animated">This is a Content1.</p>
</transition>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
flag: true,
}
})
</script>
</body>
</html>
3、钩子函数实现半场动画
可以在<transition>的属性中声明 JavaScript 钩子,类似于事件处理:
<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>
在Vue实例中具体实现 JavaScript 钩子函数:
methods: {
// 动画进入前
// el 执行动画的DOM元素
beforeEnter: function (el) {
// ...
},
// 动画进入时
// 回调函数 done 是可选的 表示立即执行afterEnter()函数
enter: function (el, done) {
// ...
done()
},
// 动画进入后
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// 动画离场前
beforeLeave: function (el) {
// ...
},
// 动画离场时
leave: function (el, done) {
// ...
done()
},
// 动画离场后
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
- 示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="../vue.min.js"></script>
<style>
.ball {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: red;
}
</style>
<body>
<div id="app">
<input type="button" value="show" @click="flag=!flag">
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div class="ball" v-show="flag"></div>
</transition>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
flag: false,
},
methods: {
beforeEnter(el){
el.style.transform = "translate(0,0)"
},
enter(el, done){
// 无实际作用,用于实时刷新动画状态
el.offsetWidth
el.style.transform = "translate(150px,450px)"
el.style.transition = 'all 1s ease'
done()
},
afterEnter(el){
this.flag = !this.flag
}
}
})
</script>
</body>
</html>
4、<transition-group>元素实现列表动画
如果要同时渲染整个列表,比如在 v-for 这种场景中,可以使用 <transition-group> 组件,这个组件有以下特点:
- 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>,也可以通过 tag 特性更换为其他元素;
- 过渡模式不可用,因为我们不再相互切换特有的元素;
- 内部元素总是需要提供唯一的 key 属性值;
- CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
<transition-group> 不仅可以设置进入和离开动画,还可以利用 v-move 特性改变定位。它会在元素的改变定位的过程中应用,对于设置过渡的切换时机和过渡曲线非常有用。
// v-move 需要搭配 v-leave-active ,才能起作用
.v-move{
transition: all 0.6s ease;
}
.v-leave-active{
position: absolute;
}
可以通过 <transition-group> 的 appear 特性设置节点在初始渲染的过渡。
<transition appear>
<!-- ... -->
</transition>
<!-- 也可以自定义css类名 -->
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class" (2.1.8+)
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>
- 示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="../vue.min.js"></script>
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<style>
li {
border: 1px dashed #999;
margin: 5px;
line-height: 35px;
padding-left: 5px;
font-size: 12px;
width: 100%;
}
li:hover {
background-color: hotpink;
transition: all 0.8s ease;
}
.v-enter,.v-leave-to {
opacity: 0;
transform: translateY(80px);
}
.v-enter-active, .v-leave-active{
transition: all 0.6s ease;
}
.v-move{
transition: all 0.6s ease;
}
.v-leave-active{
position: absolute;
}
</style>
</head>
<body>
<div id="app">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加</h3>
</div>
<div class="panel-body form-inline">
<label>
Id:
<input type="text" class="form-control" v-model="id">
</label>
<label>
<!-- 注册键盘回车事件 -->
Name:
<input type="text" class="form-control" v-model="name" @keyup.enter="add">
</label>
<input type="button" value="添加" class="btn btn-primary" @click="add">
</div>
</div>
<!-- 通过tag将transition-group标签设为ul -->
<transition-group appear tag="ul">
<li v-for="item in list" :key="item.id" @click="del(item.id)">
{{ item.id }} -- {{ item.name }}
</li>
</transition-group>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
id: '',
name: '',
list: [
{id: 1, name: '奔驰'},
{id: 2, name: '宝马'},
{id: 3, name: '五菱'},
]
},
methods: {
add: function(){
var car = {id: this.id, name: this.name}
this.list.push(car)
this.name = this.id = ''
},
del: function(id){
this.list.some((item, i) => {
if(item.id == id){
this.list.splice(i, 1)
return true
}
})
}
},
})
</script>
</body>
</html>
参考链接: