zepto源码之touch.js
来源:互联网 发布:饺子 知乎 编辑:程序博客网 时间:2024/06/05 05:11
一、前言
touch.js主要提供滑动(swipe)与点击(tap:模拟click)的事件封装,针对手机常用浏览器(touchstart,touchmove,touchend)和IE10(msPointDown)
的触摸事件兼容处理以及手势的事件处理。
二、源码
//touch事件 在触摸设备上触发tap–和swipe–相关事件。这适用于所有的`touch`(iOS, Android)和`pointer`事件(Windows Phone)。 (function ($) { var touch = {}, //延迟触发(ms): 250, 0, 0, 750 touchTimeout, tapTimeout, swipeTimeout, longTapTimeout, longTapDelay = 750, gesture; //判断滑动方向,返回Left, Right, Up, Down function swipeDirection(x1, x2, y1, y2) { return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') } //长按 function longTap() { //定时器执行了没有清空的必要 longTapTimeout = null; if (touch.last) { touch.el.trigger('longTap'); touch = {} } } //取消长按 function cancelLongTap() { if (longTapTimeout) clearTimeout(longTapTimeout); longTapTimeout = null } //取消所有 function cancelAll() { if (touchTimeout) clearTimeout(touchTimeout); if (tapTimeout) clearTimeout(tapTimeout); if (swipeTimeout) clearTimeout(swipeTimeout); if (longTapTimeout) clearTimeout(longTapTimeout); touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null; touch = {} } //判断是否是点击指针是否为主指针(http://www.ayqy.net/blog/html5%E8%A7%A6%E6%91%B8%E4%BA%8B%E4%BB%B6/) function isPrimaryTouch(event) { return (event.pointerType == 'touch' || event.pointerType == event.MSPOINTER_TYPE_TOUCH) && event.isPrimary } //判断是否为鼠标事件或者ie的点击事件 function isPointerEventType(e, type) { return (e.type == 'pointer' + type || e.type.toLowerCase() == 'mspointer' + type) } //加载完全时执行,将事件绑定到全局的document $(document).ready(function () { var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType; //IE的手势 if ('MSGesture' in window) { gesture = new MSGesture(); gesture.target = document.body } $(document) .bind('MSGestureEnd', function (e) { //处理IE手势结束 var swipeDirectionFromVelocity = e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null; if (swipeDirectionFromVelocity) { touch.el.trigger('swipe'); touch.el.trigger('swipe' + swipeDirectionFromVelocity) } }) .on('touchstart MSPointerDown pointerdown', function (e) { //处理手指接触事件 //屏蔽掉非触摸设备(非触屏设备鼠标事件isPrimary属性为true) if ((_isPointerType = isPointerEventType(e, 'down')) && !isPrimaryTouch(e)) return; //记录起点坐标(_isPointerType用于ie) firstTouch = _isPointerType ? e : e.touches[0]; //重置终点坐标(正常情况下touch会在touchend或cancelAll中清空,除了因为preventDefault等导致touchcancel不被触发) if (e.touches && e.touches.length === 1 && touch.x2) { touch.x2 = undefined; touch.y2 = undefined } //当前时间毫秒数 now = Date.now(); //距离上次触碰的时间差 delta = now - (touch.last || now); //点击元素 如果是正常的dom元素,则赋值于touch.el;若是非正常元素,比如伪元素,则touch源是其父元素 touch.el = $('tagName' in firstTouch.target ? firstTouch.target : firstTouch.target.parentNode); //重置touch延迟事件处理器 touchTimeout && clearTimeout(touchTimeout); //记录点击起点坐标 touch.x1 = firstTouch.pageX; touch.y1 = firstTouch.pageY; //判断是否双击 if (delta > 0 && delta <= 250) touch.isDoubleTap = true; //将当期时间毫秒数存储到touch的last属性上 touch.last = now; //注册长按延迟事件处理器 longTapTimeout = setTimeout(longTap, longTapDelay); //支持IE手势识别 if (gesture && _isPointerType) gesture.addPointer(e.pointerId); }) .on('touchmove MSPointerMove pointermove', function (e) { //处理手指滑动 if ((_isPointerType = isPointerEventType(e, 'move')) && !isPrimaryTouch(e)) return; firstTouch = _isPointerType ? e : e.touches[0]; //取消长按事件处理器(touchstart设置的) cancelLongTap(); //设置touch对象的位置 touch.x2 = firstTouch.pageX; touch.y2 = firstTouch.pageY; deltaX += Math.abs(touch.x1 - touch.x2); deltaY += Math.abs(touch.y1 - touch.y2); }) .on('touchend MSPointerUp pointerup', function (e) { //处理手指离开 if ((_isPointerType = isPointerEventType(e, 'up')) && !isPrimaryTouch(e)) return; //取消长按事件处理器(touchstart设置的) cancelLongTap(); //swipe 判定滑动动作(起点 - 终点的横向或者纵向距离超过30px) if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) //延迟执行所以可以在scroll事件触发时取消执行swipe事件 swipeTimeout = setTimeout(function () { //根据方向触发滑动事件,并清空touch对象(所有touchend的回调触发自定义事件都作了判断是否touch对象有el属性,屏蔽比如滚动时同时触发了滑动事件等奇怪的现象) if (touch.el) { touch.el.trigger('swipe'); touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) } touch = {} }, 0); //一般情况下,touch是会存在last属性的;如果长按,那么touch会被赋空值,就不存在last属性了 else if ('last' in touch) //当位置发生改变超过30px,不会被触发(比如移动到一个位置然后返回原先的位置) if (deltaX < 30 && deltaY < 30) { //立即准备执行轻触,不立即执行是为了scroll时能取消执行轻触(tap触发在scroll之前) tapTimeout = setTimeout(function () { // 触发全局tap,cancelTouch可以取消singleTap,doubleTap事件,以求更快响应轻触 var event = $.Event('tap'); event.cancelTouch = cancelAll; // [by paper] fix -> "TypeError: 'undefined' is not an object (evaluating 'touch.el.trigger'), when double tap if (touch.el) touch.el.trigger(event); //立即触发doubleTap if (touch.isDoubleTap) { if (touch.el) touch.el.trigger('doubleTap'); touch = {} } // 250ms后触发singleTap,因为需要判断tap后250内是否还有tap,如果有则触发doubleTap;否则触发singleTap else { touchTimeout = setTimeout(function () { //定时器已经执行了,没有必要clear touchTimeout = null; if (touch.el) touch.el.trigger('singleTap'); touch = {} }, 250) } }, 0) } else { /** * 当位置距离总和deltaX或deltaY大于30,并且起点到终点距离小于30 * 即存在这样一种情况,移动到一个位置,然后返回原先的位置 * 则直接清空touch对象,并不执行任何操作 * */ touch = {} } //重置横向,纵向滑动距离 deltaX = deltaY = 0; }) //当浏览器窗口失去焦点(如模态框弹窗显示),取消所有将要发生的事件 .on('touchcancel MSPointerCancel pointercancel', cancelAll); //浏览器窗口滚动时,取消所有将要发生的事件,因为滚动window意味着用户的意图是滚动而不是tap或者swipe $(window).on('scroll', cancelAll) }); //绑定自定义回调函数到自定义事件上 ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function (eventName) { $.fn[eventName] = function (callback) { return this.on(eventName, callback) } }) })(Zepto);
三、源码分析
1、MSPointerDown/pointerdown
由于IE浏览器的触碰事件使用的是MSPointerDown/pointerdown等这样的鼠标事件来标识的,所以为了兼容IE浏览器也需要监听此类事件。在webkit内核的
移动端浏览器上touch.js可以正常运行,但是在chrome浏览器模拟移动端时会被触发两次,一次是监听到鼠标事件(pointerdown),一次是监听到touch事件
2、longTap
原生JS并没有提供longTap之类事件,所以zepto自己封装实现了此类事件。首先在touchstart时设置一个定时器750ms,如果在此区间没有触发touchmove、
touchend、touchcancel以及scroll事件,那么说明是长按事件,触发相应回调。
3、doubleTap
zepto同时也封装实现了doubleTap。在touchstart时设置延迟变量为这次与上次点击时间差,如果大于0(第一次点击为0)并且小于250,那么设置touch对象
双击标志量isDoubleTap为true,并在touchend触发doubleTap事件。
4、tap vs swipe
在touchend中判断起点坐标与终点坐标距离如果超过了30px,那么触发swipe事件,并触发swipe相应方向事件。tap需判断累计距离不超过30px,如果超过
30px可能存在移动到某个点再返回回来这种情况。
5、singleTap
在touchend中设置定时器250ms,如果在此区间再次点击则清掉定时器,否则触发singleTap事件。
6、scroll
窗口滚动时,可以监听到scroll事件。
- zepto源码之touch.js
- 读Zepto源码之Touch模块
- zepto源码之form.js
- zepto源码之event.js
- zepto源码之data.js
- zepto源码之ajax.js
- zepto源码之callbacks.js
- zepto源码之deferred.js
- zepto源码之fx.js
- zepto源码之fx_methods.js
- Zepto源码解读-zepto.js
- Zepto.js 源码解读
- Zepto.js源码分析
- 一步一步DIY zepto库,研究zepto源码8 -- touch模块
- Zepto.js 之一步一步看源码(二)
- Zepto源码之Deferred模块
- Zepto源码之Callbacks模块
- zepto.js源码解读(二):zepto.init函数
- RXD and dividing(HDU 6060)
- 自定义view时一些概念的含义
- 《道德经》第五十三章
- 一首freestyle送给求职的你:面试通关必经路
- 动态代理和静态代理
- zepto源码之touch.js
- C++之明智而审慎地使用多重继承(40)---《Effective C++》
- JAVA中的抽象类
- 【mysql 优化 4】嵌套连接优化
- hdu 6052 To my boyfriend(枚举+容斥)
- 手机端qq客服代码点击弹出qq聊天窗http://www.51xuediannao.com/jiqiao/1026.html
- Yaf 扩展安装
- Redis做缓存的简单实例
- 学习OpenCL开发(四)shader