目录
一、使用css创建动画
css3动画的核心是定义keyframes或transition。keyframes定义了动画行为,比如对于颜色渐变的动画,需要定义起始颜色和终止颜色。transition的使用更简单,当组件的css属性发生变化时,使用transition来定义其过度动画的属性。
1、transition过渡动画
<style>
.demo {
width: 200px;
height: 100px;
background-color: #ccc;
}
.demo-transition {
width: 400px;
height: 200px;
background-color: pink;
transition: width 2s, height 2s, background-color 2s;
}
</style>
<div class="app">
<div :class="style" @click="run"></div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
style: 'demo'
}
},
methods: {
run() {
if (this.style == 'demo') {
this.style = 'demo-transition'
} else {
this.style = 'demo'
}
}
},
})
app.mount('.app')
</script>
结果:
上面的示例实际上使用了简写属性,我们也可以逐条对属性动画效果进行设置。
/* 用来设置动画的属性 */
transition-property: width, height, background-color;
/* 用来设置动画执行的时长 */
transition-duration: 2s;
/* 用来进行延时设置 */
transition-delay: 0s;
/* 用来设置动画的执行方式,linear表示线性 */
transition-timing-function: linear;
2、keyframes动画
transition动画适合用来创建简单的过渡效果。css3中支持使用animation属性来配置更加复杂的动画效果。animation属性根据keyframes配置来执行基于关键帧的动画效果。
<style>
@keyframes animation1 {
0% {
width: 50px;
height: 50px;
background-color: #ccc;
}
25% {
width: 150px;
height: 150px;
background-color: green;
}
50% {
width: 200px;
height: 200px;
background-color: blue;
}
75% {
width: 250px;
height: 250px;
background-color: red;
}
100% {
width: 300px;
height: 300px;
background-color: pink;
}
}
.demo {
width: 50px;
height: 50px;
background-color: #ccc;
}
.demo-keyframes {
width: 300px;
height: 300px;
background-color: pink;
animation: animation1 3s linear;
}
</style>
<div class="app">
<div :class="style" @click="run"></div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
style: 'demo'
}
},
methods: {
run() {
if (this.style == 'demo') {
this.style = 'demo-keyframes'
} else {
this.style = 'demo'
}
}
},
})
app.mount('.app')
</script>
结果:
二、使用JavaScript的方式实现动画效果
动画的本质是将元素的变化以渐进的方式完成,即将大的状态变化拆分成非常多个小的状态变化,通过不断执行这些变化也可以实现动画效果。
<div class="app">
<div class="box" @click="click" :style="{width:width+'px',height:height+'px',backgroundColor:'red'}"></div>
</div>
<script>
const app = Vue.createApp({
data() {
return {
width: 100,
height: 100,
timer: null
}
},
methods: {
click() {
this.timer = setInterval(() => {
if (this.width >= 200) return
this.width++
this.height++
}, 50)
}
},
})
app.mount('.app')
</script>
结果:
三、vue过渡动画
1、定义过渡动画
Vue过渡动画的核心原理依然是采用css类是实现的,只是vue帮助我们在组件的不同生命周期自定切换不同的css类。
Vue中默认提供一个名为transition的内置组件,可以用其来包装要展示过渡动画的组件。transition组件的name属性用来设置要执行的动画名称,Vue中约定了一系列css类名规则来定义各个过渡过程中的组件状态。
x-enter-from类在组件即将被插入页面时添加到组件上,可以理解为组件的初始状态。
x-enter-to类在组件被插入页面后立即被添加,此时x-enter-from类会被移除,可以理解为组件的过渡的最终状态。
x-enter-active类在组件的整个插入过渡动画中都会被添加,直到组件的过渡动画结束后才会被移除。可以在这个类中定义组件过渡动画的时长、方式、延长。
x-leave-from类在组件即将被移除页面时添加到组件上,可以理解为组件移除的初始状态。
x-leave-to则对应地用来设置移除组件动画的终止状态。
x-leave-active类在组件的整个插入过渡动画中都会被添加,直到组件的过渡动画结束后才会被移除。可以在这个类中定义组件过渡动画的时长、方式、延长。
上面的六种特殊css类虽然被添加的实际不同,但是最终都会被移除,因此当动画执行完成后,组件的样式并不会保留,更常见的做法是在组件本身绑定一个最终状态的样式类。
<style>
.demo {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-enter-from {
width: 0px;
height: 0px;
background-color: red;
}
.ani-enter-active {
transition: width 2s, height 2s, background-color 2s;
}
.ani-enter-to {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-leave-from {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-leave-active {
transition: width 2s, height 2s, background-color 2s;
}
.ani-leave-to {
width: 0px;
height: 0px;
background-color: saddlebrown;
}
</style>
<div class="app">
<button @click="run">点击</button>
<transition name="ani">
<div v-if="flag" class="demo"></div>
</transition>
</div>
<script>
const app = Vue.createApp({
data() {
return {
flag: false
}
},
methods: {
run() {
this.flag = !this.flag
}
},
})
app.mount('.app')
</script>
注意:最终状态.demo css类必需放到最开头位置。
2、设置动画过程中的监听回调
对于组件的加载或卸载过程,有一系列的生命周期函数会被调用。对于vue中的专场动画来说,也可以注册一系列的函数来对其过程进行监听。
<style>
.demo {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-enter-from {
width: 0px;
height: 0px;
background-color: red;
}
.ani-enter-active {
transition: width 2s, height 2s, background-color 2s;
}
.ani-enter-to {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-leave-from {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-leave-active {
transition: width 2s, height 2s, background-color 2s;
}
.ani-leave-to {
width: 0px;
height: 0px;
background-color: saddlebrown;
}
</style>
<div class="app">
<button @click="run">点击</button>
<transition name="ani" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"
@enter-cancelled="enterCancelled" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave"
@leave-cancelled="leaveCancelled">
<div v-if="flag" class="demo"></div>
</transition>
</div>
<script>
const app = Vue.createApp({
data() {
return {
flag: false
}
},
methods: {
run() {
this.flag = !this.flag
},
//组件插入过渡开始前
beforeEnter(el) {
console.log('beforeEnter');
},
//组件插入过渡开始
enter(el, done) {
console.log('enter');
},
//组件插入过渡后
afterEnter(el) {
console.log('afterEnter');
},
//组件插入过渡取消
enterCancelled(el) {
console.log('enterCancelled');
},
//组件移除过渡开始前
beforeLeave(el) {
console.log('beforeLeave');
},
//组件移除过渡开始
leave(el, done) {
console.log('leave');
},
//组件移除过渡后
afterLeave(el) {
console.log('afterLeave');
},
//组件移除过渡取消
leaveCancelled(el) {
console.log('leaveCancelled');
}
},
})
app.mount('.app')
</script>
结果:点击按钮:
再次点击按钮
3、多个组件的过渡动画
Vue中的transition组件支持同时包装多个互斥的子组件元素,从而实现多组件的过渡效果。元素A消失的同时元素B展示。
<style>
.demo0 {
width: 100px;
height: 100px;
background-color: blue;
}
.demo1 {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-enter-from {
width: 0px;
height: 0px;
background-color: red;
}
.ani-enter-active {
transition: width 3s, height 3s, background-color 3s;
}
.ani-enter-to {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-leave-from {
width: 100px;
height: 100px;
background-color: blue;
}
.ani-leave-active {
transition: width 3s, height 3s, background-color 3s;
}
.ani-leave-to {
width: 0px;
height: 0px;
background-color: red;
}
</style>
<div class="app">
<button @click="run">显示/隐藏</button>
<transition name="ani" mode="out-in">
<div class="demo0" v-if="show"></div>
<div class="demo1" v-else></div>
</transition>
</div>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
run() {
this.show = !this.show
}
},
})
app.mount('.app')
</script>
当我们在transition标签上设置mode等于out-in是先移除动画在插入动画,in-out相反。
4、列表过渡动画
在Vue中,通常使用v-for指令来动态构建列表视图。在动态构建列表视图的过程中,其中的元素经常会有增删、重排等操作,在vue中使用transition-group组件可以非常方便地实现列表元素变动的动画效果。
避坑:table中列表过渡动画失效
<style>
.list-enter-active,
.list-leave-active {
transition: all 1s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
}
</style>
<div class="app">
<div class="table">
<table border="1" align="center" width="200px">
<thead>
<th>姓名</th>
<th>年龄</th>
<th>删除</th>
</thead>
<tbody align="center">
<transition-group name="list">
<tr v-for="(item,index) in getUser" :key="index">
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td @click="del(index)" style="cursor:pointer">删除</td>
</tr>
</transition-group>
</tbody>
</table>
</div>
</div>
<script>
let mock = [
{ name: '张三', age: 20 },
{ name: '李四', age: 30 },
{ name: '王五', age: 40 },
{ name: '罗翔', age: 30 }
]
const app = Vue.createApp({
data() {
return {
getUser: []
}
},
mounted() {
this.getUser = mock
},
methods: {
del(index) {
this.getUser.splice(index, 1)
}
},
})
app.mount('.app')
</script>
失效!
四、优化用户列表:
<style>
.app {
width: 200px;
margin: 100px auto;
}
.table {
border: 1px solid black;
margin-top: 10px;
}
.item {
width: 100px;
height: 50px;
border: 1px solid #ccc;
line-height: 50px;
}
.thead {
display: flex;
}
.tbody {
display: flex;
}
.list-enter-from {
opacity: 0;
}
.list-enter-active {
transition: all 2s ease;
}
.list-enter-to {
opacity: 1;
}
.list-leave-from {
opacity: 1;
}
.list-leave-active {
transition: all 2s ease;
}
.list-leave-to {
opacity: 0;
}
</style>
<body>
<div class="app">
<div class="radio">
<input type="radio" :value="-1" v-model="sex">全部
<input type="radio" :value="0" v-model="sex">男
<input type="radio" :value="1" v-model="sex">女
</div>
<div class="input">
搜索:<input type="text" v-model="keyWord">
</div>
<div class="table">
<div class="thead" align="center">
<div class="name item">姓名</div>
<div class="sex item">性别</div>
</div>
<transition-group name="list">
<div class="tbody" v-for="(item,index) in data" align="center" :key="index">
<div class="name item">{{item.name}}</div>
<div class="sex item">{{item.sex==0?'男':'女'}}</div>
</div>
</transition-group>
</div>
</div>
<script>
let mock = [
{ name: 'zs', sex: 1 },
{ name: 'ls', sex: 0 },
{ name: 'ww', sex: 1 },
{ name: 'lx', sex: 0 },
]
const app = Vue.createApp({
setup() {
let data = Vue.ref([])
let keyWord = Vue.ref("")
let sex = Vue.ref(-1)
Vue.onMounted(() => {
data.value = mock
})
let sexFilter = () => {
keyWord.value = ""
if (sex.value == -1) {
data.value = mock
} else {
data.value = mock.filter((item) => {
return item.sex == sex.value
})
}
}
let keyWordFilter = () => {
sex.value = -1
data.value = mock.filter((item) => {
return item.name.search(keyWord.value) != -1
})
}
Vue.watch(sex, sexFilter)
Vue.watch(keyWord, keyWordFilter)
return { data, keyWord, sex }
}
})
app.mount('.app')
</script>