elementUI——directives:mousewheel & repeat-click

本章講解elementUI中用到的指令,自定義指令的基本方法可以參考官網(wǎng)-自定義指令
說明:本文基于element-ui@2.13.0,源碼詳見element。
在src/directives下有兩個vue指令:mousewheel和repeat-click

mousewheel

在element-ui/packages/table/src/table.vue用到v-mousewheel指令。

import normalizeWheel from 'normalize-wheel';

const isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

const mousewheel = function(element, callback) {
  if (element && element.addEventListener) {
    element.addEventListener(isFirefox ? 'DOMMouseScroll' : 'mousewheel', function(event) {
      const normalized = normalizeWheel(event);
      callback && callback.apply(this, [event, normalized]);
    });
  }
};

export default {
  bind(el, binding) {
    mousewheel(el, binding.value);
  }
};

代碼的重點是用到normalizeWheel
normalize-wheel以dependencies的形式存在,主要下面三個問題:

  • 解決不同瀏覽器、不同平臺的兼容性問題:
// Browsers
var _ie, _firefox, _opera, _webkit, _chrome;

// Actual IE browser for compatibility mode
var _ie_real_version;

// Platforms
var _osx, _windows, _linux, _android;

// Architectures
var _win64;

// Devices
var _iphone, _ipad, _native;

var _mobile;
  • 內(nèi)部通過normalizeWheel.getEventType可以獲取到當前瀏覽器支持的滾動事件:
normalizeWheel.getEventType = function() /*string*/ {
  return (UserAgent_DEPRECATED.firefox())
           ? 'DOMMouseScroll'
           : (isEventSupported('wheel'))
               ? 'wheel'
               : 'mousewheel';
};
  • 內(nèi)部通過isEventSupported,檢測滾輪監(jiān)控事件
/**
 * Checks if an event is supported in the current execution environment.
 * Borrows from Modernizr.
 * @param {string} eventNameSuffix Event name, e.g. "click".
 * @param {?boolean} capture Check if the capture phase is supported.
 * @return {boolean} True if the event is supported.
 * @internal
 * @license Modernizr 3.0.0pre (Custom Build) | MIT
 */
function isEventSupported(eventNameSuffix, capture) {
  if (!ExecutionEnvironment.canUseDOM ||
      capture && !('addEventListener' in document)) {
    return false;
  }

  var eventName = 'on' + eventNameSuffix;
  var isSupported = eventName in document;

  if (!isSupported) {
    var element = document.createElement('div');
    element.setAttribute(eventName, 'return;');
    isSupported = typeof element[eventName] === 'function';
  }

  if (!isSupported && useHasFeature && eventNameSuffix === 'wheel') {
    // This is the only way to test support for the `wheel` event in IE9+.
    isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
  }

  return isSupported;
}
  • 不同的瀏覽器,事件的滾動信息可能在detail、wheelDelta、wheelDeltaY或wheelDeltaX中,還有side scrolling的問題,以及滾動值單位問題,該工具庫通過normalizeWheel進行了統(tǒng)一處理并對外暴露四個值spinX、spinY、pixelX、pixelY
// Reasonable defaults
var PIXEL_STEP  = 10;
var LINE_HEIGHT = 40;
var PAGE_HEIGHT = 800;
function normalizeWheel(/*object*/ event) /*object*/ {
  var sX = 0, sY = 0,       // spinX, spinY
      pX = 0, pY = 0;       // pixelX, pixelY

  // Legacy
  if ('detail'      in event) { sY = event.detail; }
  if ('wheelDelta'  in event) { sY = -event.wheelDelta / 120; }
  if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120; }
  if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120; }

  // side scrolling on FF with DOMMouseScroll
  if ( 'axis' in event && event.axis === event.HORIZONTAL_AXIS ) {
    sX = sY;
    sY = 0;
  }

  pX = sX * PIXEL_STEP;
  pY = sY * PIXEL_STEP;

  if ('deltaY' in event) { pY = event.deltaY; }
  if ('deltaX' in event) { pX = event.deltaX; }

  if ((pX || pY) && event.deltaMode) {
    if (event.deltaMode == 1) {          // delta in LINE units
      pX *= LINE_HEIGHT;
      pY *= LINE_HEIGHT;
    } else {                             // delta in PAGE units
      pX *= PAGE_HEIGHT;
      pY *= PAGE_HEIGHT;
    }
  }

  // Fall-back if spin cannot be determined
  if (pX && !sX) { sX = (pX < 1) ? -1 : 1; }
  if (pY && !sY) { sY = (pY < 1) ? -1 : 1; }

  return { spinX  : sX,
           spinY  : sY,
           pixelX : pX,
           pixelY : pY };
}

