移动端手势库AlloyFinger源码分析

简介

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

JavaScript 移动端触摸事件

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

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

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

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

源码分析

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

如何使用

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

// 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 构造函数

首先,先定义了一个 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 的源码,学习到裁剪图片的原理和实现方案,平时在开发过程中,其实只要清楚了实现思路和原理,就能够方便的实现具体的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值