前言
現在一直在做移動端的開發,這次将單頁應用的網頁内嵌入了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,如需轉載請自行聯系原作者