repeat-click

el-input-number組件中,點+、-時,會用到v-repeat-click


v-repeat-click會注冊mousedown事件,當用戶連續(xù)點擊+時:

  1. 當用戶鼠標左鍵一直按住不松手,只會觸發(fā)一次觸發(fā)mousedown的回調(diào),但實際測量el-input-number發(fā)現(xiàn),輸入框中的數(shù)字會持續(xù)變大,原因就在于mousedown回調(diào)中加入了定時器,當鼠標松開,觸發(fā)一次mouseup回調(diào)方法,取消該定時器;這也許是directive為什么叫repeat-click的緣故吧;
  2. 如果時間間隔大于100毫秒,那么mousedown的回調(diào)方法里的setInterval回調(diào)就會執(zhí)行(及handler,本質(zhì)上就是執(zhí)行上圖的decreaseincrease方法);
    如果時間間隔小于100毫秒,定時器就會取消;
  3. mousedown的回調(diào)方法(clear方法)每次執(zhí)行時,都會通過once方法注冊并執(zhí)行一次mouseup回調(diào);
  4. mouseup回調(diào)中,如果發(fā)現(xiàn)距離最近一次點擊時間小于100ms,就會執(zhí)行一次handler方法,并清除定時器;
import { once, on } from 'element-ui/src/utils/dom';

export default {
  bind(el, binding, vnode) {
    let interval = null;
    let startTime;
    const handler = () => vnode.context[binding.expression].apply(); // 調(diào)用傳入的方法
    const clear = () => {
      if (Date.now() - startTime < 100) {
        handler();
      }
      clearInterval(interval);
      interval = null;
    };

    on(el, 'mousedown', (e) => {
      if (e.button !== 0) return;
      startTime = Date.now();
      once(document, 'mouseup', clear);
      clearInterval(interval);
      interval = setInterval(handler, 100);
    });
  }
};

repeat-click依賴element-ui/src/utils/dom中的兩個方法:onceon.

  • on
    很簡單,判斷是用addEventListener還是attachEvent來注冊事件監(jiān)控器:
export const on = (function() {
  if (!isServer && document.addEventListener) {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();
  • once
    從語義上來看,就是注冊事件監(jiān)聽器并且只執(zhí)行一次,然后取消監(jiān)聽方法:
export const once = function(el, event, fn) {
  var listener = function() {
    if (fn) {
      fn.apply(this, arguments);
    }
    off(el, event, listener); // 跟`on`方法相反,用來取消事件監(jiān)聽器
  };
  on(el, event, listener);
};

推薦

ElementUI的結(jié)構(gòu)與源碼研究
elementUI——mixins
elementUI——locale,國際化方案
elementU——transitions
elementUI——主題

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的。事件,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬...
    LemonnYan閱讀 745評論 0 4
  • PNG 有PNG8和truecolor PNG PNG8類似GIF顏色上限為256,文件小,支持alpha透明度,...
    hudaren閱讀 1,848評論 0 0
  • 1.JQuery 基礎 改變web開發(fā)人員創(chuàng)造搞交互性界面的方式。設計者無需花費時間糾纏JS復雜的高級特性。 1....
    LaBaby_閱讀 1,277評論 0 1
  • 事件流: 事件流:頁面接收事件的順序。 IE定義的:事件冒泡流(由最具體的元素依次傳播到DOM樹的最上層的Docu...
    xiaoguo16閱讀 664評論 0 0
  • 事件流 Click Me 冒泡型事件:事件按照從最特定的事件目標到最不特定的事件目標的順序觸發(fā)。觸發(fā)的順序是:di...
    醋留香閱讀 964評論 0 1

友情鏈接更多精彩內(nèi)容