對於“數據優先”的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("動畫結束!")
}
}