隔离 鼠标点击 双击 移动事件

隔离 鼠标点击 双击 移动事件

应用场景

在编写一个3d卡片切换的时候,需要执行
1. **单击** 触发模态框 
2. **双击** 卡片定位
3. **拖动** 卡片跟随

问题

默认dom事件 
鼠标**双击**的时候会触发**单击**
鼠标按下**拖动**的开始会触发**单击**

因此需要分离这三种事件 双击无法触发单击 拖动时无法触发单击

解决方案

思路

2.实现点击事件
 分析:需要的点击事件   单击  双击  拖拽(移动)
        通过mousedown , mouseup , mousemove ,mouseleave控制
   2.0 当鼠标事件
          按下时 mousedown
            注册mousemove , mouseup 和 mouseleave 
          抬起时 mouseup
            注销mousemove , mouseup 和 mouseleave, 判断事件类型<单击,双击,已触发的移动> 并且 清空所有按键信息
          离开时 mouseleavel
            注销mousemove , mouseup 和 mouseleave  并且 清空所有按键信息
   2.1 单击 mouseup事件下
           当鼠标不是在移动(mouseCtrl.ismoving为false)且
           当鼠标按下的和抬起的间隔小于一个值默认150毫秒 mouseDiffTime < 150 即为一次有效点击 
           开启一个异步延迟的单击事件  延迟事件 默认为300毫秒 将事件id缓存下来
   2.2 双击 mouseup事件下
           触发同样的单击事件判断
           如果存在事件id则代表上一次单击还未执行, (那即这是150毫秒+300毫秒 之间的第二此点击  即为双击)
           取消事件id的执行(取消单击事件) 
           触发双击事件
   2.3 移动 mousemove 事件下
           判断 鼠标按下的初始位置和当前位置的x距离如果(mouse.moveX) 大于 20 
           触发移动事件

实现代码

第一部分为事件系统提供一个 注册 销毁 触发 的函数

/**一个事件注册函数 
* @constructor
*/
function RegisterEvents() {
    /** 从数组中移除某个元素
     * @param {[]} arr 
     * @param {*} target 
     */
    var removeItemFromArr = function (arr , target) {
        for (var i = 0; i < arr.length; i++) {
            var element = arr[i];
            if(element === target) return arr.splice(i,1)[0];
        }
        return null;
    };
    
    var eventsStore = {
        "click":[],
        "dblclick":[],
        "movingEvent":[]
    };

    /** 注册事件函数
     * @param {'click'| 'dblclick' | 'movingEvent'} type
     * @param {Function} fnc params <{}>
     */
    this.on = function (type,fnc) {
        if(eventsStore[type] === undefined)  throw new Error("绑定的事件类型不存在");
        if(typeof fnc !== 'function') throw new Error("绑定的事件不是可执行函数");
        eventsStore[type].push(fnc);
    };

    /** 移除事件函数
     * @param {'click'| 'dblclick' | 'movingEvent'} type
     * @param {Function} fnc 
     */
    this.off = function (type,fnc) {
        if(type && eventsStore[type] === undefined)  throw new Error("绑定的事件类型不存在");
        if(type === undefined){
            eventsStore = [];
        }else if(typeof fnc !== 'function'){
            eventsStore[type].length = 0;
        }else{
            removeItemFromArr(eventsStore[type],fnc);
        }
    };
    
    /** 移除事件函数
     * @param {'click'| 'dblclick' | 'movingEvent'} type
     * @param {{event:EventTarget}} info ?
     */
    this.emit = function (type,info) {
        if(eventsStore[type] === undefined)  throw new Error("绑定的事件类型不存在");
        eventsStore[type].forEach(e=>e(info));
    };
}

第二部分 主体实现分离

/**提供事件系统 增加 单击 双击 移动 事件
 * @constructor
 * @param {JQuery<HTMLElement> } dom 
 * @extends RegisterEvents
 */
