移動端的 touch 事件處理
簡要的探討一下移動端 touch 事件處理幾個坑,以及相應的幾種簡單處理方法。
click 穿透
假設有個彈出層,上面有個關閉的按鈕支持 touchend 觸發後關閉,若正好下方有個元素支持 click 事件,在彈出層關閉後將會在下方元素觸發 click 事件。這種效果肯定不是我們需要的,而且我們無法確定合適會在上方出現一個支持 touch 的彈出層,所以我認為最好的處理方式是禁用所有元素的 click 事件,相比 click 需要長達 1s 的觸發時間,使用 touchend 可以獲得更好的體驗。
tap 事件的判定
一個正確的 tap 事件應當滿足一下條件:
- 用戶手指從屏幕移開時觸發
- 不能在用戶移動手指時觸發(防止和滾動、拖拽事件的衝突)
- 多個手指同時觸摸屏幕時不能觸發
- 不應該觸發 click 事件
具體實現代碼可以參考 tap-event。我個人也有一個 tap-event,這個分支支持綁定後函數作為事件代理函數,同時也增加了移動位置的判定。
使用原生的滾動事件
Android 4.0 以下是不支持原生的 webview 滾動的,所以只能使用 iscroll 之類的工具來模擬元素滾動。它的缺點就是有些過於的複雜,所以我還是會在條件允許的情況下使用原生的滾動。
啟用原生滾動只需要給外層元素加上樣式 -webkit-overflow-scrolling: touch; 即可,如果你的監聽函數比較佔用資源我們可以通過一個簡單的 buffer 函數來限制它的觸發間隔,例如:
function buffer(fn, ms) { var timeout; return function() { if (timeout) return; var args = arguments; timeout = setTimeout(function() { timeout = null; fn.apply(null, args); }, ms); }}document.querySelector(.scrollable).onscroll = buffer(onScroll, 100);
另外的建議就是不要在可滾動元素上使用陰影樣式(text-shadow 和 box-shadow),因為它們非常影響性能,而且看上去也不怎麼美觀。
還有需要注意的是如果你需要啟用apple-mobile-web-app-capable, 注意將apple-mobile-web-app-status-bar-style設置為black-translucent,否則會出現還差 22 像素滾動不到頭的坑爹 bug。
如果需要很好的兼容性,或者 pull refresh 等功能,也可以考慮使用我做的簡化版 iscroll。
禁用頁面整體拖動
IOS下默認情況下用戶的拖動操作在scroll滾到頭以後會導致整體頁面的滾動,一種方式是禁用掉 document 的 touchmove 原生觸發
document.addEventListener(touchmove, function(e) { e.preventDefault();});
此時原生的滾動是無法工作的,解決辦法就是禁用滾動元素的 touchmove 事件冒泡js
scrollable.addEventListener(touchmove, function (e) e.stopPropagation(); });
另一種方式是判定滾動元素滾到頭之後禁用掉默認的處理
var el = document.querySelector(.scrollable);var sy = 0;events.bind(el, touchstart, function (e) { sy = e.pageY;})events.bind(el, touchmove, function (e) { var down = (e.pageY - sy > 0); //top if (down && el.scrollTop <= 0) { e.preventDefault(); } //bottom if (!down && el.scrollTop >= el.scrollHeight - el.clientHeight) { e.preventDefault(); }})
我個人傾向於第二種方案,因為如果單純的禁用 document 的 touchmove 監聽,會導致一些處理的失效,比如說上面提到的 tap-event 模塊。
這裡有個模塊 scrollfix 使用了第一種方案。
拖動方向與距離
通過 event 的 clientX 和 clientY 即可計算。
具體實現可以參考 swipe,以及我的移動端組件 swipe-itsweet-sortable。
IOS 邊緣 touch 事件無法生效
IOS 9.0.3 之後綁定在屏幕邊緣的 touch 事件常常會無效,猜測為底層實現的邊緣 swipe 操作事件綁定導致衝突所致,一個解決辦法是針對 IOS 綁定 click 事件, 因為 IOS 解決了 click 延遲的問題,所以並不會感受到明顯延遲。
最後
禁用 click 的前提下,移動端事件其實沒什麼複雜的東西,只要理解一些基礎的技巧,就可以做出很好的體驗了。
推薦閱讀:
※移動開發每周閱讀清單:第五十四期
※我的第一個Flutter 應用被蘋果推薦了
※移動開發每周閱讀清單:第五十五期
※如何根據你的網站創建一個移動 APP?
TAG:移動開發 |