分享一些實際開發過程當中遇到的問題和解決方案,文中若有不對之處,歡迎你們指出,共勉。!javascript
我的博客地址 🍹🍰 fe-codecss
源於最近的一個移動端走馬燈需求,使用 touchmove 事件,來觸發走馬燈的動畫。可是在實際運行時發現,滑動走馬燈的時候很容易觸發頁面自身垂直方向的滾動,以下圖html
注:這裏用
overflow: auto
模擬走馬燈,只作 touchmove 的測試。前端
能夠看出,在滑動過程當中,滑動方向一旦偏向垂直方向,就會觸發頁面的垂直滾動。vue
由於是 touchmove 事件觸發的垂直滾動,因此很容易就想到了經過 e.preventDefault()
來禁用事件的默認行爲,又很容易就改了代碼。java
function Touch() {
const startTouchRef = useRef({x: 0, y: 0});
// 保存初始位置
function onTouchStart(e) {
startTouchRef.current = { x: e.touches[0].pageX, y: e.touches[0].pageY };
}
// 限制垂直方向上的滾動
function onTouchMove(e) {
const y = Math.abs(e.touches[0].pageY - startTouchRef.current.y);
const x = Math.abs(e.touches[0].pageX - startTouchRef.current.x);
// 簡單判斷滑動方向是傾向於 y 仍是 x
// 禁止 x 方向的默認滾動,由於 x 方向的滾動會經過 Touchmove 或者 css 動畫 實現
if (y < x) {
e.preventDefault();
}
}
return (
<div onTouchStart={onTouchStart} onTouchMove={onTouchMove}> // ... </div>
)
}
複製代碼
最後很容易獲得了一個報錯。node
一我的性化的報錯,讓咱們去查看 www.chromestatus.com/features/50… 這個 url。git
大意是說:addEventListener 有一個參數 passive 默認是 false,可是在 Chrome 56 的時候 把 touchstart 和 touchmove 改爲了默認 passive: true
。這樣,touchmove 事件就不會阻塞頁面的滾動。由於在 passive: false
的狀態下,不論是否須要調用 e.preventDefault()
來阻止頁面滾動,都須要等到 touchmove 函數執行完畢,頁面纔會作出反應。github
作一個簡單的測試。mongodb
// 沒有阻止頁面滾動,僅僅是增長了事件處理的時間
function Touch() {
const ref = useRef(null);
function onTouchMove(e) {
console.time();
let index = 0;
for (let i = 0; i< 1000000000; i++) {
index++;
}
console.timeEnd();
}
useEffect(() => {
ref.current.addEventListener('touchmove', onTouchMove, { passive: false });
return () => {
ref.current.removeEventListener('touchmove', onTouchMove, { passive: false });
};
}, []);
return (
<div > // ... </div>
)
}
複製代碼
每次滑動後頁面的響應明顯卡頓,由於瀏覽器須要等 touchmove 執行完才知道是否須要禁止默認滾動。而將 passive 設爲 true 後,瀏覽器將不考慮禁用默認行爲的可能性,會當即觸發頁面行爲。
固然,若是確實要阻止默認行爲,就像我以前的那個需求同樣,就須要手動設置 passive 是 false,而後正常使用 preventDefault 就好。不過,不論是哪一種方式,咱們都須要優化本身的執行代碼,儘可能減小時間代碼運行時間。不然,還會看到如下警告:
關於被動事件監聽,更多的優化是在移動端,pc 端貌似較少處理。我這裏只測試了 mousewheel,在 pc 的 Chrome 74 下,儘管設置成了 passive: true
,也沒有優先觸發頁面的滾動行爲。可是,在移動端模式下,是能夠的。你們有興趣的也能夠本身測試一下。
由於 Chrome 56以上才支持 passive,因此在使用時可能須要作一下兼容性測試。代碼來自 MDN。
// 若是觸發對 options 取值 passive 的狀況,說明支持 passive 屬性
var passiveSupported = false;
try {
var options = Object.defineProperty({}, "passive", {
get: function() {
passiveSupported = true;
}
});
window.addEventListener("test", null, options);
} catch(err) {}
someElement.addEventListener("mouseup", handleMouseUp, passiveSupported
? { passive: true } : false);
複製代碼
用於設置觸摸屏用戶如何操縱元素的區域(例如,瀏覽器內置的縮放功能)。 — MDN
這是一個 css 屬性,簡單來講,就是能夠經過 css 指定容許用戶使用的手勢操做。
其餘屬性,你們能夠去 MDN 自行查閱。結合咱們的需求,使用 pan-y 只開啓垂直方向的操做,也能作到相似的效果。須要注意的是,設置 touch-action,和咱們設置 passive: false
再調用 preventDefault 效果是同樣的,不會再對容許操做方向上的滑動效果進行優化。
另外,這個屬性也有兼容性問題,在 Safari 上的支持效果並很差,具體查看 can i use。
對於元素的禁止滾動,其實咱們給他的父元素添加 overflow: hidden
也能達到想要的效果。對於整個頁面來講,就須要給 html 標籤添加 overflow: hidden。可是,基於當前這個需求場景,由於只是但願在水平滑動時不觸發垂直方向的滾動,因此須要判斷何時設置屬性,何時移除屬性。
這裏我沒有具體去作這個測試,只是提供一種思路。
qq前端交流羣:960807765,歡迎各類技術交流,期待你的加入;
微信羣:有須要的同窗能夠加我微信(q1324210213),我拉你入羣。
若是你看到了這裏,且本文對你有一點幫助的話,但願你能夠動動小手支持一下做者,感謝🍻。文中若有不對之處,也歡迎你們指出,共勉。好了,又耽誤你們的時間了,感謝閱讀,下次再見!
感興趣的同窗能夠關注下個人公衆號 前端發動機,好玩又有料。