当前位置: 首页 > news >正文

vue超好用的自定义指令封装

一、指令封装

目录结构:

index.ts 统一注册

import { App, Directive } from "vue";
import auth from "./modules/auth";
import copy from "./modules/copy";
import waterMarker from "./modules/waterMarker";
import draggable from "./modules/draggable";
import debounce from "./modules/debounce";
import throttle from "./modules/throttle";
import longpress from "./modules/longpress";const directivesList: { [key: string]: Directive } = {auth,copy,waterMarker,draggable,debounce,throttle,longpress
};const directives = {install: function (app: App<Element>) {Object.keys(directivesList).forEach(key => {app.directive(key, directivesList[key]);});}
};export default directives;

记得use

二、自定义指令

1.防抖 v-debounce

/*** v-debounce* 按钮防抖指令,可自行扩展至input* 接收参数:function类型*/
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {__handleClick__: () => any;
}
const debounce: Directive = {mounted(el: ElType, binding: DirectiveBinding) {if (typeof binding.value !== "function") {throw "callback must be a function";}let timer: NodeJS.Timeout | null = null;el.__handleClick__ = function () {if (timer) {clearInterval(timer);}timer = setTimeout(() => {binding.value();}, 500);};el.addEventListener("click", el.__handleClick__);},beforeUnmount(el: ElType) {el.removeEventListener("click", el.__handleClick__);}
};export default debounce;

2.节流 v-throttle

/*需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。思路:1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮2、将需要触发的方法绑定在指令上使用:给 Dom 加上 v-throttle 及回调函数即可<button v-throttle="debounceClick">节流提交</button>
*/
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {__handleClick__: () => any;disabled: boolean;
}
const throttle: Directive = {mounted(el: ElType, binding: DirectiveBinding) {if (typeof binding.value !== "function") {throw "callback must be a function";}let timer: NodeJS.Timeout | null = null;el.__handleClick__ = function () {if (timer) {clearTimeout(timer);}if (!el.disabled) {el.disabled = true;binding.value();timer = setTimeout(() => {el.disabled = false;}, 1000);}};el.addEventListener("click", el.__handleClick__);},beforeUnmount(el: ElType) {el.removeEventListener("click", el.__handleClick__);}
};export default throttle;

3.复制 v-copy

/*** v-copy* 复制某个值至剪贴板* 接收参数:string类型/Ref<string>类型/Reactive<string>类型*/
import type { Directive, DirectiveBinding } from "vue";
import { ElMessage } from "element-plus";
interface ElType extends HTMLElement {copyData: string | number;__handleClick__: any;
}
const copy: Directive = {mounted(el: ElType, binding: DirectiveBinding) {el.copyData = binding.value;el.addEventListener("click", handleClick);},updated(el: ElType, binding: DirectiveBinding) {el.copyData = binding.value;},beforeUnmount(el: ElType) {el.removeEventListener("click", el.__handleClick__);}
};function handleClick(this: any) {const input = document.createElement("input");input.value = this.copyData.toLocaleString();document.body.appendChild(input);input.select();document.execCommand("Copy");document.body.removeChild(input);ElMessage({type: "success",message: "复制成功"});
}export default copy;

4.长按 v-longpress