function EventSystem(dom) {

    /**保存当前实例 */
    var instance = this;

    if(!dom.jquery) dom = $(dom);
    var mouseCtrl = generateMouseCtrl();
    
    /**鼠标按下时 注册鼠标移动函数 */
    dom.on('mousedown',  function (e) {
        mouseCtrl.isdown = true;
        mouseCtrl.event = e;
        registerDownAfterListen();

        mouseCtrl.mouseDownTime = new Date();
        mouseCtrl.moveStartX = e.screenX;
        console.log('mousedown');
    });

    /**在点击之后需要开启的所有监听 */
    function registerDownAfterListen() {
        dom.on('mousemove',mousemove);
        dom.on('mouseup',mouseup);
        dom.on('mouseleave',mouseleave);

    }

    /**注销所有因为Donw而注册的监听 */
    function cancelDownAfterListen() {
        dom.off('mousemove',mousemove);
        dom.off('mouseup',mouseup);
        dom.off('mouseleave',mouseleave);
      
    }

    function mousemove(e) {
        /**因为只有在mousedown的时候才会注册mousemove事件 所以无须 检测是否按下 */
        // if(mouseCtrl.isdown===false) return ;
        /**当前位置和上一次位置的差值 */
        var constantlyOffset = e.screenX - (mouseCtrl.moveEndX || mouseCtrl.moveStartX);

        mouseCtrl.moveEndX = e.screenX; 
        if(Math.abs(mouseCtrl.moveX)>20 && !mouseCtrl.ismoving){
            mouseCtrl.ismoving = true;
        }
        if(mouseCtrl.ismoving){
            instance.emit('movingEvent',{
                event:mouseCtrl.event,
                moveX:mouseCtrl.moveX,
                constantlyOffset:constantlyOffset
            });
        }
        console.log('mousemove');
    }
    /**@TODO:
     * 1.将点击事件也集成到mouseup上 
     *      1.1 当mouseCtrl.ismoving为false且
     *      1.2 当mouseDiffTime < 150 即为一次有效点击   
     *      1.3 如果不存在 clickId 开启一个延时点击函数 clickId  = excuteClick():async;
     *      1.4 如果存在 clickId 取消 clickId:async  执行 excuteDbclick();
     *  */
    function mouseup() {
        mouseCtrl.mouseUpTime = new Date();

        if(!mouseCtrl.ismoving && mouseCtrl.mouseDiffTime < mouseCtrl.mouseDiffTimeMax){
            mouseCtrl.clickId ?   dblclick():singleClick();
        }else{
            /**这里如果触发了单击或者双击  因为 单击时有延时的 所以不应该直接释放mouseCtrl */
            mouseCtrl.isreleased = true;
        }
        cancelDownAfterListen();
        console.log('mouseup');
    }

    function singleClick() {
        mouseCtrl.clickId = setTimeout(function () {
            instance.emit('click',{event:mouseCtrl.event});
            mouseCtrl.isreleased = true;
            console.log('singleClick');
        }, mouseCtrl.mouseDoubleClickTimeMax);
    }

    function dblclick() {
        if(mouseCtrl.clickId) clearTimeout(mouseCtrl.clickId);
        instance.emit('dblclick',{event:mouseCtrl.event});
        mouseCtrl.isreleased = true;
        console.log('dblclick');
    }

    function mouseleave() {
        cancelDownAfterListen();
        mouseCtrl.isreleased = true;
    }

     this.destroy = function () {
        this.off();
        try {
            cancelDownAfterListen();
        } catch(e){console.log(e);}
        dom.off('mousedown');
        dom = null;
        mouseCtrl = null;
    };
    /**生成鼠标属性管理
     * @returns {{isdown:false,ismoving:false,isreleased:true,moveX:null,clickId:null,moveStartX,moveEndX,mouseDownTime,mouseUpTime,mouseDiffTime,mouseDiffTimeMax:150,mouseDoubleClickTimeMax:300}}   当鼠标释放的时候会重置所有 isdown:false ismoving:false moveX:null clickId:null 
     * @summary isdown 基本无用 
     */
    function generateMouseCtrl() {
        /**是否按下,是否移动,是否释放,鼠标移动的横向距离起始点,鼠标横向移动的重点 鼠标按下的初始时间  鼠标抬起的时间 */
        var isdown , ismoving , isreleased,moveStartX,moveEndX ,mouseDownTime , mouseUpTime  ; 
        var mouseCtrl = {
            /**触发事件的id 单击 */
            clickId:null,
            /**鼠标点击触发的window.event */
            event:null,
            /**鼠标横向移动的距离 */
            moveX:null,
            /**鼠标点击抬起的间隔时间 */
            mouseDiffTime:null,
            /**固定值 鼠标按下抬起是否时单击的时间最大值 */
            mouseDiffTimeMax:150,
            /**固定值 两次鼠标抬起的最大时间间隔, 在其之内 则为双击 在其之位则为两次单击 */
            mouseDoubleClickTimeMax:300
        };
        function init() {
            isdown = false;
            ismoving = false;
            mouseCtrl.clickId = null;
            mouseCtrl.moveX = null;
            moveStartX = null;
            moveEndX = null;
            mouseDownTime = null;
            mouseUpTime = null;
            mouseCtrl.mouseDiffTime = null;
            mouseCtrl.event = null;
        }
        Object.defineProperty(mouseCtrl,'isdown',{
            get:function () {
                return isdown;
            },
            /** @param {Boolean} bol */
            set:function (bol) {
                isdown = bol;
                isreleased = !bol;
            }
        });
        Object.defineProperty(mouseCtrl,'isreleased',{
            get:function () {
                return isreleased;
            },
            /** @param {Boolean} bol */
            set:function (bol) {
                // if(bol === isreleased) return ;
                isreleased = bol;
                if(bol) {
                    init();
                }
            }
        });
        Object.defineProperty(mouseCtrl,'ismoving',{
            get:function () {
                return ismoving;
            },
            /** @param {Boolean} bol */
            set:function (bol) {
                if(isdown) {
                    ismoving = bol;
                }else {
                    ismoving = false;
                }
            }
        });
        Object.defineProperty(mouseCtrl,'moveStartX',{
            get:function () {
                return moveStartX;
            },
            /** @param {number} startX */
            set:function (startX) {
                moveStartX = startX;
                moveEndX = 0;
            }
        });
        Object.defineProperty(mouseCtrl,'moveEndX',{
            get:function () {
                return moveEndX;
            },
            /** @param {number} endX */
            set:function (endX) {
                if(typeof moveStartX !=="number") throw new Error("开始位置moveStartX必须为数字类型");
                moveEndX = endX;
                mouseCtrl.moveX = moveEndX - moveStartX;
            }
        });
        Object.defineProperty(mouseCtrl,'mouseDownTime',{
            get:function () {
                return mouseDownTime;
            },
            /** @param {number} mouseDownTime */
            set:function (downTime) {
                if((typeof downTime ==="number") || !( downTime instanceof Date)) throw new Error("downTime必须为数字类型或者时间类型");
                mouseDownTime = downTime;
            }
        });
        Object.defineProperty(mouseCtrl,'mouseUpTime',{
            get:function () {
                return mouseUpTime;
            },
            /** @param {number} endX */
            set:function (upTime) {
                if((typeof upTime ==="number") || !( upTime instanceof Date)) throw new Error("upTime必须为数字类型或者时间类型");
                mouseUpTime = upTime;
                mouseCtrl.mouseDiffTime = mouseUpTime - mouseDownTime;
            }
        });
        mouseCtrl.isreleased = true;
        return mouseCtrl;
    }
}

EventSystem.prototype = new RegisterEvents();

EventSystem 提供
1. EventSystem.on(type,callback)注册函数 ‘click’| ‘dblclick’ | ‘movingEvent’ 可选值
2. EventSystem.off(type,callback)注销函数
3. EventSystem.emit (type,value) 触发函数
4. EventSystem.destroy() 销毁函数

总结
啊啊啊啊,可以去掉jquery。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值