概述
动画分为两种:transition
和animation
,vue
只是将其融入组件中去使用了。
使用场景
v-if
、v-show
显示隐藏组件时添加动画- 组件切换实现页面切换,添加过渡动画,淡入淡出效果增强用户体验
transition
组件
vue
内置组件,用以包裹想要实现动画效果的dom
节点,且只能包裹单个元素。
如何实现包裹多个元素?——需要给transition
所包裹的子元素属性标签上添加v-if
、v-else
,需要注意的是:当transition
所包裹子元素的标签名相同时,需要给其添加上key
值,以免影响dom
工作。
transition
实现原理
给当前需要呈现变化的dom
元素添加上可能的6个class
样式:
- 动画初始状态:
v-enter
- 动画过渡过程:
v-enter-active
- 动画过渡完成状态:
v-enter-to
过程:vue
自动帮我们添加切换了样式,我们只需设置好切换的类别即可。在dom
元素运动前一帧给其绑定一个class
属性v-enter
,随即消失。来到运动过程前一刻给其class
属性变为v-enter-active
和v-enter-to
,随着运动的进行,上述两个class
属性逐渐消失。
name
属性:transition
标签头部可添加name
属性用以区分不同动画。
<transition name="fade"></transition>
<!-- vue就会将 .v-enter 变为 .fade-enter 用以私有化动画样式 -->
复制代码
appear
属性:动画一开始变展示出来。
<transition appear><transition>
复制代码
type
属性:存在transition
和animation
结合的动画,并且两者的动画时间存在长短的区别,我们可以再transition
标签头部加上type
熟悉,用于告诉vue
我们的动画是以哪种方式的时间为基准的,避免上下窜动的不和谐效果,保持了两个动画的同步。
transition-group
列表过渡
与transition
的区别:
transition-group
会生成一个真实的dom
节点,默认为span
,通过tag="ul"
可实现默认切换transition-group
必须要有key
值
小栗子
.v-enter, /* 时间点,开始动画之前元素的起始状态 */
.v-leave-to{ /* 时间点,元素完成动画离开的终止状态 */
opacity: 0;
transform: translateX(70px)
}
.v-enter-active, /* 入场动画时间段 */
.v-leave-active{ /* 离场动画时间段 */
transition: all 0.8s ease;
}
.private-enter,
.private-leave-to{
opacity: 0;
transform: translateY(70px)
}
.private-enter-active,
.private-leave-active{
transition: all 0.9s ease;
}
复制代码
<div id="app">
<transition>
<h1 v-show="flag1">使用过渡类名实现动画效果</h1>
</transition>
<br>
<transition name="private">
<h1 v-show="flag2">demo</h1>
</transition>
<input type="button" value="按钮1" @click="flag1 = !flag1">
<input type="button" value="按钮2" @click="flag2 = !flag2">
</div>
复制代码
const app = new Vue({
el: '#app',
data: {
flag1: true,
flag2: true
}
})
复制代码
自定义样式标签
在使用第三方库(如:animate.css
)的时候,使用此方式比较多,常应用于退场动画中。
animate.css
基于animation
来写的,其@keyframe
中已经设置了初始值和结束值,所以我们只需要设置一下运动过程即可。譬如下面leave-active-class="animate shake"
中的属性值animate
是必须的,后面的shake
动画样式可去官网中寻找。
栗子
.a{
opacity: 0;
}
.b{
transition: opacity 10s;
}
.c{
opacity: 1;
}
复制代码
<div id="app">
<transition
enter-class="a"
enter-active-class="b"
enter-to-class="c"
appear
leave-active-class="animated shake">
<h1 v-show="flag">我要运动</h1>
</transition>
<button @click="flag = !flag">change</button>
</div>
复制代码
const app = new Vue({
el: '#app',
data: {
flag: true
}
})
复制代码
栗子
animate.css
的其他使用:
<div id="app">
<transition enter-active-class="bounceIn" leave-active-class="bounceOut">
<h3 v-if="flag">11111111111</h3>
</transition>
<input type="button" value="toggle1" @click="flag=!flag">
<!-- 设置入场和离场的动画时长 -->
<transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="1000">
<h3 v-if="flag1" class="animated">222222222222</h3>
</transition>
<input type="button" value="toggle2" @click="flag1=!flag1">
<!-- 分别设置入场和离场的动画时长 -->
<transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="{enter: 100,leave: 1000}">
<h3 v-if="flag2" class="animated">333333333333</h3>
</transition>
<input type="button" value="toggle3" @click="flag2=!flag2">
</div>
复制代码
js
钩子函数实现动画
八个钩子函数
@before-enter
@enter
@after-enter
@enter-cancelled
@before-leave
@leave
@after-leave
@leave-cancelled
综合栗子
.fade-enter{ /* 定义进入过渡的开始状态,元素插入之前生效,元素插入的下一帧移除 */
opacity: 0;
}
.fade-enter-active{ /* 定义进入过渡生效时的动画状态 */
transition: opacity 1s;
}
.fade-enter-to{ /* 定义进入过渡的结束状态,过渡动画完成之后移除 */
opacity: 1;
}
.fade-leave{
opacity: 1;
}
.fade-leave-active{
transition: opacity 1s;
}
.fade-leave-to{
opacity: 0;
}
/* ------------分割线---------- */
.slide-enter{
opacity: 0;
transform: translateY(20px);
}
.slide-enter-active{
transition: opacity 1s; /* transition和animation结合使用 */
animation: slide-in 1s ease-out;
}
.slide-enter-to{
opacity: 1;
transform: translateY(0px);
}
.slide-leave{
opacity: 1;
transform: translateY(0px);
}
.slide-leave-active{
transition: opacity 1s;
animation: slide-out 1s ease-out;
position: absolute;
}
.slide-leave-to{
opacity: 0;
transform: translateY(20px);
}
.slide-mode{
transition: transform 1s;
}
@keyframes slide-in{
from{
transform: translateY(20px);
}
to{
transform: translateY(0px);
}
}
@keyframes slide-out{
from{
transform: translateY(0px);
}
to{
transform: translateY(20px);
}
}
/* ---------分割线------- */
.a{
opacity: 0;
}
.b{
transition: opacity 10s;
}
.c{
opacity: 1;
}
复制代码
<div id="app">
<!-- 1 -->
<button @click="show=!show">点击</button>
<br>
<select v-model="animatedName">
<option value="fade">闪现过渡动画</option>
<option value="slide">上下滑动过渡动画</option>
</select>
<transition :name="animatedName">
<div v-show="show">我是属性绑定选择样式</div>
</transition>
<!-- 2 -->
<br>
<transition name="fade" appear>
<div v-show="show">我是私有化提示语</div>
</transition>
<transition name="slide" appear>
<div v-show="show">我是一句提示语</div>
</transition>
<!-- 3 -->
<transition name="slide" appear mode="out-in">
<div v-if="show">sxw</div>
<p v-else>iu</p>
</transition>
<!-- 4 -->
<transition
enter-class="a"
enter-active-class="b"
enter-to-class="c"
appear>
<div v-show="show">自定义样式标签</div>
</transition>
<!-- 5 -->
<transition
enter-active-class="animated bounce"
leave-active-class="animated shake"
appear>
<div v-show="show">引用第三方库</div>
</transition>
</div>
复制代码
增强用户体验,以上栗子的两个注意点:
- 给目标元素添加动画时,不仅仅针对目标元素,让其附近元素都有一个类似动画效果下移
.slide-mode{
transition: transform 1s;
}
复制代码
- 譬如我们需要删除目标动画元素,我们想着的是让目标元素完全离开文档流,下面元素再排列到空缺的位置上
.slide-leave-active{
transition: opacity 1s;
animation: slide-out 1s ease-out;
position: absolute; /* 离开时候脱离文档流 */
}
复制代码
栗子a:js
钩子函数实现动画
<button @click="load = !load">js实现动画</button>
<br>
<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">
<!-- 上一行代码告诉vue我们不是使用css去实现动画的,减少了vue一开始去找css的步骤 -->
<div v-if="load" style="width: 100px;height: 100px;background-color: red"></div>
</transition>
复制代码
const vm = new Vue({
el: '#app',
data: {
load: false,
divWidth: ''
},
methods: {
beforeEnter(el) {
console.log(el) // <div v-if="load" style="width: 100px;height: 100px;background-color: red"></div>
console.log("beforeEnter")
this.divWidth = 10 // 限定每次初始动画时的div宽度
el.style.width = this.divWidth + 'px'
},
enter(el, done) {
console.log('enter')
done(); // 这里需要执行一下done函数才能往下运行其他钩子函数
let round = 1;
const timer = setInterval(() => {
el.style.width = (this.divWidth + round * 10) + 'px'
round ++
if(round > 20) {
clearInterval(timer)
done()
}
}, 20)
},
afterEnter(el) {
console.log('afterEnter')
el.style.width = '300px'
},
enterCancelled(el) {
console.log('enterCancelled')
},
beforeLeave(el) {
console.log('beforeLeave')
this.divWidth = '300'
el.style.width = this.divWidth + 'px'
},
leave(el, done) {
console.log('leave')
let round = 1
const timer = setInterval(() => {
el.style.width = (this.divWidth - round * 10) + 'px'
round ++
if(round > 20) {
clearInterval(timer)
done()
}
}, 20)
},
afterLeave(el) {
console.log('afterLeave')
},
leaveCancelled(el) {
console.log('leaveCancelled')
}
}
})
复制代码
栗子b:js
钩子函数实现动画
<div id="app">
<button @click="addClick">添加列表</button>
<transition-group tag="ul" name="slide">
<li
v-for="(list,index) in lists"
@click="removeClick(index)"
:key="list">
{{ list }}
</li>
</transition-group>
</div>
复制代码
const vm = new Vue({
el: '#app',
data: {
list: [1,2,3]
},
methods: {
addClick() {
const position = Math.floor(Math.random() * this.list.length) // 生成随机数
this.list.splice(position, 0, this.list.length + 1) // 添加的位置 删除的元素 添加元素个数
},
removeClick(index) {
this.list.splice(index, 1)
}
}
})
复制代码
demo
- 小球加入购物车动画实现
<div id="app">
<input type="button" value="加入购物车" @click="flag=!flag">
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
<div class="ball" v-show="flag" style="width: 15px;height: 15px;background-color: red;border-radius: 50%;"></div>
</transition>
</div>
复制代码
const app = new Vue({
el: '#app',
data: {
flag: false
},
methods: {
// el表示要执行动画的dom元素
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函数,完成下面钩子函数
done()
},
afterEnter(el) {
// 动画完成之后调用afterEnter函数
// 控制小球的显示与隐藏,直接跳过后半场动画,让flag标识符变为false
this.flag = !this.flag
el.style.opacity = 0.5
}
}
})
复制代码
demo
- 列表动画 - 添加删除功能
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属性,他会在元素改变定位的过程中应用 */
/* v-move和v-leave-active结合使用,使得列表的过渡动画更加平缓柔和! */
.v-move{
transition: all 0.8s ease;
}
.v-leave-active{
position: absolute;
}
复制代码
<div id="app">
<div>
<label for="">
Id:
<input type="text" v-model="id">
</label>
<label for="">
Name:
<input type="text" v-model="name">
</label>
<input type="button" value="添加" @click="add">
</div>
<ul>
<!-- 列表过渡,如果需要过渡的元素是通过v-for循环渲染出来的,就不能使用transition包裹,应该使用transitionGroup -->
<!-- 若要为v-for循环创建的元素设置动画,必须为每个元素绑定key值 -->
<!-- appear属性实现页面展示时的入场动画 -->
<transition-group appear tag="ul">
<li v-for="(item,index) in list" :key="item + index" @click="del(index)">
{{item.id}} -- {{item.name}}
</li>
</transition-group>
</ul>
</div>
复制代码
const vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
list: [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' }
]
},
methods: {
add() {
this.list.push( {id: this.id,name: this.name })
this.id = this.name = ''
},
del(index) {
this.list.splice(index, 1)
}
}
})
复制代码