超小手势库alloyfinger及其vue版实现深入解析

本文深入解析轻量级手势库alloyfinger及其vue实现,探讨长按与其它事件的关系、swipe与doubleTap的界定问题以及移动端适配策略。同时,指出官方vue版本存在的事件绑定bug,并提供了修复方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

alloyfinger是一款非常轻量的开源手势库,由于其轻量、基于原生js等特性被广泛使用。关于其原理,它的官方团队解析的非常详细——传送门。相信学过高数的人看起来应该不难,这里不深入解析了。

其核心代码只有300多行,完成了14个手势,其手势并不是浏览器原生的事件,而是通过监听touchstart、touchmove、touchend、touchcancel四个原生浏览器事件hack出来的手势,故其用法与原生可能有些不同。比如阻止默认事件、阻止冒泡,不能像原生事件那样用。

官方代码除了alloyfinger的核心库外还有react、vue的实现。在这里只对核心库即vue版本的解析。

核心库:

/* AlloyFinger v0.1.7
 * By dntzhang
 * Github: https://github.com/AlloyTeam/AlloyFinger
 * Note By keenjaan
 * Github: https://github.com/keenjaan
 */
; (function () {
    // 计算距离和角度等的数学公式

    // 根据两边的长度求直角三角形斜边长度(主要用于求两点距离)
    function getLen(v) {
        return Math.sqrt(v.x * v.x + v.y * v.y);
    }
    // 主要用于计算两次手势状态间的夹角的辅助函数
    function dot(v1, v2) {
        return v1.x * v2.x + v1.y * v2.y;
    }
    // 计算两次手势状态间的夹角
    function getAngle(v1, v2) {
        var mr = getLen(v1) * getLen(v2);
        if (mr === 0) return 0;
        var r = dot(v1, v2) / mr;
        if (r > 1) r = 1;
        return Math.acos(r);
    }
    // 计算夹角的旋转方向,(逆时针大于0,顺时针小于0)
    function cross(v1, v2) {
        return v1.x * v2.y - v2.x * v1.y;
    }
    // 将角度转换为弧度,并且绝对值
    function getRotateAngle(v1, v2) {
        var angle = getAngle(v1, v2);
        if (cross(v1, v2) > 0) {
            angle *= -1;
        }
        return angle * 180 / Math.PI;
    }
    // 用于处理手势监听函数的构造函数
    var HandlerAdmin = function(el) {
        this.handlers = []; // 监听函数列表
        this.el = el;       // 监听元素
    };
    // 构造函数的添加监听函数的方法
    HandlerAdmin.prototype.add = function(handler) {
        this.handlers.push(handler);
    }
    // 构造函数的删除监听函数的方法
    HandlerAdmin.prototype.del = function(handler) {
        if(!handler) this.handlers = []; // handler为假值时,代表清空监听函数列表
        for(var i=this.handlers.length; i>=0; i--) {
            if(this.handlers[i] === handler) {
                this.handlers.splice(i, 1);
            }
        }
    }
    // 触发用户事件监听回调函数
    HandlerAdmin.prototype.dispatch = function() {
        for(var i=0,len=this.handlers.length; i<len; i++) {
            var handler = this.handlers[i];
            if(typeof handler === 'function') handler.apply(this.el, arguments);
        }
    }
    // 实例化处理监听函数的对象
    function wrapFunc(el, handler) {
        var handlerAdmin = new HandlerAdmin(el);
        handlerAdmin.add(handler);  // 添加监听函数
        return handlerAdmin; // 返回实例
    }
    // 手势的构造函数
    var AlloyFinger = function (el, option) {
      
        this.element = typeof el == 'string' ? document.querySelector(el) : el; // 绑定事件的元素

        // 绑定原型上start, move, end, cancel函数的this对象为 AlloyFinger实例
        this.start = this.start.bind(this);
        this.move = this.move.bind(this);
        this.end = this.end.bind(this);
        this.cancel = this.cancel.bind(this);

        // 绑定原生的 touchstart, touchmove, touchend, touchcancel事件。
        this.element.addEventListener("touchstart", this.start, false);
        this.element.addEventListener("touchmove", this.move, false);
        this.element.addEventListener("touchend", this.end, false);
        this.element.addEventListener("touchcancel", this.cancel, false);
      
		// 保存当有两个手指以上时,两个手指间横纵坐标的差值,用于计算两点距离
        this.preV = { x: null, y: null };   
        this.pinchStartLen = null;  // 两个手指间的距离
        this.zoom = 1;              // 初始缩放比例
        this.isDoubleTap = false;   // 是否双击

        var noop = function () { }; // 空函数,没有绑定事件时,传入的函数

        // 对14种手势,分别实例化监听函数对象,根据option的值添加相关监听函数,没有就添加空函数。
        this.rotate = wrapFunc(this.element, option.rotate || noop);
        this.touchStart = wrapFunc(this.element, option.touchStart || noop);
        this.multipointStart = wrapFunc(this.element, option.multipointStart || noop);
        this.multipointEnd = wrapFunc(this.element, option.multipointEnd || noop);
        this.pinch = wrapFunc(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值