vue 自定義點選事件
阿新 • • 發佈:2019-02-07
我們在專案開發中使用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中可以傳物件,陣列,字串,數字。