vue图片放大缩小拖拽,缩略图相应展示页面视口图片内容

cavas实现,大致😒写了一半,主要问题就是模糊需要处理,后边没写了,有大神改改的话请@我抄抄,在此谢过了🙏

实现的也不是很好,比较垃圾。。。

注意:
(1)代码mark框初始状态应该是框起来一整个小图,我没改
(2)每次拖拽大图需要重新绘制相应小图的内容到大图展示上,每次canvas重新绘制会模糊,这块没写
(3)小图展示的是1080*1920等比10倍缩小的,根据实际情况可以改为视口等比缩小
(4)大图和小图模糊处理可以通过以下代码处理

img.onload = function () {
        // 1:计算大图的展示比例“
        // 保持图像的原始尺寸
        const imgWidth = img.width;
        const imgHeight = img.height;

        // 设置画布尺寸为图像尺寸
        canvasBig.width = imgWidth;
        canvasBig.height = imgHeight;

        // 绘制图像
        ctxBig.drawImage(img, 0, 0, imgWidth, imgHeight);
        // 2:计算小图的展示比例
        // 计算等比例缩放
        const canvasWidth = canvasSmall.width;
        const canvasHeight = canvasSmall.height;
        let drawWidth, drawHeight;

        if (imgWidth / canvasWidth > imgHeight / canvasHeight) {
          drawWidth = canvasWidth;
          drawHeight = (imgHeight / imgWidth) * canvasWidth;
        } else {
          drawHeight = canvasHeight;
          drawWidth = (imgWidth / imgHeight) * canvasHeight;
        }

        // 计算居中位置
        const offsetX = (canvasWidth - drawWidth) / 2;
        const offsetY = (canvasHeight - drawHeight) / 2;
        // 绘制图片
        ctxSmall.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
      };

showImg.vue代码,就一个文件

<template>
  <div class="image-viewer">
    <div class="big">
      <canvas class="canvas-big" id="canvas-big">
        <!--<img src="@/assets/showImg.jpg" alt="" />-->
      </canvas>
    </div>
    <div class="small">
      <div class="mask" id="mask" draggable="true"></div>
      <canvas class="canvas-small" id="canvas-small"> </canvas>
    </div>
  </div>
</template>
  
  <script>
