Element-ui 之 repeat-click 长按功能指令源码分析

此篇分析一下 element-ui 组件库中,repeat-click 指令的作用和源代码。

一. repeat-click 指令的应用

repeat-click 指令用于在用户在长按鼠标左键时,持续的执行其绑定的方法,直到用户抬起鼠标左键。

element-ui 组件库中,有两处应用到了:

  1. InputNumber 组件,在长按控制按钮(加号、减号)时,执行连续增加或减少。
  1. TimePicker 时间选择器组件,在长按上下箭头时,执行时间的切换。

二. repeat-click 指令源码分析

2.1 repeat-click 源码实现思路

实现思路:

设定一个目标时间,用鼠标左键按下和抬起的持续时间与这个目标时间做对比:

  • 第一种情况是小于目标时间,那么就执行一次绑定的方法。
  • 第二种情况是大于目标时间,那么就以目标时间为间隔,持续的执行绑定的方法。

具体实现步骤:

  1. 在 vue 自定义指令的 bind 钩子中做处理,先设置间隔的时间,mac 为 100 毫秒,其余为 200 毫秒,然后将绑定的方法取出。
  2. 增加 mousedown 事件的监听,先去判断点击的是否为鼠标左键,如果不是,直接 return;如果是鼠标左键,则记录按下鼠标左键的时间,并且设置计时器,以目标时间为间隔持续的执行绑定的方法。
  3. 绑定鼠标的 mouseup 事件,判断如果鼠标抬起时间减去按下时间小于间隔时间,则执行一次绑定的方法。并且在 mouseup 时,还需要清除计时器,停止执行绑定的方法。

2.2 repeat-click 源码分析

js 复制代码
export default {
  bind(el, binding, vnode) {
    // 设置计时器
    let interval = null;
    let startTime;
    // 每隔多长时间执行一次方法
    const maxIntervals = isMac() ? 100 : 200;
    // 绑定的方法
    const handler = () => vnode.context[binding.expression].apply();
    // 清除方法
    const clear = () => {
      // 小于间隔时间,则执行方法
      if (Date.now() - startTime < maxIntervals) {
        handler();
      }
      // 并且在鼠标抬起时清除计时器
      clearInterval(interval);
      interval = null;
    }
    on(el, 'mousedown', (e) => {
      // 如果点击的不是鼠标左键,则直接 return 
      if (e.button !== 0) return;
      // 变量 startTime 记录当前鼠标 mousedown 的时间
      startTime = Date.now();
      // 绑定鼠标 mouseup 事件,在 mouseup 事件时执行清除方法
      once(document, 'mouseup', clear);
      clearInterval(interval);
      // 每隔 maxIntervals 时间执行一次方法
      interval = setInterval(handler, maxIntervals);
    })
  }
}

涉及到的封装监听事件、以及移除监听事件的相关方法:

(1)on 方法:用于绑定监听事件,兼容 IE 浏览器

js 复制代码
export const on = (function() {
  if (!isServer && document.addEventListener) {
    // 判断 document.addEventListener 是否存在,
    return function(element, event, handler) {
      // 如果元素、事件、回调方法都存在则绑定监听事件
      if (element && event && handler) {
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    // 不存在,使用 attachEvent 兼容IE浏览器
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();

(2)off 方法:用于取消事件监听,兼容 IE 浏览器

js 复制代码
export const off = (function() {
  if (!isServer && document.removeEventListener) {
    // 判断 document.removeEventListener 是否存在
    return function(element, event, handler) {
      // 如果元素、事件存在,则取消监听的绑定事件
      if (element && event) {
        element.removeEventListener(event, handler, false);
      }
    };
  } else {
    // 不存在,使用 detachEvent 兼容IE浏览器
    return function(element, event, handler) {
      if (element && event) {
        element.detachEvent('on' + event, handler);
      }
    };
  }
})();

(3)once 方法:用于监听绑定事件后,回调函数只触发一次

js 复制代码
export const once = function(el, event, fn) {
  var listener = function() {
   // 如果传入了回调函数,则执行该函数
   if (fn) {
     fn.apply(this, arguments);
   }
   // 取消事件监听
   off(el, event, listener);
  };
  // 绑定事件监听
  on(el, event, listener);
};

三. InputNumber 组件使用该指令的具体情况

html 复制代码
<template>
  <!-- 使用自定义指令 -->
  <span
    v-repeat-click="decrease"
  >
  </span>
</template>
<script>
  // 引入自定义指令
  import RepeatClick from '../../utils/directives/repeat-click';
  // 注册自定义指令
  export default {
    directives: {
      repeatClick: RepeatClick
    },
  }
</script>
相关推荐
@大迁世界3 小时前
第03章: Vue 3 组合式函数深度指南
前端·javascript·vue.js·前端框架·ecmascript
二十雨辰3 小时前
vite与ts的结合
开发语言·前端·vue.js
我是日安3 小时前
从零到一打造 Vue3 响应式系统 Day 25 - Watch:清理 SideEffect
前端·javascript·vue.js
我的div丢了肿么办4 小时前
vue3使用h函数如何封装组件和$attrs和props的区别
前端·javascript·vue.js
我的写法有点潮4 小时前
竟然被element-plus背刺了
前端·javascript·vue.js
岁月宁静4 小时前
AI 聊天消息长列表性能优化:后端分页 + 前端虚拟滚动
前端·vue.js·人工智能
Olrookie4 小时前
若依前后端分离版学习笔记(十九)——导入,导出实现流程及图片,文件组件
前端·vue.js·笔记
墨客希4 小时前
通俗易懂的理解Vue.js
vue.js·flutter
浩男孩4 小时前
🍀终于向常量组件下手了,使用TypeScript 基于 TDesign二次封装常量组件 🚀🚀
前端·vue.js
江拥羡橙12 小时前
Vue和React怎么选?全面比对
前端·vue.js·react.js