fastclick是什么,它解决了什么问题呢?fastclick解决了移动端点击的约300ms延迟问题。当触发click事件时,无法判断用户是想进行双击还是单击,所以有个约300ms的判断是否会进行第二次点击操作。

基本结构

function FastClick(layer, options) {}
// ...... FastClick.attach = function(layer, options) { return new FastClick(layer, options); }; // 这个方法用于判断是否需要fastclick,因为如果用户设置了如禁止播放的功能, // 那么默认将不需要fastClick, 这个方法代码长,这里不贴,里面是关于不同版本 // 浏览器判断和对user-scalable的获取判断 FastClick.notNeeded = function(layer) { // ... }

可以看到基本结构很简单,定义一个构造函数,并且挂载一个静态函数attach(js没有面向对象语法,但是如果在构造函数挂载函数,实际上等同于面向对象的Class.staticMethod,即不需要实例即可访问的函数,同理,原型上的方法是需要创建实例才能使用)。
attach方法就是创建一个FastClick实例,所以基本上,fastClick的操作就在构造函数初始化内完成。接下来继续看里面的内容。

对象结构

attach方法已经拜读,可以先忽略了。

// 对象属性
function FastClick(layer, options) { this.trackingClick = false; // 当touchStart时候置为true this.trackingClickStart = 0; // 当touchStart时候的时间错 this.targetElement = null; // 触发的元素 this.touchStartX = 0; // touch位置x轴 this.touchStartY = 0; // touch位置y轴 this.lastTouchIdentifier = 0; // 注释:最后触摸的id,取自Touch.identifier. this.touchBoundary = options.touchBoundary || 10; // move边界 this.layer = layer; // 挂载的dom元素,该dom元素下fastclick将生效 this.tapDelay = options.tapDelay || 200; this.tapTimeout = options.tapTimeout || 700; // ... 执行过程 }

构造函数执行过程

function FastClick(layer, options) {// ...属性已省略 var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel']; // 列举需要挂载实例中回到到layer上的事件 var context = this; for (var i = 0, l = methods.length; i < l; i++) { context[methods[i]] = bind(context[methods[i]], context); // 绑定fastclick对象的上下文给回调函数, 这些回调是定义在构造函数的prototype上,后面会看到 } // Set up event handlers as required 按要求挂载处理事件 if (deviceIsAndroid) { layer.addEventListener('mouseover', this.onMouse, true); layer.addEventListener('mousedown', this.onMouse, true); layer.addEventListener('mouseup', this.onMouse, true); } layer.addEventListener('click', this.onClick, true); layer.addEventListener('touchstart', this.onTouchStart, false); layer.addEventListener('touchmove', this.onTouchMove, false); layer.addEventListener('touchend', this.onTouchEnd, false); layer.addEventListener('touchcancel', this.onTouchCancel, false); // ... }

构造函数核心过程如图,它会给layer挂载需要处理的回调事件,layer作为上层节点,能够获取到target节点所发生的事件,至于在各种事件中如何处理这个问题,我们会在后续的代码中看到。

bind方法的实现如下, 简单看下,没啥说的,比较简单:

// Some old versions of Android don't have Function.prototype.bind
function bind(method, context) { return function() { return method.apply(context, arguments); }; }

设计思路

关于设计思路,就是说如果是我们模拟这样一个事件,我们需要怎么去判断为点击事件,我想大概可以从几个点去考虑。

  1. 一个点击事件,它必须包含一个点,一个起的动作(start, end)。
  2. 一个点击事件,它必须是迅速的,也就是说,如果你一直按着不动,那么这不是一个点击事件,而是一个长按事件,所以我们还需要一个时间区间,对于在这个时间区间范围内我们才认为是点击事件。所以我们看fastclick的源码属性中,tapDelay就是这样的作用,默认是200ms,可以人为配置。
  3. 一个点击事件,它必须是短距的,也就是说,如果你移动了一个比较长的距离,那么我们认定为是一个移动事件,这也不算是点击事件,可以在touchMove里面去判断。

Fastclick的原型方法

原型方法即实例方法

FastClick.prototype.focus = function(targetEle) { // ... }; FastClick.prototype.updateScrollParent = function(targetEle) { // .. }; FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) { // ... }; FastClick.prototype.touchHasMoved = function(event) { // ... }; FastClick.prototype.findControl = function(event) { // ... }; FastClick.prototype.onTouch = function(event) { // ... }; FastClick.prototype.destroy = function(event) { // ... }; FastClick.prototype.onTouchStart = function(event) { // ... }; FastClick.prototype.onTouchMove = function(event) { // ... }; FastClick.prototype.onMouse = function(event) { // ... }; FastClick.prototype.onClick = function(event) { // ... }; FastClick.prototype.onTouchCancel = function(event) { // ... };

首先,按照我们预想的思路, 应该想看touchStart方法.

FastClick.prototype.onTouchStart = function(event) { var targetElement, touch, selection; // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111). if (event.targetTouches.length > 1) { return true; } targetElement = this.getTargetElementFromEventTarget(event.target); touch = event.targetTouches[0]; if (deviceIsIOS) { // ... 此省略部分issue兼容代码 } this.trackingClick = true; this.trackingClickStart = event.timeStamp; this.targetElement = targetElement; this.touchStartX = touch.pageX; this.touchStartY = touch.pageY; // Prevent phantom clicks on fast double-tap (issue #36) if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { event.preventDefault(); } return true; }

touchStart方法主要存储几个状态属性, trackingClick用于其他事件里面追逐,是否触发click以这个属性为准,trackingClickStart 用于存储点下去时候的时间戳, touchStartX, touchStartY分别为点击事件的X, Y轴。至于其他工作,比如忽略多指操作(即不追踪)。

FastClick.prototype.onTouchHove = function(event) { // trackingClick是是否追踪,不追踪直接返回 if (!this.trackingClick) { return true; } // If the touch has moved, cancel the click tracking if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) { this.trackingClick = false; this.targetElement = null; } return true; }

这是touchMove的处理事件,主要就是两个判断,一个是如果move事件触发元素和touchStart的元素不是一个元素,即已经有移动,则不触发,touchHasMoved方法必然是用于算移动距离是否已经超过限制,超过则不追踪click。
touchHasMoved实现如下:

FastClick.prototype.touchHasMoved = function(event) { var touch = event.changedTouches[0], boundary = this.touchBoundary; if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) { return true; } return false; };

这里有个值得了解的e.changedTouches属性, 属于touch event属性,学习了。

接下来就是最后一步,touchEnd,按我们预计,最后应该是模拟click事件。我看了下,整个end事件处理很多东西,但是最终其实它只做一件事情,关于其兼容判断,我暂时删除。删除完的代码如下:

FastClick.prototype.onTouchEnd = function (event) { if (!this.trackingClick) { return true; } // 模拟click事件 this.sendClick(targetElement, event); }

到这里流程其实已经结束了,最后我们还可以看下如何去模拟click实现,关于sendClick,fastClick是这样实现的:

FastClick.prototype.sendClick = function(targetElement, event) { var clickEvent, touch; // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24) if (document.activeElement && document.activeElement !== targetElement) { document.activeElement.blur(); } touch = event.changedTouches[0]; // Synthesise a click event, with an extra attribute so it can be tracked 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; targetElement.dispatchEvent(clickEvent); }; FastClick.prototype.determineEventType = function(targetElement) { //Issue #159: Android Chrome Select Box does not open with a synthetic click event if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') { return 'mousedown'; } return 'click'; }; 

实际上,就是通过DOM接口的createEvent创建一个模拟事件,叫做click覆盖默认的click,这个事件依然会冒泡,依然可以委托。这就是整个fastclick的思路,到这里,就结束了。但是fastclick还有很多兼容性工作,感兴趣的可以移步到fastclick仓库拜读。

转载于:https://www.cnblogs.com/wuweixin/p/11393082.html

fastclick源码简析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. django源码简析——后台程序入口

    django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...

  3. (Ajax)axios源码简析(三)——请求与取消请求

    传送门: axios源码简析(一)--axios入口文件 axios源码简析(二)--Axios类与拦截器 axios源码简析(三)--请求与取消请求 请求过程 在Axios.prototype.re ...

  4. java ArrayList 概述 与源码简析

    ArrayList 概述 与源码简析 1 ArrayList 创建 ArrayList<String> list = new ArrayList<>(); //构造一个初始容量 ...

  5. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  6. ffmpeg实战教程(十三)iJKPlayer源码简析

    要使用封装优化ijk就必须先了解ffmpeg,然后看ijk对ffmpeg的C层封装! 这是我看ijk源码时候的笔记,比较散乱.不喜勿喷~ ijk源码简析: 1.ijkplayer_jni.c 封装的播 ...

  7. 【Android项目】本地FM收音机开发及源码简析

    [Android项目]本地FM收音机开发及源码简析 目录 1.概述 2.收音机的基本原理 3.收音机其他信息 RDS功能 4.Android开发FM收音机源码解析 5.App层如何设计本地FM应用 6 ...

  8. Log-Pilot 源码简析

    Log-Pilot 源码简析 简单介绍 源码简析 Pilot结构体 Piloter接口 main函数 Pilot.Run Pilot.New Pilot.watch Pilot.processEven ...

  9. Spring Boot源码简析 @Qualifier

    源码 @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementT ...

最新文章

  1. R-error: 错误: nul character not allowed (line 1)
  2. Android移动开发之【Android实战项目】记一次app开发过程!
  3. postman中POST请求时参数包含参数list设置
  4. HTTP 协议是无状态协议,怎么理解
  5. Computer Systems A Programmer's Perspective(深入理解计算机系统)第一章读书笔记
  6. libgdx for eclipse开发环境搭建
  7. NoSql数据库Redis的在ubuntu下的部署使用
  8. BAT文件里注释符号
  9. linux 常用查看日志命令--more 命令
  10. 4.1-4.30推荐文章汇总
  11. 计蒜客习题:猴子打字
  12. 单元测试(01) 调试过程中遇到 Method isEmpty in android.text.TextUtils not mocked
  13. 无线路由器(WIFI)经常掉线断网的可能的原因
  14. 打印机服务无法启动(如何解决打印机后台服务没有启动)
  15. 获取固定到任务栏的快捷方式的图标
  16. 打算的亲爱额请问请问额
  17. c++中将字符串转换为无符号整数函数:std::stoul and std::stoull
  18. 初学电力系统中正序、负序、零序及对称分量法的推导
  19. JavaScript 实现简易 ATM 取款机操作
  20. Java 分数的实现 | 将float或double转换为分数

热门文章

  1. html中%3c%3e括号,打开关闭大括号检查
  2. create 执行存储过程报错出现符号_记一次数据库迁移的过程采坑过程
  3. html5中如何使图片滚动条,如何使RMarkdown代码块中出现垂直滚动条(html视图)
  4. mysql5.1 主主同步_mysql主主同步指定库的指定表(version 5.1~5.7)
  5. Java 线程详解(一)线程的基础
  6. 使用寄存器点亮LED——编程实战
  7. android 成长日记 3.关于Activity的用户体验提升办法和使用技巧说明
  8. ​求1000以内是3或者是5的倍数的值的和
  9. puppet成长日记二 Package资源详细介绍及案例分析
  10. HDUOJ--汉诺塔II