鼠标/触屏 兼容的手势方案

(function(){
    Module("Touchable",function(require,exports){
        var hasTouch = 'ontouchstart' in document;
        var EvtType = {ON:hasTouch?"touchstart":"mousedown",OFF:hasTouch?"touchend":"mouseup",MOVE:hasTouch?"touchmove":"mousemove"}
        var CenterPoint;
        var THRESHOLD_MOVE = 5,
            THRESHOLD_TAP = 200,
            THRESHOLD_DOUBLETAP = 350,
            THRESHOLD_HOLD = 600,
            THRESHOLD_ANGLE = 10,
            THRESHOLD_ROTATE = 5,
            THRESHOLD_SCALE = 10,
            THRESHOLD_SWIPE = 10;

        function vt(x,y){
            this.x=x;
            this.y=y;
        }
        vt.prototype.add=function(v){return new vt(this.x + v.x, this.y + v.y)}
        vt.prototype.minus=function(v){return new vt(this.x - v.x, this.y - v.y)}
        vt.prototype.dot=function(v){return this.x * v.x + this.y * v.y}
        vt.prototype.cross=function(v){return this.x * v.y - v.x * this.y}
        vt.prototype.mod=function(){return Math.sqrt(this.x * this.x + this.y * this.y)}
        vt.prototype.angle=function(v){
            var angle,r,mr;
            mr = this.mod() * v.mod();
            if(mr !== 0){
                r = this.dot(v) / mr;
                if(r > 1) r = 1;
                angle=Math.acos(r);
                if(this.cross(v) > 0){
                    angle *= -1;
                }
            }else{
                angle=0;
            }
            return Number((angle * 180 / Math.PI).toFixed(2));
        }

        function toFloat(num,len){return Number((num||0).toFixed(len||2))}
        function getPoints(e){
            var downList,upList;
            switch(e.type){
                case EvtType.ON:
                case EvtType.MOVE:
                    if(!hasTouch){
                        downList = [new vt(e.clientX,e.clientY)];
                    } else {
                        downList = [].map.call(e.touches,function(e){return new vt(e.clientX,e.clientY)});
                    }
                    upList = [];
                    break;
                case EvtType.OFF:
                    if(!hasTouch){
                        downList = [];
                        upList = [new vt(e.clientX,e.clientY)];
                    } else {
                        downList = [].map.call(e.touches,function(e){return new vt(e.clientX,e.clientY)});
                        upList = [].map.call(e.changedTouches,function(e){return new vt(e.clientX,e.clientY)});
                    }
                    break;
            }
            if(e.ctrlKey) downList=[new vt(CenterPoint.clientX,CenterPoint.clientY)].concat(downList);
            return {
                0:downList,
                1:upList
            }
        }

        function Touchable(el,isPD){
            var STATE={},p,pts,now,timestamp,last;
            CenterPoint = {clientX:(document.body.clientWidth)*0.5, clientY:(document.body.clientHeight)*0.5};

            function handler(e){
                isPD&&e.preventDefault();
                switch(e.type){
                    case EvtType.ON:
                        pts=getPoints(e);
                        p={
                            "0":{},
                            "1":{},
                            "2":{}
                        };
                        timestamp=(+new Date);
                        STATE.count=pts[0].length;
                        STATE.pressDown=1;
                        switch(pts[0].length){
                            case 1:
                                p[0][0]=p[1][0]=p[2][0]=pts[0][0];
                                trigger(e.target, 'pressdown', {});
                                setTimeout(function(){
                                    if(STATE.count===1 && !STATE.moved){
                                        STATE.hold = 1;
                                        trigger(e.target, 'hold', {"point":new vt(pts[0][0].x, pts[0][0].y)});
                                    }
                                }, THRESHOLD_HOLD);
                                break;
                            case 2:
                                p[0][0]=p[1][0]=p[2][0]=pts[0][0];
                                p[0][1]=p[1][1]=p[2][1]=pts[0][1];
                                break;
                        }
                        break;
                    case EvtType.MOVE:
                        if(STATE.pressDown){
                            now=(+new Date);
                            pts=getPoints(e);
                            switch(pts[0].length){
                                case 1:
                                    var ds = pts[0][0].minus(p[0][0]).mod();
                                    if(!STATE.hold) trigger(e.target, 'swiping', {"from":new vt(p[0][0].x, p[0][0].y), "point":new vt(pts[0][0].x, pts[0][0].y)});
                                    if(!STATE.moved && ds > THRESHOLD_MOVE){STATE.moved=1}
                                    if(STATE.moved){
                                        if(!STATE.hold && !STATE.swipe && ds > THRESHOLD_SWIPE){STATE.swipe=1}
                                        if(STATE.hold) trigger(e.target, 'drag', {"from":new vt(p[0][0].x, p[0][0].y), "point":new vt(pts[0][0].x, pts[0][0].y)});
                                    }
                                    p[1][0]=p[2][0];
                                    p[2][0]=pts[0][0];
                                    break;
                                case 2:
                                    var d1 = p[0][1].minus(p[0][0]).mod();
                                    var d2 = pts[0][1].minus(pts[0][0]).mod();
                                    var diff = d2 - d1;
                                    var ag = pts[0][1].minus(pts[0][0]).angle(p[0][1].minus(p[0][0]));
                                    if(!STATE.scale && Math.abs(diff) > THRESHOLD_SCALE){
                                        STATE.move = 1;
                                        STATE.scale = 1;
                                    }
                                    if(STATE.scale){
                                        trigger(e.target, 'scaling', {"scale":toFloat(d2/d1)});
                                    }
                                    if(!STATE.rotate && d1 > 80 && (Math.abs(ag)>THRESHOLD_ROTATE)){
                                        STATE.move = 1;
                                        STATE.rotate = 1;
                                    }
                                    if(STATE.rotate){
                                        trigger(e.target, 'rotating', {"rotate":toFloat(ag)});
                                    }
                                    p[1][0]=p[2][0];
                                    p[2][0]=pts[0][0];
                                    p[1][1]=p[2][1];
                                    p[2][1]=pts[0][1];
                                    break;
                            }
                            last=timestamp;
                            timestamp=now;
                        }
                        break;
                    case EvtType.OFF:
                        if(STATE.pressDown){
                            STATE.pressDown = 0;
                            pts=getPoints(e);
                            var dt=(+new Date)-last;
                            switch(pts[0].length){
                                case 1:
                                    var d0 = p[0][1].minus(p[0][0]).mod();
                                    var d1 = p[1][1].minus(p[1][0]).mod();
                                    var d2 = pts[1][1].minus(pts[1][0]).mod();
                                    var ds = d2 - d1;
                                    var da = pts[1][1].minus(pts[1][0]).angle(p[1][1].minus(p[1][0]));
                                    var ag = pts[1][1].minus(pts[1][0]).angle(p[0][1].minus(p[0][0]));
                                    if(STATE.scale){
                                        STATE.scale=0;
                                        p.scale=toFloat(d2/d0);
                                        trigger(e.target, 'scale', {scale:toFloat(d2/d0),speed:toFloat(ds/dt*1000)});
                                        if(p.scale > 1){ 
                                            trigger(e.target, 'scaleup', {});
                                        }
                                        if(p.scale < 1){ 
                                            trigger(e.target, 'scaledown', {});
                                        }
                                    }
                                    if(STATE.rotate){
                                        STATE.rotate=0;
                                        p.rotate=toFloat(ag);
                                        trigger(e.target, 'rotate', {rotate:toFloat(ag),speed:toFloat(da/dt*1000)});
                                        if(p.rotate < -45 || p.rotate > 315){ 
                                            trigger(e.target, 'rotateright', {});
                                        }
                                        if(p.rotate > 45 && p.rotate < 315){ 
                                            trigger(e.target, 'rotateleft', {});
                                        }
                                    }
                                    break;
                                case 0:
                                    if(!STATE.moved){
                                        if(STATE.hold){
                                            STATE.hold = 0;
                                            trigger(e.target, 'drop', {"point":new vt(pts[1][0].x, pts[1][0].y)});
                                        }else{
                                            if(STATE.doubleReady){
                                                STATE.doubleReady = 0;
                                                trigger(e.target, 'doubletap', {"point":new vt(pts[1][0].x, pts[1][0].y)});
                                            }else if(STATE.count===1){
                                                STATE.doubleReady = 1;
                                                setTimeout(function(){if(STATE.doubleReady) trigger(e.target, 'tap', {"point":new vt(pts[1][0].x, pts[1][0].y)})}, THRESHOLD_TAP)
                                                setTimeout(function(){STATE.doubleReady=0}, THRESHOLD_DOUBLETAP);
                                            }
                                        }
                                    }else{
                                        var dx = pts[1][0].x - p[1][0].x;
                                        var dy = pts[1][0].y - p[1][0].y;
                                        if(STATE.hold){
                                            STATE.hold = 0;
                                            trigger(e.target, 'drop', {"point":new vt(pts[1][0].x, pts[1][0].y)});
                                        }else{
                                            var angle = pts[1][0].minus(p[0][0]).angle(new vt(1,0));
                                            trigger(e.target, 'swipe', {"angle":toFloat(angle),"speed":{x:toFloat(dx/dt*1000),y:toFloat(dy/dt*1000)}});
                                            if(Math.abs(angle - 360) < THRESHOLD_ANGLE || Math.abs(angle - 0) < THRESHOLD_ANGLE){trigger(e.target, 'swiperight', {});}
                                            else if(Math.abs(angle - 90) < THRESHOLD_ANGLE){trigger(e.target, 'swipeup', {});}
                                            else if(Math.abs(angle - 180) < THRESHOLD_ANGLE){trigger(e.target, 'swipeleft', {});}
                                            else if(Math.abs(angle - 270) < THRESHOLD_ANGLE){trigger(e.target, 'swipedown', {});}
                                        }
                                    }
                                    break;
                            }
                            STATE.count=0;
                            STATE.moved=0;
                        }
                        break;
                }
            }
            function trigger(el,name,data){
                setTimeout(function(){
                    var evt = document.createEvent("CustomEvent");
                    evt.initCustomEvent(name, true, true, data);
                    el.dispatchEvent(evt);
                },0)
            }
            function bind(){
                if(hasTouch){
                    el.addEventListener("touchstart", handler, false);
                    el.addEventListener("touchmove", handler, false);
                    el.addEventListener("touchend", handler, false);
                } else {
                    el.addEventListener("mousedown", handler, false);
                    el.addEventListener("mousemove", handler, false);
                    el.addEventListener("mouseup", handler, false);
                }
            }
            bind();
        }

        exports(Touchable);
    });
})();//gesture

