记录 Vue 中 封装边框拖拽 指令-前端文章论坛-前端交流-技术鸭(jishuya.cn)

记录 Vue 中 封装边框拖拽 指令

const dragSize = {
  install(Vue) {
    Vue.directive("dragSize", {
      // Vue2 inserted
      beforeMount(el, binding) {
        const state = {
          element: el,
          allowedDirections: {
            top: false,
            bottom: false,
            left: false,
            right: false,
            topLeft: false,
            topRight: false,
            bottomLeft: false,
            bottomRight: false,
          },
          dragWidth: 3, // 拖拽条的 宽度或者高度
          class: "dragSize", // 拖拽条的class
          touch: { lastTime: 0, interval: 300, startX: 0, startY: 0 },
          moveX: 0,
          moveY: 0,
          mousemove_: null,
          mouseup_: null,
        };
        // onStart 开始前
        // onMove 拖住时
        // onEnd 拖拽结束时
        // onCustomSize 自定义调整大小
        const { onStart, onMove, onEnd, onCustomSize, all, ...directions } = binding.value || {};
        const isNumber = (value) => Object.prototype.toString.call(value) === "[object Number]" && !isNaN(value);
        ["minWidth", "maxWidth", "minHeight", "maxHeight"].forEach((property) => (isNumber(binding.value[property]) ? (el.style[property] = `${binding.value[property]}px`) : (el.style[property] = binding.value[property])));
        state.allowedDirections = ["top", "bottom", "left", "right", "topLeft", "topRight", "bottomLeft", "bottomRight"].reduce((acc, key) => {
          acc[key] = !!all || !!directions[key];
          return acc;
        }, {});
        state.class = directions?.class ? state.class + " " + directions?.class : state?.class;
        state.dragWidth = isNumber(directions?.dragWidth) ? directions?.dragWidth : 3;
        el.style.position = "relative";
        let isOnMove = false;
        state.mousemove_ = (e) => {
          if (!state.touch.init) return;
          state.moveX = e.pageX - state.touch.startX;
          state.moveY = e.pageY - state.touch.startY;
          if (onMove && typeof onMove === "function" && !isOnMove) {
            onMove(el, e);
            isOnMove = true;
          }

          if (["right", "left", "topRight", "bottomRight", "topLeft", "bottomLeft"].includes(state.touch.type)) {
            const newWidth = state.touch.width + (state.touch.type.includes("left") ? -state.moveX : state.moveX);
            if (onCustomSize && typeof onCustomSize === "function") {
              onCustomSize(el, e, { width: newWidth });
            } else {
              el.style.width = `${newWidth}px`;
            }
          }

          if (["bottom", "top", "topRight", "bottomRight", "topLeft", "bottomLeft"].includes(state.touch.type)) {
            const newHeight = state.touch.height + (state.touch.type.includes("top") ? -state.moveY : state.moveY);
            if (onCustomSize && typeof onCustomSize === "function") {
              onCustomSize(el, e, { height: newHeight });
            } else {
              el.style.height = `${newHeight}px`;
            }
          }
        };

        state.mouseup_ = (e) => {
          document.body.style.cursor = "default";
          if (!state.touch.init) return;
          if (onEnd && typeof onEnd === "function") {
            onEnd(el, e);
          }
          isOnMove = false;
          state.touch.init = false;
          document.removeEventListener("mousemove", state.mousemove_);
          document.removeEventListener("mouseup", state.mouseup_);
        };

        const addHandle = (cursor, type, styles = {}, enabled) => {
          if (!enabled) return;
          const dom = document.createElement("DIV");
          Object.assign(dom.style, {
            position: "absolute",
            zIndex: "10",
            cursor: cursor,
            userSelect: "none",
            touchAction: "none",
          });
          if (state.class) {
            const classNames = state.class.split(" ").filter((className) => className.trim());
            dom.classList.add(...classNames);
          }
          Object.assign(dom.style, styles);
          dom.addEventListener("mousedown", (e) => {
            const now = +new Date();
            if (now - state.touch.lastTime < state.touch.interval) {
              return (state.touch.init = false);
            }
            state.touch.init = true;
            state.touch.startX = e.pageX;
            state.touch.startY = e.pageY;

            const computedStyle = window.getComputedStyle(el);
            if (computedStyle.position === "relative" || computedStyle.position === "absolute") {
              if (computedStyle.left === "auto" || computedStyle.left === "") {
                el.style.left = `${el.offsetLeft}px`;
              }
              if (computedStyle.top === "auto" || computedStyle.top === "") {
                el.style.top = `${el.offsetTop}px`;
              }
            }
            document.body.style.cursor = cursor;
            const rect = el.getBoundingClientRect();
            Object.assign(state.touch, {
              width: rect.width,
              height: rect.height,
              left: parseFloat(el.style.left) || 0,
              top: parseFloat(el.style.top) || 0,
              type: type,
            });

            document.addEventListener("mousemove", state.mousemove_, { passive: false });
            document.addEventListener("mouseup", state.mouseup_);
          });

          el.appendChild(dom);
        };
        if (onStart && typeof onStart === "function") {
          onStart(el);
        }
        [
          ["ns-resize", "top", { top: "0", left: "0", right: "0", height: state.dragWidth + "px" }, state.allowedDirections.top],
          ["ns-resize", "bottom", { bottom: "0", left: "0", right: "0", height: state.dragWidth + "px" }, state.allowedDirections.bottom],
          ["ew-resize", "left", { top: "0", bottom: "0", left: "0", width: state.dragWidth + "px" }, state.allowedDirections.left],
          ["ew-resize", "right", { top: "0", bottom: "0", right: "0", width: state.dragWidth + "px" }, state.allowedDirections.right],
          ["nwse-resize", "topLeft", { top: "0", left: "0", width: "10px", height: "10px" }, state.allowedDirections.topLeft],
          ["nesw-resize", "topRight", { top: "0", right: "0", width: "10px", height: "10px" }, state.allowedDirections.topRight],
          ["nesw-resize", "bottomLeft", { bottom: "0", left: "0", width: "10px", height: "10px" }, state.allowedDirections.bottomLeft],
          ["nwse-resize", "bottomRight", { bottom: "0", right: "0", width: "10px", height: "10px" }, state.allowedDirections.bottomRight],
        ].forEach((item) => addHandle(...item));

        // 保存状态
        el.__dragState__ = state;
      },
      // Vue2 unbind
      unmounted(el) {
        const state = el.__dragState__;
        if (state) {
          document.removeEventListener("mousemove", state.mousemove_);
          document.removeEventListener("mouseup", state.mouseup_);
        }
        delete el.__dragState__;
      },
    });
  },
};
export default dragSize;

 

请登录后发表评论

    请登录后查看回复内容