前言
现在一直在做移动端的开发,这次将单页应用的网页内嵌入了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事件需要自己模拟,这个我们后面来看看
1 var startTime;
2 var log = function (msg) {
3 console.log(new Date().getTime() - startTime);
4 console.log(msg);
5 };
6 var touchStart = function () {
7 startTime = new Date().getTime();
8 log('touchStart');
9 };
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事件的参数差异
1 var startTime;
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);
12 };
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);
25
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
8 };
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事件
测试效果
完整可绑定事件代码
View Code
这个代码基本可用了,但是使用上不是很方便,我们这里就不关注了,下面我们来看看zepto的代码和兼容问题
zepto的touch与兼容
先上zepto源码,一看就知道我写的有多不行啦!
View Code
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,如需转载请自行联系原作者