vue-router 頁面切換動畫
網上一直沒翻到 vue-router
翻頁動畫的完美實現,就心[bei]動[bi]的,編了一個簡單的動畫組件。支持簡單的前進後退的左右動畫,針對不支持 history.state
的瀏覽器,也能支持簡單的透明動畫。
獻上效果圖:
可下載此項目,打開 test.html
,點擊瀏覽器的前進、後退按鈕,觀看具體效果。項目地址
使用
使用前,請確保項目內,已經使用了 vue-loader
。
在需要動畫的頁面中,引用 src/vue-page-animation.vue
文件,並且在 router-view
組件中,添加 class="vue-page-animation-router-view"
,具體如下:
<template>
<div class="app">
<VuePageAnimation>
<keep-alive>
<router-view class="vue-page-animation-router-view"></router-view>
</keep-alive>
</VuePageAnimation>
</div>
</template>
<script>
import VuePageAnimation from '../src/vue-page-animation';
export default {
components: {
VuePageAnimation
},
}
</script>
動畫鉤子
- 左右切換鉤子:
vue-page-animation-left
,vue-page-animation-right
- 透明動畫鉤子:
vue-page-animation-fade
針對不支持history
api 瀏覽器的簡單透明動畫 - 如要強制指定某種動畫,可給組件配置
force-transition-name
屬性,用於指定動畫的鉤子
對於低性能的機子,強烈建議,都指定 force-transition-name="vue-page-animation-fade"
,避免左右切換動畫,導致頁面渲染出現殘影、fixed元素錯位等問題。
滾動距離修正
若不想自動修正滾動距離,可以給進入頁面的根元素,配置 data-vue-paga-animation-back
和 data-vue-paga-animation-forward
屬性,用於指定頁面前進、後退 進入時,頁面的初始化滾動位置,如:
<!-- pageA.vue -->
<template>
<div class="page-a" data-vue-paga-animation-back="20" data-vue-paga-animation-forward="20">
內容..
</div>
</template>
頁面在 進入或後退 進入時,默認滾動 20px
距離
關於 scrollBehavior
scrollBehavior
,它是 vue-router
中,記錄滾動位置的方法,用法如下:
const router = new Router({
routes: [
// 一堆路由配置
],
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
});
配上動畫,效果如下:
雖能記錄滾動位置,但在執行動畫前,會導致頁面跳動。最後無奈放棄
實現原理
判斷前進和後退:
1. 使用 history api 保存頁面信息,如果不支持此 api,則放棄判斷,使用 fade 動畫
2. history.state.rid 頁面的唯一 id,此 id 的值是請求的時間,所以通過 id 大小的判斷,就可以知道是前進還是後退了
3. 記錄滾動位置的 map 對象,使用 history.state.rid 作爲 key 值,如果不支持 history api,則使用 url 作爲 key 值
修復滾動軸位置:
1. 在動畫前,鎖定body層的滾動軸
2. 如何鎖定滾動軸?給 body 設置 position: fixed; width: 100%; overflow-y: scroll;
3. 然後,給當前離開的頁面,修正 top 的位置
4. 同時,獲取正在進入的頁面的滾動距離,並修正 top 的位置
5. 待動畫結束後,刪除 body 的樣式,同時,刪除正在進入、離開元素的 top 屬性,同時修正滾動軸的位置
如何獲取頁面切換前,滾動軸的位置?
1. 方法一: 監聽 window.onscroll,定時記錄滾動軸的位置
2. 方法二: router.beforeEach 方法,在動畫前,記錄下滾動軸的位置
不能在 popstate
時記錄,因爲 vue-router
是可能走 hash
形式的。
最終,此組件選擇了方式二的變種,router
對象中,有一個 beforeHooks
屬性,此屬性記錄了所有 router.beforeEach
添加的函數鉤子。
組件裏,通過this.$router.beforeHooks.push()
的形式,加入新的函數鉤子。同時,在組件銷燬、不可見時,通過this.$router.beforeHooks.splice()
方式,移除函數鉤子。
入坑須知
- 因用到 position:fixed,所以組件的動畫,是 top 和 opacity 切換,性能上會有些許影響。如確保不會使用到 position:fixed,請果斷使用 transform 動畫。
- 修正 top 時,偶爾會有 1px 的抖動,有大神來指點一下嗎?
- 如果某天
vue-router
把beforeHooks
幹掉了,就只能與router.beforeEach
玩耍了 - 此項目木有測試同學參與,木有測試同學參與,木有測試同學參與