vue提供的了transition元件來實現元件的過渡和路由的過渡,合理使用這個組建可以讓我們的頁面更加的靈活,提高使用者體驗。
概念
在進入/離開的過渡中, 會有6個class的切換, 抄一張官方的圖
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 被刪除),在過渡/動畫完成之後移除。
看起來還是有有點亂,先來捋一下。
enter 定義開始的狀態, active定義過程, enter定義結束, 但是在實際進行的時候是有交叉的。 通過斷點可以發現,
- 新增v-enter
- 新增v-enter-active,
- 解除安裝v-enter
- 新增v-ernter-to
- 解除安裝v-enter-to和v-enter-active
// transition: all 2s;
.move-enter {
margin-left: 0;
}
.move-enter-active {
margin-left: 100px;
}
.move-enter-to {
margin-left: 200px;
}
複製程式碼
例如在上面這種情況下, 過渡動畫會怎麼進行呢?
這裡要注意兩點。
- enter-active覆蓋掉了enter的起點位置
- 一共經過了兩次過渡, enter-to是在active結束之後開始的, 所以第四秒, 才回到dom元素本身的位置。
所以官方文件之, 也是使用v-enter定義起點位置, 在enter-active中控制效果。
利用class實現過渡效果
在這6個class之上, 利用transition或者動畫, 都可以實現我們需要的效果。 舉個例子, 這裡我們直接將所有的路由改變都定義一個過渡效果,
appear屬性表示頁面初次載入的時候也適用於動畫
<transition appear name="move">
<router-view></router-view>
</transition>
複製程式碼
@keyframes animationIn {
0% {
transform: translate(-100%, 0);
}
100% {
transform: translate(0, 0);
}
}
@keyframes animationOut {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(100%, 0);
}
}
.move-enter {
transform: translate(-100%, 0);
position: absolute!important;
z-index: 999;
top: 0;
left: 0;
width: 100%;
}
.move-enter-active {
animation: animationIn 0.2s;
position: absolute!important; // 進入的元件要覆蓋掉移除的元件,參考 //https://cn.vuejs.org/v2/guide/transitions.html#%E8%BF%87%E6%B8%A1%E6%A8%A1%E5%BC%8F
z-index: 999;
top: 0;
left: 0;
width: 100%;
}
.move-leave {
transform: translate(0, 0);
}
.move-leave-active {
animation: animationOut 0.2s;
}
複製程式碼
效果
JavaScript 鉤子
這些鉤子函式可以結合 CSS transitions/animations 使用
<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>
複製程式碼
在這些鉤子中, 可以使用其他第三方庫,回撥中的el將是真實的dom元素 舉個例子, 這裡使用了官方推薦的Velocity.js作為動畫庫
<div class="main">
<transition name="showRect" appear
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
@before-leave="handleBeforeLeave"
@leave="handleLeave"
@after-leave="handleAfterLeave"
:css="false">
<div class="box" v-if="show"></div>
</transition>
</div>
<button @click="start">切換</button>
複製程式碼
methods: {
start() {
this.show = !this.show
},
handleBeforeEnter: function (el) {
el.style.opacity = 0;
console.log('方塊顯示動畫即將執行');
},
handleEnter: function (el, done) {
Velocity(el, 'stop');
Velocity(el, {
backgroundColor: '#0085eb',
opacity: 1,
translateX: 260,
rotateZ: ['360deg', 0]
}, {
duration: 1000,
easing: [ 0.4, 0.01, 0.165, 0.99 ],
complete: done
});
console.log('方塊顯示動畫執行中...');
},
handleAfterEnter: function (el) {
console.log('方塊顯示動畫結束');
},
handleBeforeLeave: function (el) {
console.log('方塊隱藏動畫即將執行');
},
handleLeave: function (el, done) {
Velocity(el, 'stop');
Velocity(el, {
backgroundColor: '#4dd0e1',
opacity: 0,
translateX: 0,
rotateZ: [0, '360deg']
}, {
duration: 1000,
easing: [ 0.4, 0.01, 0.165, 0.99 ],
complete: done
});
console.log('方塊隱藏動畫執行中...');
},
handleAfterLeave: function (el) {
console.log('方塊隱藏動畫結束');
}
}
複製程式碼
列表過渡
vue還提供了transition-group元件, 作為列表過渡的容器
不同於 ,它會以一個真實元素呈現:預設為一個 。你也可以通過 tag 特性更換為其他元素
transition-group 擁特別的v-move屬性,它會在元素的改變定位的過程中應用, 效果可參見官網。
其他的就不抄官網了
列表過渡中, 可以結合js鉤子, 實現一些特殊的效果
舉個例子
<template>
<div>
<div class="btn" @click="addItem">新增</div>
<div class="btn" @click="sort">排序</div>
<div class="box">
<div class="item-bar">
<transition-group name="fade" tag="p" appear
v-on:before-enter="beforeEnter"
v-on:after-enter="afterEnter">
// 這裡的data-index 是一個識別標識, 便於在js鉤子中獲得當前元素的序號
<div class="item" v-for="(i, index) in list" :key="i" :data-index="index">{{i}}</div>
</transition-group>
</div>
</div>
</div>
</template>
複製程式碼
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
name: "home",
data() {
return {
show: true,
list: [5,4,3,2,1],
nextNum: 6
};
},
methods: {
showDom() {
this.show = !this.show
},
beforeEnter: function (el: any) {
el.style.opacity = 0 // 每個元素插入之前, 透明度為0
let index = el.dataset.index 每次可能插入多個元素,
// 頁面載入時先展示5個
if (index < 5) {
//設定動畫延遲, 實現按續插入的效果
el.style.animationDelay = el.dataset.index * 0.3 + 's'
}
},
afterEnter: function (el) {
el.style.opacity = 1
console.log('afterEnter')
},
addItem() {
this.list.push(this.nextNum++)
},
sort() {
this.list = this.list.sort((a, b) => a -b)
}
}
});
</script>
複製程式碼
@keyframes animat {
0% {
margin-left: 300px;
opacity: 0;
}
100% {
margin-left: 0;
opacity: 1;
}
}
.fade-enter {
opacity: 0;
margin-left: 300px;
}
.fade-enter-active {
opacity: 0;
animation: animat 1s;
}
.fade-enter-to {
opacity: 1;
margin-left: 0;
}
.fade-move {
transition: all 0.3s;
}
.fade-leave {
left: 10px;
}
.fade-leave-active {
transition: all 2s ease-out;
}
.fade-leave-to {
left: -100%;
}
複製程式碼
效果
以上~