移动端手势库AlloyFinger源码分析

简介

AlloyFinger 是由腾讯前端团队 AlloyTeam 出品的一个小巧轻量级的移动端手势库,整个手势库的代码不超过400行,却支持绝大多数的手势操做,可以知足平常的开发需求。AlloyFinger传送门: AlloyFingerjavascript

JavaScript 移动端触摸事件

手机移动端浏览器提供了4种触摸事件:touchstart,touchmove,touchend,touchcancel,分别对应的是手指触点刚接触屏幕时触发事件,手指触点在屏幕上移动时触发事件,手指触点移开屏幕时触发事件以及被系统中断时触发事件(按 Home 键返回主屏等操做)。前端

这里要说明下,移动端浏览器也支持部分 PC 端带有的事件,好比 click 事件。可是在移动端上,click 事件会存在延时触发的状况,大概延时300ms。java

移动端300ms延时触发 click 事件

在移动端为何click事件会存在延时触发的状况呢?究其缘由,是由于苹果公司在早期发布iphone的时候,采用了双击缩放网页的设计。当用户手指点击一次屏幕时,浏览器不能当即断定用户操做是单击操做仍是双击操做,而是延迟了300ms,以判断用户是否再次点击了屏幕,若是300ms以内没有再次点击屏幕就断定为单击事件,才会去触发click事件。git

源码分析

AlloyTeam 团队为 AlloyFinger 打造了多个可以适用不一样技术栈中的手势库版本,可以方便的使用在 React 框架,Vue框架以及原生JS中。不一样场景下的手势库版本的实现思路都是同样的,因此这里只分析了原生JS的实现思路。github

如何使用

AlloyFinger 的使用方式很是简单,源码中暴露出了一个全局的 AlloyFinger 构造函数对象,使用方式以下,返回值是一个 AlloyFinger 实例对象。web

// element 是须要手势操做的DOM元素,值能够是DOM对象也能够是元素选择器。
// options 是一个对象,包含了须要的手势操做函数。
var af = new AlloyFinger(element, options);

var af = new AlloyFinger(element, {
    tap: function() {
        //do something...
    }
});
复制代码

有了 AlloyFinger 实例对象后,你还能够经过绑定自定义事件的方式使用手势库浏览器

//绑定手势事件
af.on('tap', function() {
    //do something...
});
//解绑手势事件
af.off('tap', function() {
    //do something...
});
//销毁实例
af.destroy();
复制代码

总体架构

源码架构

AlloyFinger 构造函数bash

首先,先定义了一个 AlloyFinger 构造函数,里面作了不少操做,事件的监听回调,变量值的初始化,将手势操做做为订阅者添加到订阅列表中。在这部分源码中,会初始化不少关于手指触点的水平坐标和垂直坐标的存储变量,刚开始看的时候会以为代码比较的混乱,因此笔者把这部分的变量捋一遍梳理了出来,便于清晰的阅读源码。架构

this.x1: 存储在刚开始触摸时第一个手指触点的X坐标位置
this.y1: 存储在刚开始触摸时第一个手指触点的Y坐标位置
this.preV.x: 存储第一个手指触点与第二个手指触点之间的水平间距
this.preV.y: 存储第一个手指触点与第二个手指触点之间的垂直间距
this.x2: 存储在移动操做时第一个手指触点的X坐标位置
this.y2: 存储在移动操做时第一个手指触点的Y坐标位置
this.sx2: 存储在移动操做时第二个手指触点的X坐标位置
this.sy2: 存储在移动操做时第二个手指触点的Y坐标位置
复制代码

整个的源码解读都放置在个人github上,几乎每一行都有本身的注解,感兴趣的话能够点击这里:传送门框架

源码都是精简干练的,多看优秀的源码仍是对本身的技术有帮助的,可能看完了以后会思考本身怎么去DIY一个手势库呢?想要本身怎么去DIY一个手势库,必须得先了解各个手势操做的实现思路,有思路了以后才能动手写代码。

具体实现

  • tap点击

tap的本质其实就是touchend,可是在具体实现的时候必须作下限制,当前只存在一个手指触点,且touchstart的时候手指触点和touchend时手指触点的X轴Y轴的误差不能小于30,这样才能断定当前的操做是tap操做。

var len = evt.touches.length;
if(len < 1) {
    if ((this.x2 && Math.abs(this.x1 - this.x2) <= 30) ||
                (this.y2 && Math.abs(this.y1 - this.y2) <= 30)) {
                    //我是tap操做,do something...
    }
}
复制代码
  • doubleTap

doubleTap双击操做的实现思路大体是这样的,得先判断一段时间内是否有两次touchstart操做,而且两次touchstart都是快速完成的,否则会被认为是长按操做了,还有一点就是两次触点的位置的X轴Y轴的误差不能小于30。

//存储手指按下触摸操做的时间戳
this.now = null;
//存储上一次手指触点触摸的时间戳
this.last = null;
//用于存储手指触摸操做时的水平坐标和垂直坐标(若是是多指触摸操做,则记录的是第一个手指触摸的位置)
this.preTapPosition = { x: null, y: null };
//是否为双击操做
this.isDoubleTap = false;
...
function start() {
    this.now = Date.now();
    if (this.preTapPosition.x !== null) {
        //若是手指连续触摸操做之间的时间间隔小于250毫秒,且手指连续触摸操做之间的触点位置水平坐标小于30,垂直坐标小于30,那么就断定该操做为双击操做
        this.isDoubleTap = (this.delta > 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30);
    }
    this.preTapPosition.x = this.x1;
    this.preTapPosition.y = this.y1;
    this.last = this.now;
}
function end() {
    if (this.isDoubleTap) {
        //我是doubleTap操做,do something...
    }
}
复制代码
  • swipe

swipe滑过操做具体的实现思路是touchstart的手指触点的坐标和touchend时候手指触点的坐标x、y方向偏移要大于30,且还要判断是往哪一个方向滑动。

//断定swipe滑动的方向
_swipeDirection: function (x1, x2, y1, y2) {
    return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}
复制代码

更多的手势操做源码分析能够参考个人github上的源码分析,传送门。AlloyFinger 手势库还用在了一个小巧的移动端裁剪图片工具上,下次还能够分析一波裁剪工具 AlloyCrop 的源码,学习到裁剪图片的原理和实现方案,平时在开发过程当中,其实只要清楚了实现思路和原理,就可以方便的实现具体的功能。

相关文章
相关标签/搜索