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


    一、指令封装

    目录结构:

    index.ts 统一注册

    1. import { App, Directive } from "vue";
    2. import auth from "./modules/auth";
    3. import copy from "./modules/copy";
    4. import waterMarker from "./modules/waterMarker";
    5. import draggable from "./modules/draggable";
    6. import debounce from "./modules/debounce";
    7. import throttle from "./modules/throttle";
    8. import longpress from "./modules/longpress";
    9. const directivesList: { [key: string]: Directive } = {
    10. auth,
    11. copy,
    12. waterMarker,
    13. draggable,
    14. debounce,
    15. throttle,
    16. longpress
    17. };
    18. const directives = {
    19. install: function (app: App) {
    20. Object.keys(directivesList).forEach(key => {
    21. app.directive(key, directivesList[key]);
    22. });
    23. }
    24. };
    25. export default directives;

    记得use

    二、自定义指令

    1.防抖 v-debounce

    1. /**
    2. * v-debounce
    3. * 按钮防抖指令,可自行扩展至input
    4. * 接收参数:function类型
    5. */
    6. import type { Directive, DirectiveBinding } from "vue";
    7. interface ElType extends HTMLElement {
    8. __handleClick__: () => any;
    9. }
    10. const debounce: Directive = {
    11. mounted(el: ElType, binding: DirectiveBinding) {
    12. if (typeof binding.value !== "function") {
    13. throw "callback must be a function";
    14. }
    15. let timer: NodeJS.Timeout | null = null;
    16. el.__handleClick__ = function () {
    17. if (timer) {
    18. clearInterval(timer);
    19. }
    20. timer = setTimeout(() => {
    21. binding.value();
    22. }, 500);
    23. };
    24. el.addEventListener("click", el.__handleClick__);
    25. },
    26. beforeUnmount(el: ElType) {
    27. el.removeEventListener("click", el.__handleClick__);
    28. }
    29. };
    30. export default debounce;

    2.节流 v-throttle

    1. /*
    2. 需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。
    3. 思路:
    4. 1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮
    5. 2、将需要触发的方法绑定在指令上
    6. 使用:给 Dom 加上 v-throttle 及回调函数即可
    7. */
    8. import type { Directive, DirectiveBinding } from "vue";
    9. interface ElType extends HTMLElement {
    10. __handleClick__: () => any;
    11. disabled: boolean;
    12. }
    13. const throttle: Directive = {
    14. mounted(el: ElType, binding: DirectiveBinding) {
    15. if (typeof binding.value !== "function") {
    16. throw "callback must be a function";
    17. }
    18. let timer: NodeJS.Timeout | null = null;
    19. el.__handleClick__ = function () {
    20. if (timer) {
    21. clearTimeout(timer);
    22. }
    23. if (!el.disabled) {
    24. el.disabled = true;
    25. binding.value();
    26. timer = setTimeout(() => {
    27. el.disabled = false;
    28. }, 1000);
    29. }
    30. };
    31. el.addEventListener("click", el.__handleClick__);
    32. },
    33. beforeUnmount(el: ElType) {
    34. el.removeEventListener("click", el.__handleClick__);
    35. }
    36. };
    37. export default throttle;

    3.复制 v-copy

    1. /**
    2. * v-copy
    3. * 复制某个值至剪贴板
    4. * 接收参数:string类型/Ref类型/Reactive类型
    5. */
    6. import type { Directive, DirectiveBinding } from "vue";
    7. import { ElMessage } from "element-plus";
    8. interface ElType extends HTMLElement {
    9. copyData: string | number;
    10. __handleClick__: any;
    11. }
    12. const copy: Directive = {
    13. mounted(el: ElType, binding: DirectiveBinding) {
    14. el.copyData = binding.value;
    15. el.addEventListener("click", handleClick);
    16. },
    17. updated(el: ElType, binding: DirectiveBinding) {
    18. el.copyData = binding.value;
    19. },
    20. beforeUnmount(el: ElType) {
    21. el.removeEventListener("click", el.__handleClick__);
    22. }
    23. };
    24. function handleClick(this: any) {
    25. const input = document.createElement("input");
    26. input.value = this.copyData.toLocaleString();
    27. document.body.appendChild(input);
    28. input.select();
    29. document.execCommand("Copy");
    30. document.body.removeChild(input);
    31. ElMessage({
    32. type: "success",
    33. message: "复制成功"
    34. });
    35. }
    36. export default copy;

    4.长按 v-longpress

    1. /**
    2. * v-longpress
    3. * 长按指令,长按时触发事件
    4. */
    5. import type { Directive, DirectiveBinding } from "vue";
    6. const directive: Directive = {
    7. mounted(el: HTMLElement, binding: DirectiveBinding) {
    8. if (typeof binding.value !== "function") {
    9. throw "callback must be a function";
    10. }
    11. // 定义变量
    12. let pressTimer: any = null;
    13. // 创建计时器( 2秒后执行函数 )
    14. const start = (e: any) => {
    15. if (e.button) {
    16. if (e.type === "click" && e.button !== 0) {
    17. return;
    18. }
    19. }
    20. if (pressTimer === null) {
    21. pressTimer = setTimeout(() => {
    22. handler(e);
    23. }, 1000);
    24. }
    25. };
    26. // 取消计时器
    27. const cancel = () => {
    28. if (pressTimer !== null) {
    29. clearTimeout(pressTimer);
    30. pressTimer = null;
    31. }
    32. };
    33. // 运行函数
    34. const handler = (e: MouseEvent | TouchEvent) => {
    35. binding.value(e);
    36. };
    37. // 添加事件监听器
    38. el.addEventListener("mousedown", start);
    39. el.addEventListener("touchstart", start);
    40. // 取消计时器
    41. el.addEventListener("click", cancel);
    42. el.addEventListener("mouseout", cancel);
    43. el.addEventListener("touchend", cancel);
    44. el.addEventListener("touchcancel", cancel);
    45. }
    46. };
    47. export default directive;

    5.拖拽 v-draggable

    1. /*
    2. 需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。
    3. 思路:
    4. 1、设置需要拖拽的元素为absolute,其父元素为relative。
    5. 2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。
    6. 3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值
    7. 4、鼠标松开(onmouseup)时完成一次拖拽
    8. 使用:在 Dom 上加上 v-draggable 即可
    9. */
    10. import type { Directive } from "vue";
    11. interface ElType extends HTMLElement {
    12. parentNode: any;
    13. }
    14. const draggable: Directive = {
    15. mounted: function (el: ElType) {
    16. el.style.cursor = "move";
    17. el.style.position = "absolute";
    18. el.onmousedown = function (e) {
    19. let disX = e.pageX - el.offsetLeft;
    20. let disY = e.pageY - el.offsetTop;
    21. document.onmousemove = function (e) {
    22. let x = e.pageX - disX;
    23. let y = e.pageY - disY;
    24. let maxX = el.parentNode.offsetWidth - el.offsetWidth;
    25. let maxY = el.parentNode.offsetHeight - el.offsetHeight;
    26. if (x < 0) {
    27. x = 0;
    28. } else if (x > maxX) {
    29. x = maxX;
    30. }
    31. if (y < 0) {
    32. y = 0;
    33. } else if (y > maxY) {
    34. y = maxY;
    35. }
    36. el.style.left = x + "px";
    37. el.style.top = y + "px";
    38. };
    39. document.onmouseup = function () {
    40. document.onmousemove = document.onmouseup = null;
    41. };
    42. };
    43. }
    44. };
    45. export default draggable;

    6.水印 v-waterMarker

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

    7.按钮权限 v-auth

    1. /**
    2. * v-auth
    3. * 按钮权限指令 (根据需求而定)
    4. */
    5. import { useAuthStore } from "@/stores/modules/auth";
    6. import type { Directive, DirectiveBinding } from "vue";
    7. const auth: Directive = {
    8. mounted(el: HTMLElement, binding: DirectiveBinding) {
    9. const { value } = binding;
    10. const authStore = useAuthStore();
    11. const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? [];
    12. if (value instanceof Array && value.length) {
    13. const hasPermission = value.every(item => currentPageRoles.includes(item));
    14. if (!hasPermission) el.remove();
    15. } else {
    16. if (!currentPageRoles.includes(value)) el.remove();
    17. }
    18. }
    19. };
    20. export default auth;

    8.旋转 v-rotate

    1. // 自定义指令,点击旋转 v-rotate
    2. const rotate = {
    3. beforeMount(el: any) {
    4. el.addEventListener("click", function () {
    5. console.log(el.style.transform);
    6. el.style.transition = "all 0.3s";
    7. if (el.style.transform) {
    8. let str = el.style.transform;
    9. let deg = str.substring(str.indexOf("(") + 1, str.indexOf("d"));
    10. el.style.transform = `rotate(${Number(deg) + 180}deg)`;
    11. } else {
    12. el.style.transform = "rotate(180deg)";
    13. }
    14. });
    15. }
    16. };
    17. export default rotate;

  • 相关阅读:
    探索Java异常处理的奥秘:源码解析与高级实践
    vscode 如何配置快速生成 vue3 模板
    SpringBoot框架:闲一品交易的新引擎
    git学习入门8——git-revert
    利用Python+阿里云实现DDNS(动态域名解析)
    J9数字论:模块化公链能否成为公链新趋势?
    rust闭包
    JavaScript:实现 jugglerSequence杂耍者序列算法 (附完整源码)
    StarRocks国产数据湖仓技术学习记录
    电商前台项目(三):完成Search搜索模块业务
  • 原文地址:https://blog.csdn.net/weixin_54682740/article/details/134549977