標籤:
簡要的探討一下移動端 touch 事件處理幾個坑,以及相應的簡單處理方法。
click 穿透
假設有個彈出層,上面有個關閉的按鈕支援 touchend 觸發後關閉,若正好下方有個元素支援 click 事件,在彈出層關閉後將會在下方元素觸發 click 事件。這種效果肯定不是我們需要的,而且我們無法確定合適會在上方出現一個支援 touch 的彈出層,所以我認為最好的處理方式是禁用所有元素的 click 事件,相比 click 需要長達 1s 的觸發時間,使用 touchend 可以獲得更好的體驗。
tap 事件的判定
一個正確的 tap 事件應當滿足一下條件:
- 使用者手指從螢幕移開時觸發
- 不能在使用者移動手指時觸發(防止和滾動、拖拽事件的衝突)
- 多個手指同時觸控螢幕幕時不能觸發
- 不應該觸發 click 事件
具體實現代碼可以參考 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),因為它們非常影響效能,而且看上去也不怎麼美觀。
禁用頁面整體拖動
預設情況下使用者的拖動操作在scroll滾到頭以後會導致整體頁面的滾動,一種方式是禁用掉 document 的 touchmove 原生觸發
events.bind(document, ‘touchmove‘, function (e) { e.preventDefault();});
此時原生的滾動是無法工作的,解決辦法就是禁用滾動元素的 touchmove 事件冒泡
events.bind(scrollable, ‘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 模組。
拖動方向與距離
通過 event 的 pageX 和 pageY 屬性即可計算,
移動端的touch事件處理