文章目录
1.Vue中CSS动画原理
现有页面如下:
通过按钮可以控制文字的隐藏和显示。
现在的需求是:为文字的隐藏和显示添加渐隐渐现动画效果。
Vue 提供了 transition
的封装组件,将需要应用过渡效果的组件包裹在transition内:
<transition>
<h1 v-if="show">Hello,World!</h1>
</transition>
动画原理:
/* 渐显效果 */
.v-enter{
opacity:0;
}
.v-enter-active{
transition : opacity 3s;
}
/* 渐隐效果 */
.v-leave-to{
opacity:0;
}
.v-leave-active{
transition : opacity 3s;
}
2.在Vue中使用animate.css库
我们可以使用关键帧动画来添加比较复杂的动画效果,示例如下:
<style>
@keyframes bounce-in{
0%{
transform: scale(0);
}
50%{
transform: scale(1.5);
}
100%{
transform: scale(1);
}
}
.v-enter-active{
transform-origin: left center;
animation: bounce-in 1s;
}
.v-leave-active{
transform-origin: left center;
animation: bounce-in 1s reverse;
}
</style>
<div id="app">
<transition>
<h1 v-if="show">Hello,World!</h1>
</transition>
<button @click="handleClick">{{buttonText}}</button>
</div>
我们知道,Vue是通过在动画的生命周期中动态地给DOM元素添加或删除相应的类名来实现动画的。
那么,像.v-enter-active、.v-leave-active能不能简化为自定义的类名呢?
答案是可以。
这样做:
<transition
enter-active-class="active"
leave-active-class="leave"
>
这样做就预示着我们可以使用第三方CSS动画库来完成动画效果,以animate.css库为例:
<transition
enter-active-class="animate__animated animate__rubberBand"
leave-active-class="animate__animated animate__rubberBand"
>
3.在Vue中同时使用过渡和动画
我们发现,在页面刷新时,设置了进入动画的元素在第一次出现时并不会有动画效果,为例让页面刷新时便出现动画,我们可以这样做:
<transition
appear
enter-active-class="animate__animated animate__jello"
leave-active-class="animate__animated animate__jello"
appear-active-class="animate__animated animate__jello"
>
<h1 v-if="show">Hello,World!</h1>
</transition>
同时在Vue中使用过渡和动画:
<style>
.fade-enter,
.fade-leave-to{
opacity: 0;
}
.fade-enter-active,
.fade-leave-active{
transition: opacity 3s;
}
</style>
<transition
name="fade"
appear
enter-active-class="animate__animated animate__jello fade-enter-active"
leave-active-class="animate__animated animate__jello fade-leave-active"
appear-active-class="animate__animated animate__jello"
>
<h1 v-if="show">Hello,World!</h1>
</transition>
animate.css中,动画默认时长为1秒;
而我们自定义的过渡动画为3秒;
那么这两者放在一起的效果究竟以哪个为准?
我们可以在transition标签上使用type属性,来指定动画的时长以谁为准。
<transition
type="transition"
name="fade"
appear
enter-active-class="animate__animated animate__jello fade-enter-active"
leave-active-class="animate__animated animate__jello fade-leave-active"
appear-active-class="animate__animated animate__jello"
>
<h1 v-if="show">Hello,World!</h1>
</transition>
自定义动画执行总时长:使用transition
标签的:duration
属性
<transition
:duration="10000"
name="fade"
appear
enter-active-class="animate__animated animate__jello fade-enter-active"
leave-active-class="animate__animated animate__jello fade-leave-active"
appear-active-class="animate__animated animate__jello"
>
<h1 v-if="show">Hello,World!</h1>
</transition>
分别为入场和出场指定不同的动画时长::duration="{enter:5000,leave:1000}"
4.Vue中的JS动画与Velocity.js的结合
Vue提供了一些列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>
使用钩子完成入场动画:
<body>
<div id="app">
<transition
name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
>
<h1 v-if="show">Hello,World!</h1>
</transition>
<button @click="handleClick">{{buttonText}}</button>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
show:true,
buttonText:"隐藏"
},
methods:{
handleClick:function(){
this.show = !this.show;
if(this.buttonText === "隐藏"){
this.buttonText = "显示";
}else{
this.buttonText = "隐藏"
}
},
handleBeforeEnter:function(el){
el.style.color = 'red';
},
handleEnter:function(el,done){
setTimeout(() => {
el.style.color = 'green';
// 当动画执行结束后,必须手动调用done(),告诉Vue动画已经执行完毕
},2000);
setTimeout(() => {
done();
},4000);
},
handleAfterEnter:function(el){
el.style.color = '#000';
}
}
})
</script>
</body>
el
参数是动画包裹的标签,通常是transiton
标签内的内容。
当只用 JavaScript
过渡的时候,在 enter
和 leave
中必须使用 done
进行回调。否则,它们将被同步调用,过渡会立即完成。
为简化动画,我们使用Velocity.js库
下面用Velocity库实现渐显效果:
handleEnter:function(el,done){
Velocity(el,{opacity:1},{duration:1000,complete:done});
},
5.Vue中多个元素的过渡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多个元素过渡</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.v-enter,.v-leave-to{
opacity: 0;
}
.v-enter-active,.v-leave-active{
transition: opacity 1s;
}
</style>
</head>
<body>
<div id="app">
<transition mode="in-out">
<h1 v-if="show" key="hello">Hello,World!</h1>
<h1 v-else key="bye">Bye,World!</h1>
</transition>
<button @click="handleClick">{{buttonText}}</button>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
show:true,
buttonText:"隐藏"
},
methods:{
handleClick:function(){
this.show = !this.show;
if(this.buttonText === "隐藏"){
this.buttonText = "显示";
}else{
this.buttonText = "隐藏"
}
}
}
})
</script>
</body>
</html>
6.Vue中多个组件的过渡
以下代码通过v-if+transiton的形式实现组件切换的过渡:
<body>
<div id="app">
<transition mode="in-out">
<child-one v-if="show" key="hello">Hello,World!</child-one>
<child-one v-else key="bye">Bye,World!</child-one>
</transition>
<button @click="handleClick">{{buttonText}}</button>
</div>
<script>
Vue.component('child-one',{
template:'<div>child-one</div>'
})
Vue.component('child-two',{
template:'<div>child-two</div>'
})
var vm = new Vue({
el:"#app",
data:{
show:true,
buttonText:"隐藏"
},
methods:{
handleClick:function(){
this.show = !this.show;
if(this.buttonText === "隐藏"){
this.buttonText = "显示";
}else{
this.buttonText = "隐藏"
}
}
}
})
</script>
</body>
下面通过动态组件的形式来实现同样的效果:
<transition mode="out-in">
<component :is="type"></component>
</transition>
通过动态切换type来实现。
7.Vue中的列表过渡
如果我们使用数据动态渲染列表项,在列表项增加或减少时我们想要动画效果,如何实现?
使用<transition-group>
标签包裹需要过渡的列表
使用<transition-group>
相当于为每个列表项添加一个<transiton>
标签包裹
<style>
.v-enter,.v-leave-to{
opacity: 0;
}
.v-enter-active,.v-leave-active{
transition: opacity 1s;
}
</style>
<body>
<div id="app">
<transition-group>
<div v-for="item in list" :key="item.id">{{item.content}}</div>
</transition-group>
<button @click="handleClick">add</button>
</div>
<script>
var index = 0;
var vm = new Vue({
el:"#app",
data:{
list:[]
},
methods:{
handleClick:function(){
this.list.push({
id:index++,
content:"Hello,world"
})
}
}
})
</script>
</body>
8.Vue中的动画封装
在Vue中,可以将动画封装进一个组件,这个组件的所有实例都会拥有同样的动画,非常方便。
如何封装?利用插槽。
封装方式一:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动画封装</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
/* 渐显效果 */
.v-enter{
opacity:0;
}
.v-enter-active{
transition : opacity 3s;
}
/* 渐隐效果 */
.v-leave-to{
opacity:0;
}
.v-leave-active{
transition : opacity 1s;
}
</style>
</head>
<body>
<div id="app">
<fade :show="show">
<div>Hello,World</div>
</fade>
<fade :show="show">
<h1>Hello,World</h1>
</fade>
<button @click="handleClick">Toggle</button>
</div>
<script>
Vue.component('fade',{
props:['show'],
template:`
<transition>
<slot v-if="show"></slot>
</transition>
`
})
var vm = new Vue({
el:"#app",
data:{
show:false,
},
methods:{
handleClick:function(){
this.show = !this.show;
}
}
})
</script>
</body>
像下面这样使用JS钩子定义动画,可以避免在style标签内定义css样式,可以完全将动画封装到组件内:
<body>
<div id="app">
<fade :show="show">
<div>Hello,World</div>
</fade>
<fade :show="show">
<h1>Hello,World</h1>
</fade>
<button @click="handleClick">Toggle</button>
</div>
<script>
Vue.component('fade',{
props:['show'],
template:`
<transition
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
>
<slot v-if="show"></slot>
</transition>
`,
methods:{
handleBeforeEnter:function(el){
el.style.color = 'red';
},
handleEnter:function(el,done){
setTimeout(() => {
el.style.color = 'green';
},1000);
setTimeout(() => {
done();
},2000);
},
handleAfterEnter:function(el){
el.style.color = "#000";
}
}
})
var vm = new Vue({
el:"#app",
data:{
show:false,
},
methods:{
handleClick:function(){
this.show = !this.show;
}
}
})
</script>
</body>