对于“数据优先”的Vue来说,没有了dom的操作,过渡效果该如何实现?
其实,vue也不是完全失去了对dom的操作,仍然可以通过比如
$refs
获取对应的dom元素
先看一个小demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id="root">
<div v-show="show">Hello world</div>
<button @click="handleClick">toggle</button>
</div>
<script>
var vm=new Vue({
el:'#root',
data:{
show:true
},
methods:{
handleClick:function(){
this.show=!this.show
}
}
})
</script>
</body>
</html>
其效果如下:
但可以看到,其中文字“Hello world”的“行为”非常生硬。一个元素尚且如此,更别说两个元素来回切换的场景了。
为此,vue提供了一个“进出过渡”的特效CSS语法。其使用前要先用<transition></transition>
包裹涉及到的所有标签:
我们来看两个元素的效果——修改上面demo-Hello world部分
<transition>
<div v-if="show">Hello world</div>
<div v-else>Bye world</div>
</transition>
保存一下,到页面上看,会发现依然很“不爽”。这可能是因为还没有写style的缘故:
.v-enter,.v-leave-to{ /* 过渡前 */
opacity:0;
}
.v-enter-active,.v-leave-active{ /* 过渡中 */
transition:opacity 1s;
}
做完这些我们就会发现。。。效果依然没有出现!
这是为什呢? ——Vue默认会【复用dom】,通俗地说,就是【缓存DOM】。要想解除这个,需要给每个元素加一个key:
<transition mode="out-in">
<div v-if="show" key="key1">Hello world</div>
<div v-else key="key2">Bye world</div>
</transition>
这样就可以了:
上面是多个元素的切换,那么多个组件呢?
笔者建议最好采用动态组件的方式:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id="root">
<component :is="type"></component>
<button @click="handleClick">toggle</button>
</div>
<script>
Vue.component('child',{
template:'<div>Hello world</div>'
})
Vue.component('childs',{
template:'<div>Bye world</div>'
})
var vm = new Vue({
el: '#root',
data: {
type:'child'
},
methods: {
handleClick: function() {
this.type=this.type==='child'?'childs':'child'
}
}
})
</script>
</body>
</html>
其实,在真正项目里,很多时候笔者采用的就是这种方法:比如在一个vue多页面(组件)的项目里,页面之间切换时有一个向左/右滑动的效果:
<transition :name="transitionName">
<router-view class="Router" />
</transition>
//js-data
data(){
retrun{
transitionName:'slide-right',
//...
}
}
//js-watch
watch:{
$route(to,from){ //导航发生变化,$route也就是会改变
if(to.meta.index > from.meta.index){
this.transitionName:'slide-right'
}else{
this.transitionName:'slide-left'
}
}
}
//css
.Router{
position:absolute;
width:100%;
transition:all .8s ease;
}
.slide-left-enter,.slide-right-leave-active{
opacity:0;
transform:translate(100%,0);
}
.slide-left-leave-active,.slide-right-enter{
opacity:0;
transform:translate(-100%,0);
}
这样的话,其app.vue里的路由routes中每一项都要加上一个属性:
meta:{index:数字};
emmmmmm…vue中使用第三方库函数又是另一番体验了:
让我们把目光重新聚焦到本文第一例demo上,看一下animate.css的“风情”:
npm install animate.css
<link rel="stylesheet" href="./animate.css">
<div id="root">
<transition
enter-active-class="animated swing"
leave-active-class="animated shake"
>
<div v-show="show">Hello world</div>
<button @click="handleClick">toggle</button>
</transition>
</div>
<script>
var vm=new Vue({
el:'#root',
data:{
show:true
},
methods:{
handleClick:function(){
this.show=!this.show
}
}
})
</script>
如上,swing和shake就是animate.css内部封装好的两种负责“抖动”动画的类名。 —— 没错,它的实现原理就是@keyframes
说完了css,不妨再来看看JS:他也可以制造动画效果 —— Velocity.js插件:
npm install velocity.js
<transition
@before-enter="ha1"
@enter="ha2"
@after-enter="ha3"
>
<div v-show="show">Hello</div>
</transition>
//js-methods
methods:{
//这些函数会接收一个参数:指向绑定的dom
ha1:function(el){
el.style.opacity=0
},
ha2:function(el){
Velocity(el,{opacity:1},{duration:1000,complete:done})
},
ha3:function(el){
alert("动画结束!")
}
}