天天看點

手持裝置點選響應速度,滑鼠事件與touch事件的那些事

前言

現在一直在做移動端的開發,這次将單頁應用的網頁内嵌入了app,于是老大反映了一個問題:

app應用點選響應慢!

我開始不以為然,于是拿着網頁版的試了試,好像确實有一定延遲,于是開始了研究,最後選擇了touch取代滑鼠事件

但是,touch事件取代mouse事件,還是有一定問題的,據說網上問題很多,因為兩者之間還是有一定差異

而且如果完全使用touch事件,對自動化測試的同僚來說,他們的系統根本不支援touch事件,再者我們平時網頁開發也不友善

是以,了解滑鼠事件與touch事件的差別,探讨滑鼠事件與touch事件的相容也是有必要的,于是我們開始今天的學習吧

PS:這裡使用zepto架構,懶得自己搞了......

事件差異

滑鼠事件

首先,我們來看看滑鼠事件相關吧:

 1 var startTime;

 2 var log = function (msg) {

 3     console.log(new Date().getTime() - startTime);

 4     console.log(msg);

 5 };

 6 var mouseDown = function () {

 7     startTime = new Date().getTime();

 8     log('mouseDown');

 9 };

10 var mouseClick = function () {

11     log('mouseClick');

12 };

13 var mouseUp = function () {

14     log('mouseUp');

15 };

16  

17 document.addEventListener('mousedown', mouseDown);

18 document.addEventListener('click', mouseClick);

19 document.addEventListener('mouseup', mouseUp);

從這裡看到了,滑鼠順序是有mousedown -> click -> mouseup 的順序,其時間差也出來了

touch事件

然後我們看看touch事件

沒有click

