vue2企业级项目(六)

文章详细介绍了在Vue2企业级项目中创建和使用自定义指令,包括v-draggable实现拖拽功能,v-click-outside检测点击外部事件,v-ripple实现涟漪效果,v-intersect使用交叉观察者,v-scroll监听滚动,v-scroll-to滚动设置以及v-auto-scroll自动滚动。这些指令增强了组件交互性和用户体验。
摘要由CSDN通过智能技术生成

vue2企业级项目(六)

自定义指令

  1. 创建src/directive/index.js

    const directives = require.context("./modules", true, /\.js$/);
    
    export default {
      install: (Vue) => {
        directives.keys().forEach((key) => {
          let directive = directives(key).default;
          let name = key.replace(/^\.\/(.*)\.\w+$/, "$1");
          directive && Vue.directive(name, directive);
        });
      },
    };
    
    
  2. main.js注入使用

    import directives from "./directive";
    
    ...
    
    Vue.use(directives);
    
  3. 创建src/directive/modules目录

1、v-loading

element-ui有,就不需要

2、v-draggable

创建src/directive/draggable.js

import { checkDataType } from "@/utils/utils";
let recoverStyle = null;

export default {
  update(el, binding) {
    let config = binding.value;
    let visible = false;
    let target = ".el-dialog__header";
    let control = ".el-dialog";
    if (checkDataType(config, "object")) {
      visible = config.visible;
      target = config.target;
      control = config.control;
    } else {
      visible = config;
    }

    const dialogHeaderEl = el.querySelector(target);
    const dragDom = el.querySelector(control);
    dialogHeaderEl.style.cursor = "move";

    function readStyle(dom, attr) {
      if (document.body.currentStyle) return dom.currentStyle[attr];
      return getComputedStyle(dom, false)[attr];
    }

    if (visible) {
      recoverStyle = {
        left: readStyle(dragDom, "left"),
        top: readStyle(dragDom, "top"),
        margin: readStyle(dragDom, "margin"),
      };

      dialogHeaderEl.onmousedown = function(e) {
        // 获取鼠标相对当前元素内部位置
        const disX = e.clientX - dragDom.offsetLeft;
        const disY = e.clientY - dragDom.offsetTop;

        // 获取当前窗口视图大小
        const screenWidth = document.body.clientWidth;
        const screenHeight = document.documentElement.clientHeight;

        // 获取当前元素大小
        const dragDomWidth = dragDom.offsetWidth;
        const dragDomheight = dragDom.offsetHeight;

        // 计算获取最大偏移量
        const maxLeft = screenWidth - dragDomWidth;
        const maxTop = screenHeight - dragDomheight;

        document.onmousemove = function(e) {
          let left = e.clientX - disX;
          let top = e.clientY - disY;

          // 碰撞判断
          if (left <= 0) left = 0;
          if (left >= maxLeft) left = maxLeft;

          if (top <= 0) top = 0;
          if (top >= maxTop) top = maxTop;

          left = ((left * 100) / screenWidth).toFixed(2) + "%";
          top = ((top * 100) / screenHeight).toFixed(2) + "%";

          dragDom.style.left = left;
          dragDom.style.top = top;
          dragDom.style.margin = 0;
        };

        document.onmouseup = function() {
          document.onmousemove = null;
          document.onmouseup = null;
        };
      };
    } else {
      // 还原初始化的样式
      dragDom.style.left = recoverStyle.left;
      dragDom.style.top = recoverStyle.top;
      dragDom.style.margin = recoverStyle.margin;
      dialogHeaderEl.onmousedown = null;
      recoverStyle = null;
    }
  },
};

3、v-click-outside

创建src/directive/click-outside.js

export default {
  bind(el, binding) {
    // 点击事件回调函数
    const onClickOutside = (event) => {
      if (!(el === event.target || el.contains(event.target))) {
        binding.value(event);
      }
    };

    // 将回调函数绑定到元素上
    el._clickOutsideCallback = onClickOutside;

    // 添加点击事件监听器
    document.addEventListener("click", onClickOutside);
  },
  unbind(el) {
    // 移除点击事件监听器
    document.removeEventListener("click", el._clickOutsideCallback);
    delete el._clickOutsideCallback;
  },
};

4、v-ripple(简单的)

创建src/directive/ripple.js

