vue拖动缩放组件(vue-drag-zoom) 增加禁止缩放拖拽功能

前段时间写了一个类似于百度ICOR,可拖拽缩放图片并在图片上框选文字的功能,这里的拖拽缩放功能就用到了vue-drag-zoom组件,组件是从npm下载的VUE2代码,放在VUE3+vite项目里面也是可以兼容的,但是组件仅提供了禁止缩放的API且存在一定问题

附npm地址:vue-drag-zoom - npm该组件适用于对一个元素在某个区域内进行拖动/缩放. Latest version: 1.0.9, last published: 4 months ago. Start using vue-drag-zoom in your project by running `npm i vue-drag-zoom`. There are no other projects in the npm registry using vue-drag-zoom.https://www.npmjs.com/package/vue-drag-zoom
以下为改造后源码:
1.新增禁止拖拽API,与禁止缩放API整合
2.解决了缩放后图片不固定在中心点问题
3.解决了频繁禁止与恢复拖拽缩放时,图片回到初始位置的问题

<template>
  <div ref="xx-drag-zoom" class="xx-drag-zoom" :style="dragZoomNodeStyle">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "xx-drag-zoom",
  components: {},
  props: {
    /* 被操作的元素 start */
    // X 坐标
    left: {
      type: Number,
      default: 0,
    },
    // Y 坐标
    top: {
      type: Number,
      default: 0,
    },
    // 宽度
    width: Number,
    // 高度
    height: Number,
    // 允许缩放
    allowZoom: Boolean,
    // 缩放比例
    zoom: {
      type: Number,
      default: 1,
    },
    // 最大缩放比例
    maxZoom: {
      type: Number,
      default: 2,
    },
    // 最小缩放比例
    minZoom: {
      type: Number,
      default: 0.5,
    },
    // 缩放幅度
    range: {
      type: Number,
      default: 0.1,
    },
    /* 被操作的元素 end */

    /* 活动区域 start */
    // 节点 (注: 传入节点后可以自动获取数据, 不需要再传坐标与宽高了)
    areaNode: HTMLDivElement,
    // X 坐标 (未设置 areaNode 时生效)
    areaLeft: {
      type: Number,
      default: 0,
    },
    // Y 坐标 (未设置 areaNode 时生效)
    areaTop: {
      type: Number,
      default: 0,
    },
    // 宽度 (未设置 areaNode 时生效)
    areaWidth: {
      type: Number,
      default: 200,
    },
    // 高度 (未设置 areaNode 时生效)
    areaHeight: {
      type: Number,
      default: 100,
    },
    /* 活动区域 end */
  },
  data() {
    return {
      currentZoom: this.zoom,
      initLeft: this.left,
      initTop: this.top,
      lastPosition: {},
    };
  },
  computed: {
    // 被操作的元素节点
    dragZoomNode() {
      return this.$refs["xx-drag-zoom"];
    },
    // 活动区域数据
    areaNodeData() {
      let obj = {};
      //   计算属性无法监听活动区域的宽高变化
      const node = this.areaNode;
      if (node) {
        obj = {
          left: node.clientLeft, //活动区域定位 ? 左边框宽度
          top: node.clientTop,
          width: node.offsetWidth, // 活动区域宽高
          height: node.offsetHeight,
        };
      } else {
        obj = {
          left: this.areaLeft,
          top: this.areaTop,
          width: this.areaWidth,
          height: this.areaHeight,
        };
      }
      return obj;
    },
    // 设置样式
    dragZoomNodeStyle() {
      return {
        transform: `scale(${this.currentZoom})`,
        left: `${this.initLeft}px`,
        top: `${this.initTop}px`,
        width: this.width + "px",
        height: this.height + "px",
        cursor: this.allowZoom ? "move" : "default",
      };
    },
  },
  watch: {
    zoom(val) {
      this.currentZoom = val;
      this.initStyle(val);
    },
  },
  created() {},
  mounted() {
    this.dragZoomNode.addEventListener("mousedown", this.mousedown);
    this.dragZoomNode.addEventListener("wheel", this.mousescroll);
    this.initStyle("mounted");
  },
  beforeDestroy() {
    this.dragZoomNode.removeEventListener("mousedown", null);
    this.dragZoomNode.removeEventListener("wheel", null);
  },
  methods: {
    // 鼠标点击事件
    mousedown(evt) {
      const areaW = this.areaNode
        ? this.areaNode.offsetWidth
        : this.areaNodeData.width;
      const areaH = this.areaNode
        ? this.areaNode.offsetHeight
        : this.areaNodeData.height;
      const {
        offsetLeft: dragL,
        offsetTop: dragT,
        offsetWidth: dragW,
        offsetHeight: dragH,
      } = this.dragZoomNode; //缩放内容的宽高与到活动区域的宽高距离
      const x = evt.clientX - dragL; //鼠标相对于图片的位置
      const y = evt.clientY - dragT;

      // 鼠标拖动事件
      document.onmousemove = (evt) => {
        const zoom = this.currentZoom; //缩放比
        // 不允许拖动
        if (!this.allowZoom) {
          return;
        }
        let styleL = evt.clientX - x;
        let styleT = evt.clientY - y;
        // 当拖动元素宽度小于父元素时
        if (dragW * zoom < areaW) {
          // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
          const boundaryL = (dragW * zoom - dragW) / 2;
          const boundaryR = areaW - (dragW + boundaryL);

          // 左边界
          if (styleL < boundaryL) {
            styleL = boundaryL;
          }
          // 右边界
          if (styleL > boundaryR) {
            styleL = boundaryR;
          }
        } else {
          // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
          const boundaryL = (dragW * zoom - dragW) / 2;
          const boundaryR = -(dragW * zoom - areaW - boundaryL);

          // 左边界
          if (styleL > boundaryL) {
            styleL = boundaryL;
          }
          // 右边界
          if (styleL < boundaryR) {
            styleL = boundaryR;
          }
        }

        // 当拖动元素高度小于父元素时
        if (dragH * zoom < areaH) {
          // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
          const boundaryT = (dragH * zoom - dragH) / 2;
          const boundaryB = areaH - (dragH + boundaryT);

          // 上边界
          if (styleT < boundaryT) {
            styleT = boundaryT;
          }
          // 下边界
          if (styleT > boundaryB) {
            styleT = boundaryB;
          }
        } else {
          // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
          const boundaryT = (dragH * zoom - dragH) / 2;
          const boundaryB = -(dragH * zoom - areaH - boundaryT);

          // 上边界
          if (styleT > boundaryT) {
            styleT = boundaryT;
          }
          // 下边界
          if (styleT < boundaryB) {
            styleT = boundaryB;
          }
        }

        this.dragZoomNode.style.left = styleL + "px";
        this.dragZoomNode.style.top = styleT + "px";
        this.lastPosition.left = styleL;
        this.lastPosition.top = styleT;
        this.$emit("mousemove", evt);
      };
      document.onmouseup = () => {
        document.onmousemove = null;
      };
    },

    // 鼠标滚轮事件
    mousescroll(evt) {
      // 阻止默认行为
      if (evt.preventDefault) {
        evt.preventDefault();
      } else {
        evt.returnValue = false;
      }

      const { deltaY } = evt;
      const {
        left: areaL,
        top: areaT,
        // width: areaW,
        // height: areaH,
      } = this.areaNodeData;
      const areaW = this.areaNode
        ? this.areaNode.offsetWidth
        : this.areaNodeData.width;
      const areaH = this.areaNode
        ? this.areaNode.offsetHeight
        : this.areaNodeData.height;
      const {
        offsetLeft: dragL,
        offsetTop: dragT,
        offsetWidth: dragW,
        offsetHeight: dragH,
      } = this.dragZoomNode;
      let zoom = this.currentZoom;

      // 不允许缩放
      if (!this.allowZoom) {
        return;
      }

      // 上滑
      if (deltaY < 0) {
        if (zoom >= this.maxZoom) {
          return;
        }

        zoom += this.range;
      } else {
        if (zoom <= this.minZoom) {
          return;
        }

        zoom -= this.range;
      }

      this.currentZoom = Number(zoom.toFixed(1));

      /* 边界判定 */
      const subtractW = (dragW * this.currentZoom - dragW) / 2;
      const subtractH = (dragH * this.currentZoom - dragH) / 2;
      const currentL = dragL - subtractW;
      const currentT = dragT - subtractW;
      const currentR = dragL + dragW + subtractW;
      const currentB = dragT + dragH + subtractH;

      // 当拖动元素宽度小于父元素时
      if (dragW * zoom < areaW) {
        // 左边界判定
        if (currentL < areaL) {
          this.dragZoomNode.style.left = areaL + subtractW + "px";
        }
        // 右边界判定
        if (currentR > areaW) {
          this.dragZoomNode.style.left = areaW - dragW - subtractW + "px";
        }
      } else {
        // 左边界判定
        if (currentL > areaL) {
          this.dragZoomNode.style.left = areaL + subtractW + "px";
        }
        // 右边界判定
        if (currentR < areaW) {
          this.dragZoomNode.style.left = areaW - dragW - subtractW + "px";
        }
      }
      // 当拖动元素高度小于父元素时
      if (dragH * zoom < areaH) {
        // 上边界判定
        if (currentT < areaT) {
          this.dragZoomNode.style.top = areaT + subtractH + "px";
        }
        // 下边界判定
        if (currentB > areaH) {
          this.dragZoomNode.style.top = areaH - dragH - subtractH + "px";
        }
      } else {
        // 上边界判定
        if (currentT > areaT) {
          this.dragZoomNode.style.top = areaT + subtractH + "px";
        }
        // 下边界判定
        if (currentB < areaH) {
          this.dragZoomNode.style.top = areaH - dragH - subtractH + "px";
        }
      }

      this.$emit("mousescroll", evt);
    },

    // 样式初始化
    initStyle(title) {
      let tmpLeft = this.left;
      let tmpTop = this.top;
      const { offsetWidth: dragW, offsetHeight: dragH } = this.dragZoomNode;
      tmpLeft = this.left - (dragW * (1 - this.zoom)) / 2;
      tmpTop = this.left - (dragH * (1 - this.zoom)) / 2;
      this.initLeft = tmpLeft || this.lastPosition.left;
      this.initTop = tmpTop || this.lastPosition.top;
    },
  },
};
</script>

<style scoped>
.xx-drag-zoom {
  position: absolute;
  user-select: none;
}
</style>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值