模块化方案使用Module

代码参考了AlloyFinger

事件所带的参数一直很纠结,犹豫不定哪些是必须的,哪些是不需要的

(function(){Module("Touchable",function(require,exports){var hasTouch = 'ontouchstart' in document;var EvtType = {ON:hasTouch?"touchstart":"mousedown",OFF:hasTouch?"touchend":"mouseup",MOVE:hasTouch?"touchmove":"mousemove"}var CenterPoint;var THRESHOLD_MOVE = 5,THRESHOLD_TAP = 200,THRESHOLD_DOUBLETAP = 350,THRESHOLD_HOLD = 600,THRESHOLD_ANGLE = 10,THRESHOLD_ROTATE = 5,THRESHOLD_SCALE = 10,THRESHOLD_SWIPE = 10;
function vt(x,y){this.x=x;this.y=y;}vt.prototype.add=function(v){return new vt(this.x + v.x, this.y + v.y)}vt.prototype.minus=function(v){return new vt(this.x - v.x, this.y - v.y)}vt.prototype.dot=function(v){return this.x * v.x + this.y * v.y}vt.prototype.cross=function(v){return this.x * v.y - v.x * this.y}vt.prototype.mod=function(){return Math.sqrt(this.x * this.x + this.y * this.y)}vt.prototype.angle=function(v){var angle,r,mr;mr = this.mod() * v.mod();if(mr !== 0){r = this.dot(v) / mr;if(r > 1) r = 1;angle=Math.acos(r);if(this.cross(v) > 0){angle *= -1;}}else{angle=0;}return Number((angle * 180 / Math.PI).toFixed(2));}
function toFloat(num,len){return Number((num||0).toFixed(len||2))}function getPoints(e){var downList,upList;switch(e.type){case EvtType.ON:case EvtType.MOVE:if(!hasTouch){downList = [new vt(e.clientX,e.clientY)];} else {downList = [].map.call(e.touches,function(e){return new vt(e.clientX,e.clientY)});}upList = [];break;case EvtType.OFF:if(!hasTouch){downList = [];upList = [new vt(e.clientX,e.clientY)];} else {downList = [].map.call(e.touches,function(e){return new vt(e.clientX,e.clientY)});upList = [].map.call(e.changedTouches,function(e){return new vt(e.clientX,e.clientY)});}break;}if(e.ctrlKey) downList=[new vt(CenterPoint.clientX,CenterPoint.clientY)].concat(downList);return {0:downList,1:upList}}
function Touchable(el,isPD){var STATE={},p,pts,now,timestamp,last;CenterPoint = {clientX:(document.body.clientWidth)*0.5, clientY:(document.body.clientHeight)*0.5};
function handler(e){isPD&&e.preventDefault();switch(e.type){case EvtType.ON:pts=getPoints(e);p={"0":{},"1":{},"2":{}};timestamp=(+new Date);STATE.count=pts[0].length;STATE.pressDown=1;switch(pts[0].length){case 1:p[0][0]=p[1][0]=p[2][0]=pts[0][0];trigger(e.target, 'pressdown', {});setTimeout(function(){if(STATE.count===1 && !STATE.moved){STATE.hold = 1;trigger(e.target, 'hold', {"point":new vt(pts[0][0].x, pts[0][0].y)});}}, THRESHOLD_HOLD);break;case 2:p[0][0]=p[1][0]=p[2][0]=pts[0][0];p[0][1]=p[1][1]=p[2][1]=pts[0][1];break;}break;case EvtType.MOVE:if(STATE.pressDown){now=(+new Date);pts=getPoints(e);switch(pts[0].length){case 1:var ds = pts[0][0].minus(p[0][0]).mod();if(!STATE.hold) trigger(e.target, 'swiping', {"from":new vt(p[0][0].x, p[0][0].y), "point":new vt(pts[0][0].x, pts[0][0].y)});if(!STATE.moved && ds > THRESHOLD_MOVE){STATE.moved=1}if(STATE.moved){if(!STATE.hold && !STATE.swipe && ds > THRESHOLD_SWIPE){STATE.swipe=1}if(STATE.hold) trigger(e.target, 'drag', {"from":new vt(p[0][0].x, p[0][0].y), "point":new vt(pts[0][0].x, pts[0][0].y)});}p[1][0]=p[2][0];p[2][0]=pts[0][0];break;case 2:var d1 = p[0][1].minus(p[0][0]).mod();var d2 = pts[0][1].minus(pts[0][0]).mod();var diff = d2 - d1;var ag = pts[0][1].minus(pts[0][0]).angle(p[0][1].minus(p[0][0]));if(!STATE.scale && Math.abs(diff) > THRESHOLD_SCALE){STATE.move = 1;STATE.scale = 1;}if(STATE.scale){trigger(e.target, 'scaling', {"scale":toFloat(d2/d1)});}if(!STATE.rotate && d1 > 80 && (Math.abs(ag)>THRESHOLD_ROTATE)){STATE.move = 1;STATE.rotate = 1;}if(STATE.rotate){trigger(e.target, 'rotating', {"rotate":toFloat(ag)});}p[1][0]=p[2][0];p[2][0]=pts[0][0];p[1][1]=p[2][1];p[2][1]=pts[0][1];break;}last=timestamp;timestamp=now;}break;case EvtType.OFF:if(STATE.pressDown){STATE.pressDown = 0;pts=getPoints(e);var dt=(+new Date)-last;switch(pts[0].length){case 1:var d0 = p[0][1].minus(p[0][0]).mod();var d1 = p[1][1].minus(p[1][0]).mod();var d2 = pts[1][1].minus(pts[1][0]).mod();var ds = d2 - d1;var da = pts[1][1].minus(pts[1][0]).angle(p[1][1].minus(p[1][0]));var ag = pts[1][1].minus(pts[1][0]).angle(p[0][1].minus(p[0][0]));if(STATE.scale){STATE.scale=0;p.scale=toFloat(d2/d0);trigger(e.target, 'scale', {scale:toFloat(d2/d0),speed:toFloat(ds/dt*1000)});if(p.scale > 1){ trigger(e.target, 'scaleup', {});}if(p.scale < 1){ trigger(e.target, 'scaledown', {});}}if(STATE.rotate){STATE.rotate=0;p.rotate=toFloat(ag);trigger(e.target, 'rotate', {rotate:toFloat(ag),speed:toFloat(da/dt*1000)});if(p.rotate < -45 || p.rotate > 315){ trigger(e.target, 'rotateright', {});}if(p.rotate > 45 && p.rotate < 315){ trigger(e.target, 'rotateleft', {});}}break;case 0:if(!STATE.moved){if(STATE.hold){STATE.hold = 0;trigger(e.target, 'drop', {"point":new vt(pts[1][0].x, pts[1][0].y)});}else{if(STATE.doubleReady){STATE.doubleReady = 0;trigger(e.target, 'doubletap', {"point":new vt(pts[1][0].x, pts[1][0].y)});}else if(STATE.count===1){STATE.doubleReady = 1;setTimeout(function(){if(STATE.doubleReady) trigger(e.target, 'tap', {"point":new vt(pts[1][0].x, pts[1][0].y)})}, THRESHOLD_TAP)setTimeout(function(){STATE.doubleReady=0}, THRESHOLD_DOUBLETAP);}}}else{var dx = pts[1][0].x - p[1][0].x;var dy = pts[1][0].y - p[1][0].y;if(STATE.hold){STATE.hold = 0;trigger(e.target, 'drop', {"point":new vt(pts[1][0].x, pts[1][0].y)});}else{var angle = pts[1][0].minus(p[0][0]).angle(new vt(1,0));trigger(e.target, 'swipe', {"angle":toFloat(angle),"speed":{x:toFloat(dx/dt*1000),y:toFloat(dy/dt*1000)}});if(Math.abs(angle - 360) < THRESHOLD_ANGLE || Math.abs(angle - 0) < THRESHOLD_ANGLE){trigger(e.target, 'swiperight', {});}else if(Math.abs(angle - 90) < THRESHOLD_ANGLE){trigger(e.target, 'swipeup', {});}else if(Math.abs(angle - 180) < THRESHOLD_ANGLE){trigger(e.target, 'swipeleft', {});}else if(Math.abs(angle - 270) < THRESHOLD_ANGLE){trigger(e.target, 'swipedown', {});}}}break;}STATE.count=0;STATE.moved=0;}break;}}function trigger(el,name,data){setTimeout(function(){var evt = document.createEvent("CustomEvent");evt.initCustomEvent(name, true, true, data);el.dispatchEvent(evt);},0)}function bind(){if(hasTouch){el.addEventListener("touchstart", handler, false);el.addEventListener("touchmove", handler, false);el.addEventListener("touchend", handler, false);} else {el.addEventListener("mousedown", handler, false);el.addEventListener("mousemove", handler, false);el.addEventListener("mouseup", handler, false);}}bind();}
exports(Touchable);});})();//gesture

 

posted @ 2016-11-29 17:36  叮了个嘣  阅读(248)  评论(0)    收藏  举报