1. 程式人生 > >vue 自定義點選事件

vue 自定義點選事件

我們在專案開發中使用vue的時候發現vue內建的click事件在移動端使用會有300毫秒的點選延遲,所以我們就自己寫了一個點選的自定義事件,希望對各位有所幫助,不足之處希望各位能夠及時溝通。

廢話就不多說了,直接貼程式碼了:

(function(){
var touchSupport = (('ontouchstart' in window) || 
                (navigator.MaxTouchPoints > 0) || 
                (navigator.msMaxTouchPoints > 0)) 
                /(Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone)/i.test(navigator.userAgent);
var touchSpace = {};

//  獲取支援的事件型別
var touchEventType = (function(){

    return touchSupport ? 
        {
            start: 'touchstart',
            move: 'touchmove',
            end : 'touchend',
            cancel: 'touchcancel'
        } : {
            start : 'mousedown',
            move: 'mousemove',
            end : 'mouseup',
            cancel: 'mousecancel'
        }
})();

//  繫結事件
var touchEventAdd = function(el, handler, capture){

    el.addEventListener(touchEventType.start, handler.start, capture);
    el.addEventListener(touchEventType.move, handler.move, capture);
    el.addEventListener(touchEventType.end, handler.end, capture);
    el.addEventListener(touchEventType.cancel, handler.end, capture);
};

//  解除繫結事件
var touchEventRemove = function(el, handler){

    el.removeEventListener(touchEventType.start, handler.start);
    el.removeEventListener(touchEventType.move, handler.move);
    el.removeEventListener(touchEventType.end, handler.end);
    el.removeEventListener(touchEventType.cancel, handler.end);
};

//  判斷事件型別
var touchType = function(start, end){

    //  初始化變數
    var type = null;
    var direction = null;
    var ratio = window.devicePixelRatio;
    var during = end.T - start.T;
    var h = (end.X - start.X)/ratio;
    var v = (end.Y - start.Y)/ratio;
    var absh = Math.abs(h);
    var absv = Math.abs(v);
    var move = Math.sqrt(Math.pow(h,2) + Math.pow(v,2));

    //  判斷事件按型別
    switch (true){ 
        case (during < 32): break;
        case (move < 3 && during > 740): type = 'holdtap'; break; 
        case (move < 3 && during < 300): type = 'tap'; break;
        case (move > 5 && during < 300): type = 'swipe'; break; 
        default: break;
    }
    switch (type == 'swipe'){ 
        case (absv < 5 && absh > 5 && h > 0 ): direction = 'right'; break; 
        case (absv < 5 && absh > 5 && h < 0 ): direction = 'left'; break;
        case (absv > 5 && absh < 5 && v > 0 ): direction = 'down'; break;
        case (absv > 5 && absh < 5 && v < 0 ): direction = 'up'; break;
        default: break;
    }
    return {
        type: type,
        direction: direction,
        move: move,
        during: during
    };
};

//  事件調節器
var touchModifier = function (dom, e, modifiers) {

    modifiers.stop && e.stopPropagation();
    modifiers.prevent && e.preventDefault();
    if(modifiers.self && dom !== e.target){
        return false;
    }
    return true;
};

//  觸控事件類
var touchHandler = function(type){

    return  {
        start: function(e){

            //  初始化變數
            var key = this['vue-touch-id'];
            var data = touchSpace[key][type];
            var modifiers = data.modifiers;
            var method = data.method;
            var start = data.start;

            //  初始化觸控位置
            start.X = touchSupport ? e.touches[0].pageX : e.screenX;
            start.Y = touchSupport ? e.touches[0].pageY : e.screenY;
            start.T = touchTimestamp();

            //  如果調節器停止事件
            if (!touchModifier(this,e,modifiers)){
                return false;
            }

            //  如果是拖拽,則觸發 start
            if (type === 'drag') {
                e.source = this;
                e.touchPoint = start;
                method['start'].apply(e, [e].concat(data.params));
            }

            //  如果是長按,則通過定時器實現
            if (type === 'holdtap'){
                if (data.timer) {
                    clearTimeout(data.timer);
                }
                data.timer = setTimeout(function () {

                    method.apply(e, [e].concat(data.params));
                    data.timer = 0;
                }, 750);
            }
        },
        move: function(e){

            //  初始化變數
            var key = this['vue-touch-id'];
            var data = touchSpace[key][type];
            var modifiers = data.modifiers;
            var method = data.method;
            var move = {
                X : touchSupport ? e.changedTouches[0].pageX : e.screenX,
                Y : touchSupport ? e.changedTouches[0].pageY : e.screenY,
                T : touchTimestamp()
            };

            if (type === 'drag') {
                e.source = this;
                e.touchPoint = move;
                method['move'].apply(e, [e].concat(data.params));
                e.preventDefault();
                return false;
            } else {
                fixedTouchScrollBug();
            }
        },
        end: function(e){

            //  初始化手指位置
            var key = this['vue-touch-id'];
            var data = touchSpace[key][type];
            var modifiers = data.modifiers;
            var method = data.method;
            var start = data.start;
            var end = {
                X : touchSupport ? e.changedTouches[0].pageX : e.screenX,
                Y : touchSupport ? e.changedTouches[0].pageY : e.screenY,
                T : touchTimestamp()
            };
            var action = touchType(start,end);

            //  如果調節器停止事件
            if(!touchModifier(this,e,modifiers)){
                return false;
            }

            //  如果正在滾動中
            if ( touchScrolling === true){
                return false;
            }

            //  如果是拖拽事件
            if (type === 'drag') {
                e.source = this;
                e.move = action.move;
                e.during = action.during;
                e.touchPoint = end;
                method['end'].apply(e, [e].concat(data.params));
            }

            //  判斷事件型別
            if (!type || action.type !== type) {
                return false;
            }

            //  設定滑動方向
            if (type === 'swipe'){
                e.direction = action.direction;
            }

            //  如果是其他事件
            if (type !== 'holdtap' && type !== 'drag'){
                e.source = this;
                method.apply(e, [e].concat(data.params));
            }

            e.stopImmediatePropagation();
            return false;
        }
    };      
};

//  獲取隨機標識
var touchRandomKey = function(){
    return 'vue-touch-' + (Math.random() + '').replace(/\D/g, '');
};

//  獲取時間戳
var touchTimestamp = function(){
    return new Date().getTime();
};

Vue.directive( 'touch', {
    bind:function(el, binding) { 

        //  初始化變數
        var key = '';
        var type = binding.arg;
        var data = binding.value || {};
        var modifiers = binding.modifiers;
        var capture = !!modifiers.capture;
        var method = ( typeof data === 'function' ? data : ( data.event || function(){} ) );
        var params = ( data.params || [] );
        var handler = new touchHandler(type);

        //  初始化物件標識
        if (el.hasOwnProperty('vue-touch-id')){
            key = el['vue-touch-id'];
        } else { 
            key = el['vue-touch-id'] = touchRandomKey();
            touchSpace[key] = {};
        }

        //  如果是拖拽方法
        if (type === 'drag') {
            method = {
                start: data.start || function(){},
                move: data.move || function(){},
                end: data.end || function(){}
            }
        }

        //  初始化事件容器
        touchSpace[key][type] = {
            modifiers: modifiers,
            method: method,
            params: params,
            handler: handler,
            start : {
                X : 0,
                Y : 0,
                T : 0
            },
            timer : 0
        };

        //  繫結事件
        touchEventAdd(el,handler,capture);
    },
    update: function(el, binding){

        var key = el['vue-touch-id'];
        var type = binding.arg;
        var params = ( binding.value.params || [] );

        touchSpace[key][type].params = params;
    },
    unbind:function(el){

        //  初始化變數
        var key = el['vue-touch-id'];
        var data = touchSpace[key];

        //  迴圈繫結的事件, 接觸繫結
        for (var item in data){
            if (data.hasOwnProperty(item) && item){
                touchEventRemove(el,data[item].handler);
            }
        }

        //  釋放變數記憶體
        delete touchSpace[key];
    }
});

//  修復滾動條與 touch 事件的衝突
var timer, lastScrollTop = 0, touchScrolling = false;
var fixedTouchScrollBug = function(){

    var currentScrollTop = document.body.scrollTop;

    if ( lastScrollTop == currentScrollTop ) {
        if (touchScrolling === true){
            timer = window.setTimeout(function(){

                touchScrolling = false;
            }, 100);    
        }
    } else {
        touchScrolling = true;
        lastScrollTop = currentScrollTop;
    }
};
window.setInterval(fixedTouchScrollBug, 10
})();

以上則是事件的方法,在頁面中呼叫的方式也比較方便,與click的用法基本上可以說是大同小異,下面則是頁面中使用的方法:

<div class="best" v-touch:tap="{ event: method, params: [ parameter ] }">點選</div>

<div class="best" v-touch:tap="{ event: method, params: [] }">點選</div>

點選事件的頁面呼叫方法如上: event是呼叫的點選時呼叫的方法名稱,params是呼叫方法時需要的引數,params中可以傳物件,陣列,字串,數字。