目录
1.6 配合使用第三方库--自定义类名结合Animate.css
一、过渡动画
1.1 初识过渡动画
i.Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:
- 在 CSS 过渡和动画中自动应用 class
- 可以配合使用第三方 CSS 动画库,如 Animate.css
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
ii.如何给Vue控制的元素添加过渡动画?
- 将需要执行的动画元素放到transition组件中,一个transition组件中只能放一个元素
- 当transition组件中的元素显示时会自动查找:.v-enter/.v-enter-active/.v-enter-to类名
- 当transition组件中的元素隐藏时会自动查找:.v-leave/.v-leave-active/.v-leave-to类名
- 我们只需要在:.v-enter和 .v-leave-to中指定动画开始的状态;在.v-enter-active和.v-leave-active中指定动画执行的状态即可完成动画
iii.注意点:
- 如果想给多个元素添加过渡动画,那么就必须创建多个transition组件
- 默认情况下第一次进入时是没有动画的,若想一进来就有动画,我们可以通过给transition添加appear属性来告诉Vue第一次进来就需要动画
- 如果有多个不同的元素需要执行不同的过渡动画,那么我们可以通过给transition指定name的方式来指定“进入之前/进入之后/进入过程中,离开之前/离开之后/离开过程中”对应的类名以此来实现不同元素执行不同的过渡动画
1.2 通过 class 添加动画
* {
margin: 0;
padding: 0;
}
.box {
width: 150px;
height: 150px;
background: #f40;
}
/* .v-enter {
opacity: 0;
}
.v-enter-active {
transition: all 3s;
}
.v-enter-to {
opacity: 1;
}
.v-leave-active {
transition: all 3s;
}
.v-leave {
opacity: 1;
}
.v-leave-to {
opacity: 0;
} */
.one-enter {
/* 显示之前 */
opacity: 0;
}
.one-enter-active {
/* 显示过程中 */
transition: all 3s;
}
.one-enter-to {
/* 显示之后 */
opacity: 1;
}
.two-leave-active {
/* 显示过程中 */
transition: all 3s;
}
.two-leave {
opacity: 1;
}
.two-leave-to {
opacity: 0;
}
<script src="js/vue.js"></script>
<div id="app">
<button @click="toggle">按钮</button>
<transition appear name="one">
<div class="box" v-show="isShow"></div>
</transition>
<transition appear name="two">
<div class="box" v-show="isShow"></div>
</transition>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
isShow: true
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.isShow = !this.isShow;
}
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
1.3 通过钩子函数添加动画
可以在 attribute 中声明 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还是会去查找类名,因此我们可以给transition添加 v-bind:css="false" 来阻止其默认行为
如果是通过钩子函数来实现过渡动画,那么必须在动画执行过程中的回调函数中写上el.offsetWidth/el.offsetHeight
如果想让元素一进来就有动画,那么最好延迟一下再调用done方法
* {
margin: 0;
padding: 0;
}
.box {
width: 150px;
height: 150px;
background: #f40;
}
<script src="js/vue.js"></script>
<div id="app">
<button @click="toggle">按钮</button>
<transition appear v-bind:css="false" v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter">
<div class="box" v-show="isShow"></div>
</transition>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
isShow: true
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.isShow = !this.isShow;
},
beforeEnter(el) {
//进入动画开始之前
console.log("beforeEnter");
el.style.opacity = "0";
},
enter(el, done) {
//进入动画执行过程中
console.log("enter");
el.offsetWidth;
el.style.transition = "all 3s";
setTimeout(function() {
/*
动画执行完毕之后一定要调用done回调函数,否则后面的afterEnter钩子函数不会被执行
*/
done();
}, 0);
},
afterEnter(el) {
//进入动画执行完毕之后
console.log("afterEnter");
el.style.opacity = "1";
el.style.marginLeft = "500px";
}
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
1.4 使用第三方库--Velocity.js
使用方式:
1.引入:
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.min.js"></script>
2.给transition绑定对应的钩子函数:
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
......
3.找到过渡动画执行过程中的函数即enter(),在该函数里直接通过Velocity()实现动画:
enter(el, done) {
//进入动画执行过程中
console.log("enter");
el.offsetWidth;
el.style.transition = "all 3s";
Velocity();
setTimeout(function() {
/*
动画执行完毕之后一定要调用done回调函数,否则后面的afterEnter钩子函数不会被执行
*/
done();
}, 0);
},
示例:
* {
margin: 0;
padding: 0;
}
.box {
width: 150px;
height: 150px;
background: #f40;
}
<script src="js/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.min.js"></script>
<div id="app">
<button @click="toggle">按钮</button>
<transition appear v-bind:css="false" v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter">
<div class="box" v-show="isShow"></div>
</transition>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
isShow: true
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.isShow = !this.isShow;
},
beforeEnter(el) {
//进入动画开始之前
console.log("beforeEnter");
},
enter(el, done) {
//进入动画执行过程中
console.log("enter");
Velocity(el, {
opacity: 1,
marginLeft: "500px"
}, 3000);
setTimeout(function() {
/*
动画执行完毕之后一定要调用done回调函数,否则后面的afterEnter钩子函数不会被执行
*/
done();
}, 0);
},
afterEnter(el) {
//进入动画执行完毕之后
console.log("afterEnter");
}
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
1.5 使用自定义类名添加动画
我们可以通过以下 attribute 来自定义过渡类名:
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 结合使用十分有用。
示例:
* {
margin: 0;
padding: 0;
}
.box {
width: 150px;
height: 150px;
background: #f40;
}
.custom-enter-nj {
opacity: 0;
}
.custom-enter-to-nj {
opacity: 1;
margin-left: 500px;
}
.custom-enter-active-nj {
transition: all 3s;
}
<script src="js/vue.js"></script>
<div id="app">
<button @click="toggle">按钮</button>
<transition appear enter-class="custom-enter-nj" enter-active-class="custom-enter-active-nj" enter-to-class="custom-enter-to-nj">
<div class="box" v-show="isShow"></div>
</transition>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
isShow: true
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.isShow = !this.isShow;
},
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
1.6 配合使用第三方库--自定义类名结合Animate.css
* {
margin: 0;
padding: 0;
}
.box {
width: 150px;
height: 150px;
background: #f40;
}
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<script src="js/vue.js"></script>
<div id="app">
<button @click="toggle">按钮</button>
<transition appear enter-class="" enter-active-class="animated bounceInRight" enter-to-class="">
<div class="box" v-show="isShow"></div>
</transition>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
isShow: true
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.isShow = !this.isShow;
},
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
二、列表动画
2.1 给多个元素添加动画效果
示例:
.v-enter {
opacity: 0;
}
.v-enter-active {
transition: all 3s;
}
.v-enter-to {
opacity: 1;
}
.v-leave-active {
transition: all 3s;
}
.v-leave {
opacity: 1;
}
.v-leave-to {
opacity: 0;
}
<script src="js/vue.js"></script>
<div id="app">
<form>
<input type="text" v-model="name">
<input type="submit" value="添加" @click.prevent="add">
</form>
<transition-group appear tag="ul">
<li v-for="(person,index) in persons" :key="person.id" @click="del(index)">
<input type="checkbox">
<span>{{index}} --- {{person.name}}</span>
</li>
</transition-group>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
persons: [{
name: "zs",
id: 1
}, {
name: "ls",
id: 2
}, {
name: "ww",
id: 3
}],
name: "",
id: 3
},
// 专门用于存储监听事件回调函数
methods: {
add() {
this.id++;
let lastPerson = this.persons[this.persons.length - 1];
let newPerson = {
name: this.name,
id: lastPerson.id + this.id
// id: lastPerson.id + 1
};
// 在尾部添加
// this.persons.push(newPerson);
//在头部添加
this.persons.unshift(newPerson);
this.name = "";
},
del(index) {
this.persons.splice(index, 1);
}
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
注意点:
默认情况下 transition-group 会将所有执行动画的元素放入到 span 中,可通过 tag 属性解决
在我们不断添加数据时这里可能会发生key重复的情况:
此时,我们可以通过如下解决,具体看示例
三、单元素、单组件的入场出场动画
3.1 初识单元素、单组件的入场出场动画
-
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
被删除),在过渡/动画完成之后移除。
3.2 单元素、单组件的入场出场动画示例
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.v-leave-active {
animation: shake 3s;
}
.v-enter-active {
animation: shake 3s;
}
<script src="js/vue.js"></script>
<div id="app">
<div>
<transition>
<div v-if="show">hello world </div>
</transition>
<button @click="handleClick">切换</button>
</div>
</div>
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
show: false
},
// 专门用于存储监听事件回调函数
methods: {
handleClick() {
this.show = !this.show;
}
},
// template: `
// <p>{{time | dateFormate("yyyy-MM-dd")}}</p>
// `
});
3.2 单元素、单组件的入场出场动画2
公共部分:
<script src="https://unpkg.com/vue@next"></script>
<div id="root"></div>
JS实现动画:
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
el.style.color = "red";
},
handleEnterActive(el, done) {
const animation = setInterval(() => {
const color = el.style.color;
if (color === 'red') {
el.style.color = 'green';
} else {
el.style.color = 'red';
}
}, 1000);
setTimeout(() => {
clearInterval(animation);
done();
}, 3000);
},
handleEnterEnd() {
alert(123);
}
},
/*
type="transition" 执行动画和过渡的过程中,如果过渡和动画的时间不一致,以过渡时间为主
:duration="2000" 过渡和动画的持续时间都为2s
:duration="{enter:1000,leave:3000}" 入场1s,出场3s
*/
template: `
<div>
<transition
:css="false"
@before-enter="handleBeforeEnter"
@enter="handleEnterActive"
@after-enter="handleEnterEnd"
@before-leave=""
@leave=""
@leave-after=""
>
<div v-if="show">hello world </div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
// <button @click="handleClick">切换</button>
// class="transition" :style="styleObj"
});
//将组件挂到root节点里,即只作用在id等于root的div元素里
//vm 代表的就是 vue 应用的根组件
const vm = app.mount('#root');
//vm.$data 获得数据
// vm.$data.message = 'root';
</script>
公共部分CSS:
.v-leave-to {
opacity: 0;
}
.v-enter-from {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 3s ease-in;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
多个单元素标签之间的动画切换:
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
},
},
template: `
<div>
<transition mode="out-in" appear>
<div v-if="show">hello world </div>
<div v-else="show">bye world </div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
});
//将组件挂到root节点里,即只作用在id等于root的div元素里
//vm 代表的就是 vue 应用的根组件
const vm = app.mount('#root');
//vm.$data 获得数据
// vm.$data.message = 'root';
</script>
多个单组件之间的动画切换:
<script>
const ComponentA = {
template: '<div>bye world</div>'
}
const ComponentB = {
template: '<div>bye</div>'
}
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
},
},
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
template: `
<div>
<transition mode="out-in" appear>
<component-a v-if="show" />
<component-b v-else="show" />
</transition>
<button @click="handleClick">切换</button>
</div>
`
});
//将组件挂到root节点里,即只作用在id等于root的div元素里
//vm 代表的就是 vue 应用的根组件
const vm = app.mount('#root');
//vm.$data 获得数据
// vm.$data.message = 'root';
</script>
或,
<script>
const ComponentA = {
template: '<div>bye world</div>'
}
const ComponentB = {
template: '<div>bye</div>'
}
const app = Vue.createApp({
data() {
return {
component: 'component-a'
}
},
methods: {
handleClick() {
if (this.component === 'component-a') {
this.component = 'component-b';
} else {
this.component = 'component-a';
}
},
},
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
template: `
<div>
<transition mode="out-in" appear>
<component :is="component" />
</transition>
<button @click="handleClick">切换</button>
</div>
`
});
//将组件挂到root节点里,即只作用在id等于root的div元素里
//vm 代表的就是 vue 应用的根组件
const vm = app.mount('#root');
//vm.$data 获得数据
// vm.$data.message = 'root';
</script>