import { _debounce, _throttle } from "../util/throttle";
export default {
  data() {
    return {
      imageSrc: "showImg.jpg",
      initialMaskSize: { width: 96, height: 54 }, // 初始 mask 大小
      maskSize: 54, // 初始 mask 大小
      aspectRatio: 96 / 54, // 计算初始纵横比
    };
  },
  mounted() {
    let _this = this;
    window.onload = function () {
      // 获取 canvas 元素
      //==mask方框拖拽事件===
      const canvasBig = document.getElementById("canvas-big");
      const canvasSmall = document.getElementById("canvas-small");
      const ctxBig = canvasBig.getContext("2d");
      const ctxSmall = canvasSmall.getContext("2d");

      // 创建一个新的图像对象
      const img = new Image();

      // 图像加载完成后绘制图像
      img.onload = function () {
        ctxBig.drawImage(img, 0, 0, canvasBig.width, canvasBig.height);
        ctxSmall.drawImage(img, 0, 0, canvasSmall.width, canvasSmall.height);
      };

      // 设置图像源(可以是本地路径或远程 URL)
      img.src = require("@/assets/showImg.jpg");

      const mask = document.getElementById("mask");
      const container = document.querySelector(".small");
      const containerRect = container.getBoundingClientRect();
      const maskRect = mask.getBoundingClientRect();
      let isDragging = false;
      let offsetX, offsetY;

      // 计算 Canvas 的位置
      function getCanvasPosition() {
        const rect = canvasSmall.getBoundingClientRect();
        return {
          x: rect.left - containerRect.left,
          y: rect.top - containerRect.top,
        };
      }
      // 处理拖拽开始事件
      mask.addEventListener("dragstart", (event) => {
        event.dataTransfer.setData("text/plain", ""); // 需要设置 dataTransfer 对象
        isDragging = true;
        const rect = mask.getBoundingClientRect();
        offsetX = event.clientX - rect.left;
        offsetY = event.clientY - rect.top;
      });

      // 处理拖拽结束事件
      mask.addEventListener("dragend", (event) => {
        isDragging = false;
        const canvasPos = getCanvasPosition();
        let x = event.clientX - containerRect.left - offsetX;
        let y = event.clientY - containerRect.top - offsetY;

        // 限制拖拽范围在 container 内部
        x = Math.max(0, Math.min(x, containerRect.width - maskRect.width));
        y = Math.max(0, Math.min(y, containerRect.height - maskRect.height));

        mask.style.left = `${x}px`;
        mask.style.top = `${y}px`;

        // 更新 canvas-big 上的图像显示区域
        _this.updateBigCanvasView();
      });

      // 处理拖拽移动事件
      document.addEventListener("dragover", (event) => {
        if (isDragging) {
          event.preventDefault(); // 必须要阻止默认行为

          // const x = event.clientX - containerRect.left - offsetX;
          // const y = event.clientY - containerRect.top - offsetY;

          // // 限制拖拽范围在 container 内部
          // const constrainedX = Math.max(
          //   0,
          //   Math.min(x, containerRect.width - maskRect.width)
          // );
          // const constrainedY = Math.max(
          //   0,
          //   Math.min(y, containerRect.height - maskRect.height)
          // );

          // mask.style.left = `${constrainedX}px`;
          // mask.style.top = `${constrainedY}px`;

          // // 更新 canvas-big 上的图像显示区域
          // _this.updateBigCanvasView();
        }
      });

      document.addEventListener("drop", (event) => {
        if (isDragging) {
          event.preventDefault();
          const x = event.clientX - containerRect.left - offsetX;
          const y = event.clientY - containerRect.top - offsetY;

          // 限制拖拽范围在 container 内部
          const constrainedX = Math.max(
            0,
            Math.min(x, containerRect.width - maskRect.width)
          );
          const constrainedY = Math.max(
            0,
            Math.min(y, containerRect.height - maskRect.height)
          );

          mask.style.left = `${constrainedX}px`;
          mask.style.top = `${constrainedY}px`;

          // 更新 canvas-big 上的图像显示区域
          _this.updateBigCanvasView();
        }
      });
      //====

      // 处理 canvas-big 的拖拽事件====
      let isDraggingTest = false;
      let offsetXTest, offsetYTest;
      let offsetXBig = 0,
        offsetYBig = 0;
      canvasBig.addEventListener("mousedown", (event) => {
        isDraggingTest = true;
        offsetXTest = event.clientX - offsetXBig;
        offsetYTest = event.clientY - offsetYBig;
      });

      canvasBig.addEventListener(
        "mousemove",
        _throttle((event) => {
          if (isDraggingTest) {
            const x = event.clientX - offsetXBig - offsetXTest;
            const y = event.clientY - offsetYBig - offsetYTest;

            // 更新 mark 的位置
            // maskRect.left + x / 10 / 2
            console.log(mask.style.left, mask.style.top);
            const matchLeft = mask.style.left
              ? mask.style.left.match(/-?\d+(\.\d+)?/)
              : [0];
            const matchTop = mask.style.top
              ? mask.style.top.match(/-?\d+(\.\d+)?/)
              : [0];

            mask.style.left = `${parseFloat(matchLeft[0], 10) + x / 10 / 2}px`;
            mask.style.top = `${parseFloat(matchTop[0], 10) + y / 10 / 2}px`;
            offsetXBig = parseFloat(matchLeft[0], 10) || 0;
            offsetYBig = parseFloat(matchTop[0], 10) || 0;

            _this.updateBigCanvasView();
          }
        }, 100)
      );

      canvasBig.addEventListener("mouseup", () => {
        isDraggingTest = false;
      });
      //====
    };

    //ctrl+滚轮控制mask方框放大缩小======
    const small = document.querySelector(".small");

    // 绑定事件处理函数到组件实例上
    this.resizeMask = (event) => {
      if (!event.ctrlKey) return;
      event.preventDefault();

      const delta = event.deltaY < 0 ? 10 : -10;
      // 计算新的大小
      let newSize = Math.max(
        10,
        Math.min(
          this.maskSize + delta,
          Math.min(small.clientWidth, small.clientHeight)
        )
      );

      // 保持纵横比
      let newWidth, newHeight;
      if (small.clientWidth / small.clientHeight > this.aspectRatio) {
        newWidth = newSize;
        newHeight = newSize / this.aspectRatio;
      } else {
        newHeight = newSize;
        newWidth = newSize * this.aspectRatio;
      }

      // 确保新宽高不会超出容器的范围
      newWidth = Math.min(newWidth, small.clientWidth);
      newHeight = Math.min(newHeight, small.clientHeight);

      this.maskSize = newSize;
      this.updateMaskSize(newWidth, newHeight);
      this.updateBigCanvasView();
    };

    this.updateMaskSize(
      this.initialMaskSize.width,
      this.initialMaskSize.height
    );

    document.addEventListener("wheel", this.resizeMask, { passive: false });

    window.addEventListener("resize", () => {
      const newRect = small.getBoundingClientRect();
      // 更新小方块的容器尺寸
      // (如果需要,可以在这里添加额外逻辑)
    });
    // Initialize and load images into canvases
    this.loadImageToCanvas(this.imageSrc);
  },
  methods: {
    updateMaskSize(width, height) {
      const mask = document.getElementById("mask");
      mask.style.width = width + "px";
      mask.style.height = height + "px";
    },

    updateBigCanvasView() {
      const mask = document.getElementById("mask");
      const small = document.querySelector(".small");

      const canvasBig = document.getElementById("canvas-big");
      const canvasSmall = document.getElementById("canvas-small");

      // Calculate the scale factor and position
      const scaleX = canvasBig.width / canvasSmall.width;
      const scaleY = canvasBig.height / canvasSmall.height;

      // Calculate the position and size in canvas-big coordinates
      const maskRect = mask.getBoundingClientRect();
      const smallRect = small.getBoundingClientRect();
      const maskX = (maskRect.left - smallRect.left) / smallRect.width;
      const maskY = (maskRect.top - smallRect.top) / smallRect.height;
      const maskWidth = maskRect.width / smallRect.width;
      const maskHeight = maskRect.height / smallRect.height;

      const ctxBig = canvasBig.getContext("2d");

      // Set the viewport on the big canvas
      ctxBig.clearRect(0, 0, canvasBig.width, canvasBig.height);
      ctxBig.drawImage(
        canvasSmall,
        maskX * canvasSmall.width,
        maskY * canvasSmall.height,
        maskWidth * canvasSmall.width,
        maskHeight * canvasSmall.height,
        0,
        0,
        canvasBig.width,
        canvasBig.height
      );
    },
    loadImageToCanvas(src) {
      const canvasBig = document.getElementById("canvas-big");
      const canvasSmall = document.getElementById("canvas-small");
      const image = new Image();
      image.src = src;
      image.onload = () => {
        canvasSmall.width = image.width;
        canvasSmall.height = image.height;
        this.ctxSmall.drawImage(image, 0, 0);

        canvasBig.width = canvasSmall.width;
        canvasBig.height = canvasSmall.height;
        this.updateBigCanvasView();
      };
    },
  },
  beforeDestroy() {
    document.removeEventListener("wheel", this.resizeMask);
  },
};
</script>
  
