滑動穿透(鎖body)終極探索

seventhMa發表於2019-04-08

場景

當頁面出現浮層的時候,滑動浮層的內容,正常情況下預期應該是浮層下邊的內容不會滾動;然而事實並非如此。

滑動穿透(鎖body)終極探索
如圖所示,浮層下邊的內容並沒有如想象中不受影響。

解決

先去github上搜尋一番,發現有解決此問題的開源包,簡單粗暴直接挑選了其中star的最高的(body-scroll-lock)操作一番! 使用後發現有一些問題:

  • 安卓端全掛
  • ios端偶爾會有鎖不住的情況

查原始碼發現該包在iOS端使用禁止touchmove的方式單獨處理,但是在其他端只是給bodyoverflow: hidden簡單處理。 於是決定寫一個針對多端通用的包來處理類似的問題。

探索一:overflow: hidden

看到下邊的滾動肯定立刻就想到了是整個viewport的滾動,那麼如果給body設定overflow: hidden,此時body的內容就只有一屏了,肯定不會滾動了;

body { overflow: hidden; }
複製程式碼

此方案在pc端完美解決了我們的問題,然而事情並沒有那麼簡單;

再試試移動端:

滑動穿透(鎖body)終極探索
移動端中並沒有出現期待的效果。。。 既然pc端已經有了完美的方案,下邊我們繼續探索移動端的解決方案。

探索二:body定位

上邊想到給body設定overflow: hidden在移動端並不能解決我們的問題,是否在於body的height沒有設定

  • 將html、body的高度都設定為100%
  • 給body設定絕對定位(fixed);

同時使用這兩個操作似乎完美滿足了我們的需求;

滑動穿透(鎖body)終極探索
但是如圖所示,每次都會將頁面拉到最頂上的位置,這樣看起來又不完美了; 既然使用了定位,那麼給一個top值不就定位到我們想要的位置了(聰明如我)。

tips: body 設定 relative 定位會頁面自身拉上去,下邊留白

多次實驗發現這個方案在android端中完美達到了我們想要的結果,但是在ios端並不理想;每次定位的時候會有閃動的問題;好事多磨,接著探索ios端的方案。

探索三:禁止touchmove

如果禁止掉頁面的touchmove是否可行呢?話不多說就是幹! 當彈出浮層的時候禁掉頁面元素的touchmove

document.addEventListener('touchmove', function (event) {
    event.preventDefault()
})
複製程式碼

測試發現沒有達到想象中的效果,感覺這個結果並不能接受啊,禁止documenttouchmove都不能禁止滾動的嗎?

進一步的探索後發現原因竟是這個屬性

  • passive addEventListener第三個引數中傳入

原來是瀏覽器做的一些優化,chrome passive-event-listeners Passive Event Listeners是Chrome提出的一個新的瀏覽器特性:Web開發者通過一個新的屬性passive來告訴瀏覽器,當前頁面內註冊的事件監聽器內部是否會呼叫preventDefault函式來阻止事件的預設行為,以便瀏覽器根據這個資訊更好地做出決策來優化頁面效能。當屬性passive的值為true的時候,代表該監聽器內部不會呼叫preventDefault函式來阻止預設滑動行為,Chrome瀏覽器稱這型別的監聽器為被動(passive)監聽器。

知道問題就好說了,給addEventListener傳入第三個引數

document.addEventListener('touchmove', function (event) {
    event.preventDefault()
}, { passive: false })
複製程式碼

大功告成! 突然想到,如果浮層中還需要滾動那就不GG了! so,是不是可以有選擇性的禁止滾動(在浮層中元素滾動到最頂部或者最底部之後禁止滾動)。 單獨處理浮層中需要滾動的元素;

targetElement.ontouchmove = function (event) {
    const clientY = event.targetTouches[0].clientY - initialClientY

    if (targetElement && targetElement.scrollTop === 0 && clientY > 0) {
        return preventDefault(event)
    }

    if (targetElement && (targetElement.scrollHeight - 1 - targetElement.scrollTop <= targetElement.clientHeight) && clientY < 0) {
        return preventDefault(event)
    }

    event.stopPropagation()
    return true
}
複製程式碼

這個方案在ios中完美實現,但是在 android中還是有一點問題;浮層內容拉到最頂部或者最底部的時候依然會帶動頁面的內容有一定程度的移動。

滑動穿透(鎖body)終極探索

tua-body-scroll-lock

終極方案來啦!

tua-body-scroll-lock即是在iosandroidPC各個端單獨處理,保證在每個端都可以實現完美的效果!

demo

安裝

$ npm i -S tua-body-scroll-lock
# OR
$ yarn add tua-body-scroll-lock
複製程式碼

使用

移動端
import { lock, unlock } from 'tua-body-scroll-lock'

// 禁止滑動後還需要內部可以滾動的元素(針對移動端ios處理)
const targetElement = document.querySelector("#someElementId");

lock(targetElement)
unlock(targetElement)
複製程式碼
PC端

tips: PC端不需要targetElement, 不傳targetElement也不想要控制檯提示可以傳null

import { lock, unlock } from 'tua-body-scroll-lock'

lock()
unlock()
複製程式碼

相關文章