放大鼠标框选区域的图片

使用 Canvas 显示图片,通过鼠标选中 Canvas 内的一个矩形区域,松开鼠标后将该矩形内的内容以最适应 Canvas 框大小的方式来显示。基于 Vue 的写法。

示例:

1

1.加入 Canvas 元素

<div id="canvasContainer" style="width:100%;text-align:center;overflow:auto">
  <canvas id="canvas" width="1" height="1" tabindex="1">
    Sorry, your browser does not support HTML5 Canvas functionality which is
    required for this application.
  </canvas>
</div>

2.声明全局变量

data() {
    return {
      zoomRate: 1,
      // canvas拖拽放大缩小
      canvas: {},
      context: {},
      offscreenCanvas: {},
      offscreenContext: {},
      mousedown: {},
      rubberbandRectangle: {},
      dragging: false,
      scaleRate: 1,
      mousemove: {},
    };
}

3.在页面的 dom 渲染完成做事件监听

mounted() {
    // 初始化canvas对象
    this.canvas = document.getElementById("canvas");
    this.context = this.canvas.getContext("2d");
    this.offscreenCanvas = document.createElement("canvas");
    this.offscreenContext = this.offscreenCanvas.getContext("2d");

    this.canvas.onmousedown = (e) => { // 开始框选
      let loc = this.windowToCanvas(this.canvas, e.clientX, e.clientY); //获取鼠标点击在canvas的坐标
      e.preventDefault();

      this.mousedown.x = loc.x;
      this.mousedown.y = loc.y;
      this.rubberbandRectangle.left = this.mousedown.x;
      this.rubberbandRectangle.top = this.mousedown.y;
      this.dragging = true;
    };

    this.canvas.onmousemove = (e) => { // 框选中
      let loc = {};
      if (this.dragging) {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.context.drawImage(
          this.offscreenCanvas,
          0,
          0,
          this.canvas.width,
          this.canvas.height
        ); //把离屏canvas上的数据复制到当前canvas
        loc = this.windowToCanvas(this.canvas, e.clientX, e.clientY);
        this.rubberbandStretch(loc.x, loc.y); //计算出选中的矩形坐标
      } else {
        // 记录非点击状态下鼠标的位置,为定点缩放做准备
        this.mousemove = this.windowToCanvas(this.canvas, e.clientX, e.clientY);
      }
    };

    this.canvas.onmouseup = (e) => { // 结束框选
      let loc = this.windowToCanvas(this.canvas, e.clientY, e.clientY);
      this.rubberbandEnd(loc.x, loc.y); //鼠标抬起 开始从离屏canvas复制数据
    };
}

4.事件处理函数

methods: {
    drawImage() {
      const canvasContainer = document.getElementById("canvasContainer");
      if (canvasContainer && this.canvas) {
        if (this.context) {
          const img = new Image();
          img.src = this.imageData;
          img.onload = () => {
            // 获得body当前的尺寸,计算出缩放比例,对高度做限制
            // 使用原尺寸高度,在默认状态下会出现上下滚动条 canvasContainer.offsetHeight,略丑
            const heightScaleRate =
              (canvasContainer.offsetHeight - 5) / img.height;
            const widthScaleRate =
              (canvasContainer.offsetWidth - 5) / img.width;
            // 选取比率小的,以防超过边界出现滚动条
            this.scaleRate =
              heightScaleRate > widthScaleRate
                ? widthScaleRate
                : heightScaleRate;
            this.context.scale(this.scaleRate, this.scaleRate);

            this.canvas.width = Math.round(
              img.width * this.scaleRate * this.zoomRate
            );
            this.canvas.height = Math.round(
              img.height * this.scaleRate * this.zoomRate
            );

            this.context.beginPath();
            this.context.drawImage(
              img,
              0,
              0,
              this.canvas.width,
              this.canvas.height
            );
            this.context.stroke();

            this.offscreenCanvas.width = img.width;
            this.offscreenCanvas.height = img.height;
            this.offscreenContext.drawImage(
              img,
              0,
              0,
              this.offscreenCanvas.width,
              this.offscreenCanvas.height
            );
          };
        }
      }
    },
    windowToCanvas(canvas, x, y) {
      let bbox = canvas.getBoundingClientRect();
      return {
        x: x - bbox.left,
        y: y - bbox.top,
      };
    },
    rubberbandStretch(x, y) {
      this.rubberbandRectangle.left = Math.min(x, this.mousedown.x);
      this.rubberbandRectangle.top = Math.min(y, this.mousedown.y);
      this.rubberbandRectangle.width = Math.abs(x - this.mousedown.x);
      this.rubberbandRectangle.height = Math.abs(y - this.mousedown.y);

      this.context.fillStyle = "rgba(96,98,102,0.3)";
      this.context.fillRect(
        this.rubberbandRectangle.left,
        this.rubberbandRectangle.top,
        this.rubberbandRectangle.width,
        this.rubberbandRectangle.height
      );
    },
    rubberbandEnd(x, y) {
      if (
        x == this.rubberbandRectangle.left ||
        y == this.rubberbandRectangle.top
      ) {
        this.dragging = false;
        return;
      }

      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

      // 获取在原图里的尺寸
      this.canvas.width =
        this.rubberbandRectangle.width / this.scaleRate / this.zoomRate;
      this.canvas.height =
        this.rubberbandRectangle.height / this.scaleRate / this.zoomRate;
      this.context.drawImage(
        this.offscreenCanvas,
        this.rubberbandRectangle.left / this.scaleRate / this.zoomRate,
        this.rubberbandRectangle.top / this.scaleRate / this.zoomRate,
        this.rubberbandRectangle.width / this.scaleRate / this.zoomRate,
        this.rubberbandRectangle.height / this.scaleRate / this.zoomRate,
        0,
        0,
        this.rubberbandRectangle.width / this.scaleRate / this.zoomRate,
        this.rubberbandRectangle.height / this.scaleRate / this.zoomRate
      );

      var base64Img = this.canvas.toDataURL("image/jpg");
      this.imageData = base64Img;
      this.drawImage();

      this.dragging = false;
    },
}

可通过按钮、框选、滚轮滚动来放大缩小图片的示例:

Sorry, your browser does not support HTML5 Canvas functionality which is required for this application.