touch包含三個事件,touchstart、touchmove、touchend,并沒有click事件,是以click事件需要自己模拟,這個我們後面來看看

 6 var touchStart = function () {

 8     log('touchStart');

10 

11 var touchEnd = function () {

12     log('touchEnd');

13 };

14 

15 document.addEventListener('touchstart', touchStart);

16 document.addEventListener('touchend', touchEnd);

在chrome開啟touch事件的情況下,可以看到這個結果

混合事件

現在我們在手機上同時觸發兩者事件看看差別,這裡代碼做一定修改

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 2 <html xmlns="http://www.w3.org/1999/xhtml">

 3 <head>

 4     <title></title>

 5     <script id="others_zepto_10rc1" type="text/javascript" class="library" src="http://sandbox.runjs.cn/js/sandbox/other/zepto.min.js"></script>

 6 </head>

 7 <body>

 8     <div id="d" style="width: 100px; height: 100px; border: 1px solid black;">

 9     </div>

10 </body>

11 <script type="text/javascript">

12     var startTime;

13     var log = function (msg) {

14         var div = $('<div></div>');

15         div.html((new Date().getTime()) + ': ' + (new Date().getTime() - startTime) + ': ' + msg)

16         $('body').append(div);

17 

18     };

19     var touchStart = function () {

20         startTime = new Date().getTime();

21         log('touchStart');

22     };

23     var touchEnd = function () {

24         log('touchEnd');

25 

26     };

27     var mouseDown = function () {

28         log('mouseDown');

29     };

30     var mouseClick = function () {

31         log('mouseClick');

32     };

33     var mouseUp = function () {

34         log('mouseUp');

35 

36     };

37     var d = $('#d');

38     d.bind('mousedown', mouseDown);

39     d.bind('click', mouseClick);

40     d.bind('mouseup', mouseUp);

41     d.bind('touchstart', touchStart);

42     d.bind('touchend', touchEnd);

43 </script>

44 </html>

測試位址

http://sandbox.runjs.cn/show/ey54cgqf

此處手機與電腦有非常大的差別!!!

結論

不要同時給document綁定滑鼠與touch事件

document.addEventListener('mousedown', mouseDown);

document.addEventListener('click', mouseClick);

document.addEventListener('mouseup', mouseUp);

document.addEventListener('touchstart', touchStart);

document.addEventListener('touchend', touchEnd);

這個樣子,在手機上不會觸發click事件,click事件要綁定到具體元素

PS:此處的原因我就不去研究了,如果您知道為什麼,請留言

手機上mousedown本來響應就慢

經過測試,電腦上touch與click事件的差距不大,但是手機上,當我們手觸碰螢幕時,要過300ms左右才會觸發mousedown事件

是以click事件在手機上響應就是慢一拍

資料說明

可以看到,在手機上使用click事件其實對使用者體驗并不好,是以我們可能會逐漸使用touch事件

參數差異

現在,我們來看看滑鼠與touch事件的參數差異

 2 var log = function (msg, e) {

 3     console.log(e);

 4     var div = $('<div></div>');

 5     div.html((new Date().getTime()) + ': ' + (new Date().getTime() - startTime) + ': ' + msg)

 6     $('body').append(div);

 7 

 8 };

 9 var touchStart = function (e) {

10     startTime = new Date().getTime();

11     log('touchStart', e);

13 var touchEnd = function (e) {

14     log('touchEnd', e);

15 

16 };

17 var mouseDown = function (e) {

18     log('mouseDown', e);

19 };

20 var mouseClick = function (e) {

21     log('mouseClick', e);

22 };

23 var mouseUp = function (e) {

24     log('mouseUp', e);

26 };

27 var d = $('#d');

28 d.bind('mousedown', mouseDown);

29 d.bind('click', mouseClick);

30 d.bind('mouseup', mouseUp);

31 d.bind('touchstart', touchStart);

32 d.bind('touchend', touchEnd);

事件參數(touchstart/mouseup)

我們來看幾個關鍵的地方:

changedTouches/touches/targetTouches

touches:為螢幕上所有手指的資訊

PS:因為手機螢幕支援多點觸屏,是以這裡的參數就與手機有所不同

targetTouches:手指在目标區域的手指資訊

changedTouches:最近一次觸發該事件的手指資訊

比如兩個手指同時觸發事件,2個手指都在區域内,則容量為2,如果是先後離開的的話,就會先觸發一次再觸發一次,這裡的length就是1,隻統計最新的

PS:一般changedTouches的length都是1

touchend時,touches與targetTouches資訊會被删除,changedTouches儲存的最後一次的資訊,最好用于計算手指資訊

這裡要使用哪個資料各位自己看着辦吧,我也不是十厘清晰(我這裡還是使用changedTouches吧)

參數資訊(changedTouches[0])

幾個重要通用點:

① clientX:在顯示區的坐标

② pageX:滑鼠在頁面上的位置

③ screenX:滑鼠在顯示屏上的坐标(我是雙屏是以x很大)

④ target:目前元素

幾個重要不同點:

① layerX:這個是相對距離,這個不同,是以不要用這個東西了

② ......

這個有必要說明下,比如我們改下代碼:

 View Code

http://sandbox.runjs.cn/show/7tyo48bf

各位自己運作看看差異吧

簡單擴充touch事件

touch沒有click事件,于是有zepto搞了個tap事件,我們這裡先來簡單模拟一下,再看源碼怎麼幹的

 1 var mouseData = {

 2     sTime: 0,

 3     eTime: 0,

 4     sX: 0,

 5     eX: 0,

 6     sY: 0,

 7     eY: 0

 9 var log = function (msg) {

10     console.log(msg);

11 };

12 var touchStart = function (e) {

13     var pos = e.changedTouches[0];

14     mouseData.sTime = new Date().getTime();

15     mouseData.sX = pos.pageX;

16     mouseData.sY = pos.pageY;

17 };

18 var touchMove = function (e) {

19     //        var pos = e.changedTouches[0];

20     //        mouseData.eTime = new Date().getTime();

21     //        mouseData.eX = pos.pageX;

22     //        mouseData.eY = pos.pageY;

23     e.preventDefault();

24     return false;

25 };

26 var touchEnd = function (e) {

27     var pos = e.changedTouches[0];

28     mouseData.eTime = new Date().getTime();

29     mouseData.eX = pos.pageX;

30     mouseData.eY = pos.pageY;

31     var data = onTouchEnd();

32     log(data);

33     var d = $('body');

34     d.append($('<div>間隔:' + data.timeLag + ', 方向:' + data.dir + '</div>'));

35 };

36 var onTouchEnd = function () {

37     //時間間隔

38     var timeLag = mouseData.eTime - mouseData.sTime;

39     //移動狀态,預設亂移動

40     var dir = 'move';

41     if (mouseData.sX == mouseData.eX) {

42         if (mouseData.eY - mouseData.sY > 0) dir = 'down';

43         if (mouseData.eY - mouseData.sY < 0) dir = 'up';

44         if (mouseData.eY - mouseData.sY == 0) dir = 'tap';

45     }

46     if (mouseData.sY == mouseData.eY) {

47         if (mouseData.eX - mouseData.sX > 0) dir = 'right';

48         if (mouseData.eX - mouseData.sX < 0) dir = 'left';

49         if (mouseData.eX - mouseData.sX == 0) dir = 'tap';

50     }

51     return {

52         timeLag: timeLag,

53         dir: dir

54     };

55 };

56 

57 var touchEvents = function (el, func) {

58     el = el || document;

59     func = func || function () { };

60     el.addEventListener('touchstart', touchStart);

61     el.addEventListener('touchmove', touchMove);

62     el.addEventListener('touchend', touchEnd);

63 };

64 var d = $('body');

65 touchEvents(d[0]);

http://sandbox.runjs.cn/show/2n9nqssv

這裡就可以看到一次touch事件是tap還是up等屬性,當然很多時候我們需要設定x方向或者y方向不可拖動,這樣就更好呈現

時間間隔長短可以讓我們判斷自己的拖動是長拖動還是短拖動,長拖動也許使用者希望動畫慢點,短拖動也許動畫就快了

touch事件代碼彙總

  1 var log = function (msg) {

  2     console.log(msg);

  3 };

  4 var d = $('body');

  5 

  6 var touchEvents = function (el, type, func) {

  7     this.long = 400; //用于設定長點選閥值

  8     this.el = el || document;

  9     this.func = func || function () { };

 10     this.type = type || 'tap';

 11     this.mouseData = {

 12         sTime: 0,

 13         eTime: 0,

 14         sX: 0,

 15         eX: 0,

 16         sY: 0,

 17         eY: 0

 18     };

 19     this.addEvent();

 20 

 21 };

 22 touchEvents.prototype = {

 23     constructor: touchEvents,

 24     addEvent: function () {

 25         var scope = this;

 26         this.startFn = function (e) {

 27             scope.touchStart.call(scope, e);

 28         };

 29         this.moveFn = function (e) {

 30             scope.touchMove.call(scope, e);

 31         };

 32         this.endFn = function (e) {

 33             scope.touchEnd.call(scope, e);

 34         };

 35         this.el.addEventListener('touchstart', this.startFn);

 36         //此處可以換成這樣

 37         //            document.addEventListener('touchmove', this.touchMove);

 38         this.el.addEventListener('touchmove', this.moveFn);

 39         this.el.addEventListener('touchend', this.endFn);

 40     },

 41     removeEvent: function () {

 42         this.el.removeEventListener('touchstart', this.touchStart);

 43         this.el.removeEventListener('touchmove', this.touchMove);

 44         this.el.removeEventListener('touchend', this.touchEnd);

 45     },

 46     touchStart: function (e) {

 47         var pos = e.changedTouches[0];

 48         this.mouseData.sTime = new Date().getTime();

 49         this.mouseData.sX = pos.pageX;

 50         this.mouseData.sY = pos.pageY;

 51     },

 52     touchMove: function (e) {

 53         e.preventDefault();

 54         return false;

 55     },

 56     touchEnd: function (e) {

 57         var pos = e.changedTouches[0];

 58         this.mouseData.eTime = new Date().getTime();

 59         this.mouseData.eX = pos.pageX;

 60         this.mouseData.eY = pos.pageY;

 61         this.onTouchEnd();

 62     },

 63     onTouchEnd: function () {

 64         if (this.type == this._getDir()) {

 65 

 66         }

 67     },

 68     _getDir: function () {

 69         //時間間隔,間隔小于100都認為是快速,大于400的認為是慢速

 70         var timeLag = this.mouseData.eTime - this.mouseData.sTime;

 71         var dir = 'swipe';

 72         if (timeLag > this.long) dir = 'longSwipe';

 73         if (this.mouseData.sX == this.mouseData.eX && this.mouseData.sY == this.mouseData.eY) {

 74             dir = 'tap';

 75             if (timeLag > this.long) dir = 'longTap';

 76         } else {

 77             if (Math.abs(this.mouseData.eY - this.mouseData.sY) > Math.abs(this.mouseData.eX - this.mouseData.sX)) {

 78                 dir = this._getUDDir(dir);

 79             } else {

 80                 dir = 'swipe';

 81                 dir = this._getLRDir(dir);

 82             }

 83         }

 84         log(dir);

 85         d.append($('<div>間隔:' + timeLag + ', 方向:' + dir + '</div>'));

 86         return dir;

 87     },

 88     //單獨用于計算上下的

 89     _getUDDir: function (dir) {

 90         if (this.mouseData.eY - this.mouseData.sY > 0) dir += 'Down';

 91         if (this.mouseData.eY - this.mouseData.sY < 0) dir += 'Up';

 92         return dir;

 93     },

 94     //計算左右

 95     _getLRDir: function (dir) {

 96         if (this.mouseData.eX - this.mouseData.sX > 0) dir += 'Right';

 97         if (this.mouseData.eX - this.mouseData.sX < 0) dir += 'Left';

 98         return dir;

 99     }

100 };

101 

102 new touchEvents(d[0], 'swipe', function () {

103 //        d.append($('<div>間隔:' + data.timeLag + ', 方向:' + data.dir + '</div>'));

104 });

http://sandbox.runjs.cn/show/rpohk79w

測試時請使用chrome,并且開啟touch事件

測試效果

完整可綁定事件代碼

這個代碼基本可用了,但是使用上不是很友善,我們這裡就不關注了,下面我們來看看zepto的代碼和相容問題

zepto的touch與相容

先上zepto源碼,一看就知道我寫的有多不行啦!

touch對象與上面mouseData功效相同,記錄一些屬性

delta 用于記錄兩次點選的間隔,間隔短就是輕按兩下

swipeDirection 函數與_getDir _getUDDir _getLRDir 功能相似,隻不過代碼更為簡練,并且真正的私有化了

63行代碼開始,若是代碼移動過便是劃屏,否則就是點選,這點我也沒考慮到

73行,否則就應該是點選,這裡并且判斷是否存在結束時間,代碼比較健壯,做了輕按兩下或者快速點選的判斷

開始相容

zepto代碼我自然沒有資格去評說,現在我們來看看他的相容問題

PS:我這裡很水,不太敢動源碼,就加一個tap判斷,因為也隻是用了這個,具體大動手腳的事情,我們後面再做

這樣做事因為,我們的項目主要是把click改成了tap事件,導緻頁面很多功能不可用

1 ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function (m) {

2   //相容性方案處理,以及後期資源清理,如果為假時候,就觸發點選事件

3     var isTouch = 'ontouchstart' in document.documentElement;

4     if(m === 'tap' && isTouch === false) {

5         $.fn[m] = function (callback) { return this.bind('click', callback) }

6     } else {

7         $.fn[m] = function (callback) { return this.bind(m, callback) }

8     }

9   })

我就幹了這麼一點點事情......

待續

今天耗時過長,暫時到這裡,對滑鼠等操作,對event參數的相容我們後面點再看看

本文轉自葉小钗部落格園部落格,原文連結http://www.cnblogs.com/yexiaochai/p/3377900.html,如需轉載請自行聯系原作者

繼續閱讀