epto touch 库源码分析
所谓 zepto 的 touch 其实就是指这个文件啦,可以看到区区 165 行(包括注释)就完成了 swipe 和 tap 相关的事件实现。在正式开始分析源码之前,我们先说说 touch 相关的几个事件,因为无论是 tap 还是 swipe 都是基于他们的。
touch 相关事件
touchstart 触摸屏幕的瞬间
touchmove 手指在屏幕上的移动过程一直触发
touchend 离开屏幕的瞬间
touchcancel 触摸取消(取决于浏览器实现,并不常用)
触摸屏下事件触发顺序是 touchstart -> touchmove -> touchend -> click
引入 touch 的背景
click事件在移动端上会有 300ms 的延迟,同时因为需要 长按,双触击 等富交互,所以我们通常都会引入类似 zepto 这样的库。zepto 实现了`'swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown',
'doubleTap', 'tap', 'singleTap', 'longTap'` 这样一些功能。
zepto touch 源码
我们直接看到 touch 源码的 49 行,从这里开始就是上述事件的实现了。不难想到 MSGesture 是对 mobile ie 的实现,本文不做讨论。往下面看到 66 行,$(document).on('touchstart MSPointerDown pointerdown') 开始
//判断事件类型是否为 touch
if((_isPointerType = isPointerEventType(e, 'down')) &&
!isPrimaryTouch(e)) return
// touches 是触摸点的数量
firstTouch = _isPointerType ? e : e.touches[0]
if (e.touches && e.touches.length === 1 && touch.x2) {
touch.x2 = undefined
touch.y2 = undefined
}
// 记录第一次触摸的时间
now = Date.now()
// 计算本次触摸与最后一次的时间差
delta = now - (touch.last || now)
// 查找 touch 事件的 dom
touch.el = $('tagName' in firstTouch.target ?
firstTouch.target : firstTouch.target.parentNode)
// 如果 touchTimeout 存在就清理掉
touchTimeout && clearTimeout(touchTimeout)
// 记录当前坐标
touch.x1 = firstTouch.pageX
touch.y1 = firstTouch.pageY
// 触摸时间差小于 250ms 则为 DoubleTap
if (delta > 0 && delta
接下来是 $(document).on('touchmove MSPointerMove pointermove')
//判断事件类型是否为 move
if((_isPointerType = isPointerEventType(e, 'move')) &&
!isPrimaryTouch(e)) return
firstTouch = _isPointerType ? e : e.touches[0]
// 一旦进入 move 就会清理掉 LongTap
cancelLongTap()
// 当前手指坐标
touch.x2 = firstTouch.pageX
touch.y2 = firstTouch.pageY
// x 轴和 y 轴的变化量 Math.abs 是取绝对值的意思
deltaX += Math.abs(touch.x1 - touch.x2)
deltaY += Math.abs(touch.y1 - touch.y2)
最后当然就是 $(document).on('touchend MSPointerUp pointerup') 了,这个也是整个 touch 最为复杂的一部分。
if((_isPointerType = isPointerEventType(e, 'up')) &&
!isPrimaryTouch(e)) return
cancelLongTap()
// 如果是 swipe,x 轴或者 y 轴移动超过 30pxif ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) swipeTimeout = setTimeout(function() { touch.el.trigger('swipe') // swipeDirection 是判断 swipe 方向的 touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) touch = {} }, 0)// tap 事件else if ('last' in touch) if (deltaX 0 && delta
整个读下来其实就是对 touchstart,touchmove,touchend 做了一些封装和判断,然后通过 zepto 自己的事件体系来注册和触发。
fastclick 对比 zepto
我们在聊到移动端 js 方案的时候很容易听到这两者,但我个人认为这两者是无法对比的。原因如下:zepto 是一个移动端的 js 库,而 fastclick 专注于 click 在移动端的触发问题。fastclick 的 github 主页上有一句话是Polyfill to remove click delays on browsers with touch UIs,翻译过来就是干掉移动端 click 延时的补丁。这个延时就是我们在引入 touch 的背景里提到过。
fastclick 源码分析
不愿意下代码的可以直接点这里github地址首先赞一下 fastclick 的代码注释,非常全。
fastclick 的使用非常简单,直接 FastClick.attach(document.body); 一句话搞定。所以源码分析就从 attach 方法来看吧,824 行
FastClick.attach = function(layer, options) { // 返回 FastClick 实例 layer 是一个 element 通常是 document.body ,options 自然就是配置了 return new FastClick(layer, options);};
接下来回到 23 行看到 FastClick 构造函数,
// 方法绑定,兼容老版本的安卓
function bind(method, context) {
return function() { return method.apply(context, arguments); };
}
var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
var context = this;
for (var i = 0, l = methods.length; i
FastClick.prototype.onTouchStart 和 zepto 一样做了一些参数的纪录,所以我这里就直接跳到 FastClick.prototype.onTouchEnd 看 fastclick 的核心。
FastClick.prototype.onTouchEnd = function(event) {
var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
if (!this.trackingClick) { return true;}// 防止 double tap 的时间间隔内 click 触发if ((event.timeStamp - this.lastClickTime) this.tapTimeout) { return true;}this.cancelNextClick = false;// 纪录当前时间this.lastClickTime = event.timeStamp;trackingClickStart = this.trackingClickStart;this.trackingClick = false;this.trackingClickStart = 0;if (deviceIsIOSWithBadTarget) { touch = event.changedTouches[0]; targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement; targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;}// 获取 targetTagName 上面的一段是 targetTagName 兼容性targetTagName = targetElement.tagName.toLowerCase();// 解决 label forif (targetTagName === 'label') { forElement = this.findControl(targetElement); if (forElement) { this.focus(targetElement); if (deviceIsAndroid) { return false; } targetElement = forElement; }} else if (this.needsFocus(targetElement)) { if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) { this.targetElement = null; return false; } // 解决 input focus this.focus(targetElement); // 触发 sendClick this.sendClick(targetElement, event); if (!deviceIsIOS || targetTagName !== 'select') { this.targetElement = null; event.preventDefault(); } return false;}if (deviceIsIOS && !deviceIsIOS4) { scrollParent = targetElement.fastClickScrollParent; if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) { return true; }}// 最后就来触发 sendClick 了if (!this.needsClick(targetElement)) { event.preventDefault(); this.sendClick(targetElement, event);}return false;
};
看完上面的代码,我们马上来解读 FastClick.prototype.sendClick
FastClick.prototype.sendClick = function(targetElement, event) {
var clickEvent, touch;
// 拿触摸的第一个手指
touch = event.changedTouches[0];
// 自定义 clickEvent 事件
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true;
// 触发 clickEvent 事件
targetElement.dispatchEvent(clickEvent);
};
到此 fastclick 主要的东西我们就看得差不多了,代码当中不难看到 fastclick 的兼容性做的很好。它的主要目的是解决 click 在触摸屏下的使用,引入之后再初始化一次就好了,很适合复用代码的情景。
扩展讲一下 touchEvent
本文中 zepto 和 fastclick 都有用到 touchEvent,但是 zepto 当中用的是 e.touches 而 fastclick 却用的是 e.targetTouches。这两者的差异我们来一点一点地扒。
TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等。
属性:
1.TouchEvent.changedTouches
一个 TouchList 对象,包含了代表所有从上一次触摸事件到此次事件过程中,状态发生了改变的触点的 Touch 对象。
2.TouchEvent.targetTouches
一个 TouchList 对象,是包含了如下触点的 Touch 对象:触摸起始于当前事件的目标 element 上,并且仍然没有离开触摸平面的触点.
3.TouchEvent.touches
一个 TouchList 对象,包含了所有当前接触触摸平面的触点的 Touch 对象,无论它们的起始于哪个 element 上,也无论它们状态是否发生了变化。
4.TouchEvent.type
此次触摸事件的类型,可能值为 touchstart,touchmove,touchend 等等
5.TouchEvent.target
触摸事件的目标 element ,这个目标元素对应 TouchEvent.changedTouches 中的触点的起始元素.
6.TouchEvent.altKey,TouchEvent.ctrlKey,TouchEvent.metaKey,TouchEvent.shiftKey
触摸事件触发时,键盘对应的键(例如 alt )是否被按下。
TouchList 与 Touch
TouchList 就是一系列的 Touch,通过 TouchList.length 可以知道当前有几个触点, TouchList[0] 或者 TouchList.item(0) 用来访问第一个触点。
属性
1.Touch.identifier: touch 的唯一标志,整个 touch 过程中(也就是 end 之前)不会改变。
2.Touch.screenX 和 Touch.screenY: 坐标原点为屏幕左上角
3.Touch.clientX 和 Touch.clientY: 坐标原点在当前可视区域左上角,这两个值不包含滚动偏移。
4.Touch.pageX 和 Touch.pageY: 坐标原点在HTML文档左上角,这两个值包含了水平滚动的偏移
5.Touch.radiusX 和 Touch.radiusY: 触摸平面的最小椭圆的水平轴(X轴)半径和垂直轴(Y轴)半径
6.Touch.rotationAngle:触摸平面的最小椭圆与水平轴顺时针夹角。
7.Touch.force: 压力值 0.0-1.0
8.Touch.target: Touch相关事件触发时的 element 不会随 move 变化。如果 move 当中该元素被删掉,这个 target 依然会不变,但不会冒泡。最佳实践是将触摸事件的监听器绑定到这个元素本身, 防止元素被移除后, 无法再从它的上一级元素上侦测到从该元素冒泡的事件。
希望本文能解答一些大家在移动端开发当中的一些问题,本文行文匆忙如有不正确的地方希望能回复告知。
关键字:JavaScript, touch, fastclick, false
版权声明
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!