vue+canvas 实现在图片上鼠标手动或自动画框多边形,以点连点的方式得到框的坐标

421 阅读3分钟

以下是一个完整的案例 只要加上自己图片的地址 就可以直接在页面上用了

有任何问题可以评论,看到会及时回复

<template>
  <div>
    <!-- 撤回按钮 -->
    <el-button type="primary" size="mini" @click="undo">撤回</el-button>{{ polygonsConvent }}
    <!-- 保存图片按钮 -->
    <!-- <el-button type="primary" size="mini" @click="saveImage">保存图片</el-button> -->
    
    <!-- 颜色选择器 -->
    <!-- <input type="color" v-model="polygonColor" /> -->
    <!-- 创建一个容器来放置画布 -->
    <div style="width: 1500px; height: 800px; overflow: hidden; overflow:auto">
      <!-- 在容器内部创建一个画布,并设置其样式 -->
      <canvas style="object-fit: cover;" ref="canvas" @mousedown="startDrawing" @mousemove="drawLine" @mouseup="endDrawing"></canvas>
    </div>
    
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 画布和上下文
      canvas: null,
      context: null,
      // 点和多边形
      points: [], // 存储当前正在绘制的多边形的点
      polygons: [], // 存储已完成的多边形
      polygonsConvent:[],//存储已换算后的多边形坐标
      // 多边形的颜色和吸附距离
      polygonColor: '#FF0000',
      snapDistance: 20,
      // 原始图片和绘制状态
      originalImage: null,
      drawing: false,
      imageUrl:'自己图片的地址',
    };
  },
  mounted() {
    // 获取画布和上下文,并加载图片
    this.canvas = this.$refs.canvas;
    this.context = this.canvas.getContext('2d');
    this.loadImage();
  },
  methods: {
    //把已有的坐标自动显示在图片上    如果有需求的话 可以直接调用这个方法   this.loadImageshow()
    loadImageshow() {
      // 创建图片对象,加载图片,加载完成后绘制到画布上
      const image = new Image();
      image.onload = () => {
        this.originalImage = image;
        // 将图片尺寸缩小40%
        this.canvas.width = image.width * 0.4;
        this.canvas.height = image.height * 0.4;
        this.context.drawImage(image, 0, 0, this.canvas.width, this.canvas.height);
        // 图片加载完成后显示多边形
        this.showPolygonsOnImage(this.iceArea);//this.iceArea自己要显示在图片上的坐标  例子:[ [ [ 753, 383 ], [ 755, 578 ], [ 1315, 580 ], [ 1265, 308 ] ] ]
      };
      image.src = this.imageUrl;
    },
    // 绘制多边形
    drawPolygon(polygon) {
      // 绘制多边形线条
      this.context.beginPath();
      this.context.moveTo(polygon[0][0], polygon[0][1]);
      for (let i = 1; i < polygon.length; i++) {
        this.context.lineTo(polygon[i][0], polygon[i][1]);
      }
      this.context.closePath();
      this.context.strokeStyle = this.polygonColor;
      this.context.lineWidth = 2;
      this.context.stroke();
    },
    // 在图片加载后显示多边形
    showPolygonsOnImage(polygons) {
      polygons.forEach(polygon => {
        const scaledPolygon = polygon.map(point => [point[0] * 0.4, point[1] * 0.4]);
        this.drawPolygon(scaledPolygon);
      });
    },



    //鼠标手动画框  调用这个方法
    loadImage() {
      // 创建图片对象,加载图片,加载完成后绘制到画布上
      const image = new Image();
      image.onload = () => {
        this.originalImage = image;
        // 将图片尺寸缩小40%
        this.canvas.width = image.width * 0.4;
        this.canvas.height = image.height * 0.4;
        this.context.drawImage(image, 0, 0, this.canvas.width, this.canvas.height);
      };
      image.src = this.imageUrl;
    },
    startDrawing(event) {
      // 开始绘制,记录鼠标位置,并绘制点和线
      this.drawing = true;
      const x = event.clientX - this.canvas.getBoundingClientRect().left;
      const y = event.clientY - this.canvas.getBoundingClientRect().top;
      this.points.push([x, y]);
      this.drawPoints();
      this.drawLines();
    },
    drawLine(event) {
      // 绘制线
      if (!this.drawing) return;
      if (this.points.length < 1) return;
      const x = event.clientX - this.canvas.getBoundingClientRect().left;
      const y = event.clientY - this.canvas.getBoundingClientRect().top;
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.context.drawImage(this.originalImage, 0, 0, this.canvas.width, this.canvas.height);
      this.drawPoints();
      this.drawLines();
      // 绘制当前点和之前所有点之间的线
      for (let i = 1; i < this.points.length; i++) {
        this.context.beginPath();
        this.context.moveTo(this.points[i - 1][0], this.points[i - 1][1]);
        this.context.lineTo(this.points[i][0], this.points[i][1]);
        this.context.strokeStyle = this.polygonColor;
        this.context.lineWidth = 2;
        this.context.stroke();
      }
      // 绘制当前点和最后一个点之间的线
      this.context.beginPath();
      this.context.moveTo(this.points[this.points.length - 1][0], this.points[this.points.length - 1][1]);
      this.context.lineTo(x, y);
      this.context.strokeStyle = this.polygonColor;
      this.context.lineWidth = 2;
      this.context.stroke();
    },
    drawPoints() {
      // 绘制所有的点
      this.points.forEach(point => {
        this.context.beginPath();
        this.context.arc(point[0], point[1], 3, 0, 2 * Math.PI);
        this.context.fillStyle = this.polygonColor;
        this.context.fill();
      });
    },
    drawLines() {
      // 绘制所有的多边形线条
      this.polygons.forEach(polygon => {
        this.context.beginPath();
        this.context.moveTo(polygon[0][0], polygon[0][1]);
        polygon.forEach(point => {
          this.context.lineTo(point[0], point[1]);
        });
        this.context.closePath();
        this.context.strokeStyle = this.polygonColor;
        this.context.lineWidth = 2;
        this.context.stroke();
      });
    },
    endDrawing() {
      // 结束绘制
      if (!this.drawing) return;
      if (this.points.length > 1) {
        const firstPoint = this.points[0];
        const lastPoint = this.points[this.points.length - 1];
        const distance = Math.sqrt((firstPoint[0] - lastPoint[0]) ** 2 + (firstPoint[1] - lastPoint[1]) ** 2);
        if (distance <= this.snapDistance && this.points.length > 2) {
          console.log([...this.points.slice(0, -1)])
          // 计算缩放比例
          const scale = 1 / 0.4; // 缩小了40%,所以放大比例是 1 / 0.4

          // 将多边形的每个点的坐标乘以比例因子,实现100%换算
          const scaledPolygon = this.points.slice(0, -1).map(point => [Math.round(point[0] * scale), Math.round(point[1] * scale)]);
          console.log(scaledPolygon)
          this.polygons.push([...this.points.slice(0, -1)]); // 将当前多边形添加到多边形数组中
          this.polygonsConvent.push(scaledPolygon)
          this.points = []; // 清空当前绘制的多边形的点数组
          this.drawing = false;
          this.redrawPolygons();
        }
      }
    },
    undo() {
      // 撤回操作
      if (this.polygons.length > 0) {
        this.polygons.pop(); // 删除最后一个多边形
        this.polygonsConvent.pop();
        this.redrawPolygons();
      }
    },
    redrawPolygons() {
      // 重新绘制多边形
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.context.drawImage(this.originalImage, 0, 0, this.canvas.width, this.canvas.height);
      this.polygons.forEach(polygon => {
        this.context.beginPath();
        this.context.moveTo(polygon[0][0], polygon[0][1]);
        polygon.forEach(point => {
          this.context.lineTo(point[0], point[1]);
        });
        this.context.closePath();
        this.context.strokeStyle = this.polygonColor;
        this.context.lineWidth = 2;
        this.context.stroke();
      });
    },
    saveImage() {
      // 保存绘制的图片
      const dataUrl = this.canvas.toDataURL('image/png');
      const link = document.createElement('a');
      link.href = dataUrl;
      link.download = 'drawn_image.png';
      link.click();
    },
  },
};
</script>

<style>
canvas {
  border: 1px solid #000;
}
</style>