<style scoped lang="less">
///
.image-viewer {
  position: relative;
  height: 100%;
  width: 100%;
  .big {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    .canvas-big {
      height: 100%;
      width: 100%;
      object-fit: contain;
    }
  }
  .small {
    position: absolute;
    bottom: 10px;
    right: 10px;
    height: 108px;
    width: 192px;
    border: 1px solid #ccc;
    overflow: hidden;
    .mask {
      z-index: 33;
      position: absolute;
      top: 0;
      left: 0;
      height: 54px;
      width: 96px;
    }

    .mask::before {
      content: "";
      position: absolute;
      top: 0; /* 内边距的颜色距离边框的距离 */
      left: 0;
      right: 0;
      bottom: 0;
      border: 2px solid #ff0000;
      z-index: -1; /* 将伪元素放置在内容下方 */
    }
    .canvas-small {
      height: 100%;
      width: 100%;
      object-fit: contain;
    }
  }
}
</style>
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue移动端中实现图片的双指放大缩小拖拽,可以通过以下步骤进行操作。 首先,需要在Vue组件中引入相应的移动端手势库,比如AlloyFinger或是基于它封装的vue-alloyfinger插件。这些手势库可以监听移动端的触摸事件,方便实现手势操作。 其次,在组件的模板中需要渲染一张图片,并设置图片的初始宽度和高度。可以通过绑定样式属性的方式,将图片的宽度和高度与组件中的data数据绑定起来。 然后,需要为图片绑定手势事件,并在对应的方法中实现双指放大缩小拖拽的逻辑。比如,可以监听双指缩放事件,在事件处理函数中根据手指的位置和缩放比例来更新图片的宽度和高度。可以监听拖拽事件,在事件处理函数中根据手指的移动距离来更新图片的位置。 最后,还可以添加一些边界判断,比如设置图片的最大和最小缩放比例,防止图片过小或过大。还可以添加过渡动画,使操作更加平滑。 需要注意的是,双指放大缩小拖拽的实现需要一定的数学计算,比如计算手指的距离和角度,或是计算图片的偏移量等。因此,在实现过程中需要对数学计算有一定的了解。 综上所述,通过Vue和移动端手势库,我们可以很方便地实现图片的双指放大缩小拖拽功能。通过监听手势事件,并在事件处理函数中更新图片的属性和位置,可以实现用户友好的图片操作效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值