最近在開發新的網頁前台時,上版後在本人的Android手機上面發現一個問題,我在Nuxt Layout、Nuxt-Transition上面有設CSS動畫進行切頁面的淡入淡出,但是當我在我的手機上面按上一頁,卻會發生詭異的現象。
第一頁稱為A頁,第二頁稱為B頁。當我從B頁使用Android手勢返回A頁,這時候畫面的樣子會是
- A頁跳B頁前的最後樣子(A頁)
- 慢慢從B頁淡入淡出到A頁(B頁-A頁)
簡單來說就是會是A-B-A的顯示順序,這點目前在測試中只發生在Android上Google Pixel的手機,非常奇怪,這邊先簡稱這個為A-B-A上一頁問題。
狀況推論:預測返回手勢(Predictive Back) 🔗
首先,PC不會發生、IOS不會發生、其他Android手機上不會發生,我拿了其他朋友的舊款Pixel會發生。於是把思考方向往Pixel的機制上面,我又做了一個測試:Pixel用底下傳統三鍵式返回上一頁事不會發生A-B-A問題的;但是用返回手勢(左滑螢幕、右滑螢幕)會發生,於是查了一下相關資料,發現可能是這個機制造成的:預測返回手勢。
預測返回手勢的特殊機制是,會在回上一頁的時候,先顯示快取的上一頁的內容,這也就是A-B-A當中最開始的A。至於後面的B-A就是正常在進到上一頁的時候,實際的淡入淡出動作了。
有副作用的解決方法 🔗
因為這個快取上一頁的機制算是正常邏輯,如果真的要修正會需要捨棄掉轉場動畫,或是在抓上一頁的時候取消動畫、並在後續正常路由導向其他頁面時恢復。
這邊我是用Nuxt的Plugin機制進行後者的實踐(predictive-back.client.ts):
export default defineNuxtPlugin(() => {
if (!import.meta.client) return
const disableTransitions = () => document.body.classList.add('no-layout-transition')
const enableTransitions = () => document.body.classList.remove('no-layout-transition')
window.addEventListener('popstate', () => {
disableTransitions()
})
const router = useRouter()
router.beforeEach(async (to, from, next) => {
enableTransitions()
next()
})
})
主要是抓popstate(瀏覽器歷史紀錄變動,也就是上一頁下一頁之類的操作會觸發)的時候給body加上禁用轉場的class,然後router移動的時候恢復轉場,這邊後續再用這個禁轉場的class做css處理:
/* 針對所有轉場進行取消動畫的覆蓋 要放在全域css而非scoped裡面 */
body.no-layout-transition .layout-leave-active,
body.no-layout-transition .layout-enter-active,
body.no-layout-transition .page-leave-active,
body.no-layout-transition .page-enter-active,
body.no-layout-transition .el-main {
transition: none !important;
animation: none !important;
}
這樣處理後會在上一頁的時候不會有轉場效果,但是有個副作用是上一頁後再轉到其他路由連結時動畫會有前面一小部份被吃掉導致看起來有點突兀。
小結 🔗
這個預測返回手勢的機制目前也只有在少部分Android有實裝,且確實是正常機制的快取導致而非異常,應該目前不會實踐上面的補救措施,這邊提供給不想要用戶被這個快取+動畫導致的A-B-A呈現效果影響的人作為參考做法。