/*** v-longpress* 长按指令,长按时触发事件*/
import type { Directive, DirectiveBinding } from "vue";const directive: Directive = {mounted(el: HTMLElement, binding: DirectiveBinding) {if (typeof binding.value !== "function") {throw "callback must be a function";}// 定义变量let pressTimer: any = null;// 创建计时器( 2秒后执行函数 )const start = (e: any) => {if (e.button) {if (e.type === "click" && e.button !== 0) {return;}}if (pressTimer === null) {pressTimer = setTimeout(() => {handler(e);}, 1000);}};// 取消计时器const cancel = () => {if (pressTimer !== null) {clearTimeout(pressTimer);pressTimer = null;}};// 运行函数const handler = (e: MouseEvent | TouchEvent) => {binding.value(e);};// 添加事件监听器el.addEventListener("mousedown", start);el.addEventListener("touchstart", start);// 取消计时器el.addEventListener("click", cancel);el.addEventListener("mouseout", cancel);el.addEventListener("touchend", cancel);el.addEventListener("touchcancel", cancel);}
};export default directive;

5.拖拽 v-draggable

/*需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。思路:1、设置需要拖拽的元素为absolute,其父元素为relative。2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值4、鼠标松开(onmouseup)时完成一次拖拽使用:在 Dom 上加上 v-draggable 即可<div class="dialog-model" v-draggable></div>
*/
import type { Directive } from "vue";
interface ElType extends HTMLElement {parentNode: any;
}
const draggable: Directive = {mounted: function (el: ElType) {el.style.cursor = "move";el.style.position = "absolute";el.onmousedown = function (e) {let disX = e.pageX - el.offsetLeft;let disY = e.pageY - el.offsetTop;document.onmousemove = function (e) {let x = e.pageX - disX;let y = e.pageY - disY;let maxX = el.parentNode.offsetWidth - el.offsetWidth;let maxY = el.parentNode.offsetHeight - el.offsetHeight;if (x < 0) {x = 0;} else if (x > maxX) {x = maxX;}if (y < 0) {y = 0;} else if (y > maxY) {y = maxY;}el.style.left = x + "px";el.style.top = y + "px";};document.onmouseup = function () {document.onmousemove = document.onmouseup = null;};};}
};
export default draggable;

6.水印 v-waterMarker

/*需求:给整个页面添加背景水印。思路:1、使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。2、将其设置为背景图片,从而实现页面或组件水印效果使用:设置水印文案,颜色,字体大小即可<div v-waterMarker="{text:'版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
*/import type { Directive, DirectiveBinding } from "vue";
const addWaterMarker: Directive = (str: string, parentNode: any, font: any, textColor: string) => {// 水印文字,父元素,字体,文字颜色let can: HTMLCanvasElement = document.createElement("canvas");parentNode.appendChild(can);can.width = 205;can.height = 140;can.style.display = "none";let cans = can.getContext("2d") as CanvasRenderingContext2D;cans.rotate((-20 * Math.PI) / 180);cans.font = font || "16px Microsoft JhengHei";cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)";cans.textAlign = "left";cans.textBaseline = "Middle" as CanvasTextBaseline;cans.fillText(str, can.width / 10, can.height / 2);parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
};const waterMarker = {mounted(el: DirectiveBinding, binding: DirectiveBinding) {addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor);}
};export default waterMarker;

7.按钮权限 v-auth

/*** v-auth* 按钮权限指令 (根据需求而定)*/
import { useAuthStore } from "@/stores/modules/auth";
import type { Directive, DirectiveBinding } from "vue";const auth: Directive = {mounted(el: HTMLElement, binding: DirectiveBinding) {const { value } = binding;const authStore = useAuthStore();const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? [];if (value instanceof Array && value.length) {const hasPermission = value.every(item => currentPageRoles.includes(item));if (!hasPermission) el.remove();} else {if (!currentPageRoles.includes(value)) el.remove();}}
};export default auth;

8.旋转 v-rotate

// 自定义指令,点击旋转 v-rotate
const rotate = {beforeMount(el: any) {el.addEventListener("click", function () {console.log(el.style.transform);el.style.transition = "all 0.3s";if (el.style.transform) {let str = el.style.transform;let deg = str.substring(str.indexOf("(") + 1, str.indexOf("d"));el.style.transform = `rotate(${Number(deg) + 180}deg)`;} else {el.style.transform = "rotate(180deg)";}});}
};export default rotate;

http://www.lryc.cn/news/238845.html

相关文章:

  • 文件描述符与锁定状态在系统层面的表示
  • C#,数值计算——插值和外推,PolCoef的计算方法与源程序
  • 单体进化微服务:拆分、注册、调用、网关、过滤、治理、分布式事务
  • 介绍正则表达式及其用法
  • SpEL 表达式 是什么
  • gbase 8s 按时间点恢复
  • OceanBase:OBServer节点管理
  • 记录一个简单的博客系统该开发过程
  • 计算机毕业设计选题推荐-家庭理财微信小程序/安卓APP-项目实战
  • html实现计算器源码
  • 处理无线debug问题
  • redis的性能管理
  • es各种报错问题及解决方案20231121
  • python数据结构与算法-10_递归
  • 如何设计鞋材出库入账管理系统
  • 一个简单的QT应用示例
  • 南京数字孪生赋能工业制造,加速推进制造业数字化转型
  • Visual Studio Code 从英文界面切换中文
  • 邦芒支招:利用自荐电话求职的七大技巧
  • 埃尔米特插值(hermite 插值) C++
  • mysql优化之explain 以及 索引优化
  • WebSocket --- ws模块源码解析(详解)
  • 一文带你拿下MySQL之增删查改(基础)
  • 2023亿发数字化智能工单,专业管理工单处理全流程,助力企业转型腾飞
  • JavaScript 常用符号
  • GPT-4:论文阅读笔记
  • hm商城微服务远程调用及拆分
  • 设置指定时间之前的时间不可选
  • Java使用Redis来实现分布式锁
  • 移动端表格分页uni-app