function createRipple(el, radius, color, disX, disY) {
  new Promise((resolve) => {
    const rippleDom = document.createElement("span");
    rippleDom.className = "ripple";
    rippleDom.setAttribute(
      "style",
      `
        display: block;
        position: absolute;
        border-radius: 50%;
        transform: translate(-50%, -50%) scale(0);
        transition: 0.5s;
        background-color: ${color || "rgba(0, 0, 0, 0.6)"};
        top: ${disY}px;
        left: ${disX}px;
        width: ${radius * 3}px;
        height: ${radius * 3}px;
        animation: ripple-animation 0.5s linear;
      `,
    );
    el.appendChild(rippleDom);
    let timer = setTimeout(() => {
      el.removeChild(rippleDom);
      resolve(timer);
    }, 520);
  }).then((timer) => {
    clearTimeout(timer);
  });
}

function rippleShow(e) {
  const targetDom = e.target;

  const radius =
    targetDom.offsetHeight > targetDom.offsetWidth
      ? targetDom.offsetHeight
      : targetDom.offsetWidth;
  const disX = e.clientX - targetDom.offsetLeft;
  const disY = e.clientY - targetDom.offsetTop;

  createRipple(targetDom, radius, e?._ripple, disX, disY);
}

export default {
  bind(el, binding) {
    el._ripple = binding.value;
    el.style.position = "relative";
    el.style.overflow = "hidden";
    el.style.userSelect = "none";

    el.addEventListener("mousedown", rippleShow);
  },
  unbind(el) {
    el._ripple = null;
    el.removeEventListener("mousedown", rippleShow);
  },
};

创建src/styles/animation.less,并在scr/styles/index.less内引入

// ripple动画
@keyframes ripple-animation {
  to {
    transform: translate(-50%, -50%) scale(1);
    opacity: 0;
  }
}

5、v-intersect

交叉观察者

创建src/directive/intersect.js

export default {
  bind(el, binding) {
    let info = null;
    let callback = null;
    if (typeof binding.value === "object") {
      info = binding.value?.info;
      callback = binding.value?.callback;
    } else {
      callback = binding.value;
    }
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            callback && callback(info);
          }
        });
      },
      { root: el.parentElement },
    );
    observer.observe(el);
  },
};

6、v-scroll

滚动监听

创建src/directive/scroll.js

export default {
  bind(el, binding) {
    el.onscroll = function() {
      binding.value({
        x: el.scrollLeft,
        y: el.scrollTop,
      });
    };
  },
  unbind(el) {
    el.onscroll = null;
  },
};

7、v-scroll-to

滚动设置

创建src/directive/scroll-to.js

export default {
  update(el, binding) {
    let x = binding.modifiers?.x || false;
    if (binding.value && binding.value === binding.oldValue) return;

    if (typeof binding.value === "number") {
      x ? (el.scrollLeft = binding.value) : (el.scrollTop = binding.value);
    } else if (typeof binding.value === "string") {
      let targetDom = document.querySelector(binding.value);

      if (x) {
        let border = getComputedStyle(el, false)["borderLeftWidth"];
        let padding = getComputedStyle(el, false)["paddingLeft"];
        border = border.replace("px", "");
        padding = padding.replace("px", "");
        el.scrollLeft = targetDom.offsetLeft - el.offsetLeft - border - padding;
      } else {
        let border = getComputedStyle(el, false)["borderTopWidth"];
        let padding = getComputedStyle(el, false)["paddingTop"];
        border = border.replace("px", "");
        padding = padding.replace("px", "");
        el.scrollTop = targetDom.offsetTop - el.offsetTop - border - padding;
      }
    }
  },
};

8、v-auto-scroll

自动滚动

创建src/directive/scroll-auto.js

function active(el, scroll, step, max, x) {
  window.requestAnimationFrame(() => {
    x ? (el.scrollLeft = scroll) : (el.scrollTop = scroll);
    scroll += step;
    if (scroll >= max) scroll = 0 + step;
    active(el, scroll, step, max, x);
  });
}

export default {
  inserted(el, binding) {
    const x = binding.modifiers?.x || false;
    let step = 0;
    let scroll = 0;
    if (typeof binding.value === "object") {
      step = binding.value?.step || 5;
      scroll = binding.value?.begin || 0;
    } else {
      step = binding.value || 5;
    }
    const scrollMax = x ? el.scrollWidth : el.scrollHeight;
    el._children = [].slice.call(el.children);
    el._children.forEach((child) => {
      el.appendChild(child.cloneNode(true));
    });

    active(el, scroll, step, scrollMax, x);
  },
  unbind(el) {
    el.innerHtml = "";
    let children = [].slice.call(el.children);
    children.forEach((child) => {
      el.removeChild(child);
    });

    el._children.forEach((child) => {
      el.appendChild(child.cloneNode(true));
    });
  },
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值