<template>
  <div>
    <div class="tool-box">
      <el-button size="mini" :type="isDrawing ? 'warning' : 'primary'" @click="startDraw">绘制区域</el-button>

      <el-button size="mini"  type="danger" :disabled="isDrawing" @click="clearAll">全部清除</el-button>
      <el-button size="mini"  type="success" :disabled="isDrawing" @click="savePoints">保存</el-button>
    </div>
    <div class="canvas-wrap">
      <canvas id="imgCanvas" ref="canvaxbox"></canvas>
      <!--用来和鼠标进行交互操作的canvas-->
      <canvas id="drawCanvas" ref="canvas" :style="{cursor: isDrawing?'crosshair': 'default'}"> </canvas>
      <!--存储已生成的点线,避免被清空-->
      <canvas id="saveCanvas" ref="canvasSave"></canvas>
    </div>
  </div>
</template>
<script>
// import OldJs from './oldJs.js'
export default {
  // mixins: [OldJs],
  data() {
    return {
      imgUrl: 'img/code/wechat-code.jpg',
      isDrawing: false, // 是否正在绘制
      ratio: 1,
      imgWidth: 3020,
      imgHeight: 1080,
      wrapWidth: 300,
      wrapHeight: 300,
      canvasWidth: 300,
      canvasHeight: 300,
      drawingPoints: [],
      drawedPoints: [],
      imgCanvas: null,
      imgCtx: null,
      drawCanvas: null,
      drawCtx: null,
      saveCanvas: null,
      saveCtx: null,
      submitData: [
        // {"polygon":{"x1":0,"y1":0,"x2":1920,"y2":0,"x3":1920,"y3":1080,"x4":0,"y4":1080}},
        {"polygon":{"x1":700,"y1":273,"x2":975,"y2":278,"x3":1107,"y3":368,"x4":718,"y4":354}},
        {"polygon":{"x1":49,"y1":32,"x2":183,"y2":35,"x3":181,"y3":100,"x4":55,"y4":97}},
        {"polygon":{"x1":433,"y1":250,"x2":706,"y2":253,"x3":707,"y3":392,"x4":435,"y4":393}},
        {"polygon":{"x1":45,"y1":539,"x2":193,"y2":538,"x3":192,"y3":622,"x4":41,"y4":623,"x5":42,"y5":623}}
      ]
      // [
      //   {"polygon": {"x1":700,"y1":273,"x2":975,"y2":278,"x3":1107,"y3":368,"x4":718,"y4":354}}
      // ]
    }
  },
  mounted() {
    this.initCanvas()
    this.getImage()
  },
  methods: {
    initCanvas() { // 初始化canvas画布

      let canvasWrap = document.getElementsByClassName('canvas-wrap')
      this.wrapWidth = canvasWrap [0].clientWidth
      this.wrapHeight = canvasWrap [0].clientHeight

      this.imgCanvas = document.getElementById('imgCanvas')
      this.imgCtx = imgCanvas.getContext('2d')

      // 绘制canvas
      this.drawCanvas = document.getElementById('drawCanvas')
      this.drawCtx = drawCanvas.getContext('2d')

      // 保存绘制区域 saveCanvas
      this.saveCanvas = document.getElementById('saveCanvas')
      this.saveCtx = saveCanvas.getContext('2d')
      // this.initImgCanvas()
    },
    initImgCanvas() {
      // 计算宽高比
      let ww = this.wrapWidth // 画布宽度
      let wh = this.wrapHeight // 画布高度
      let iw = this.imgWidth // 图片宽度
      let ih = this.imgHeight // 图片高度

      if (iw/ih < ww/wh) { // 以高为主
        this.ratio = ih / wh
        this.canvasHeight = wh
        this.canvasWidth = wh * iw/ih
      } else { // 以宽为主
        this.ratio = iw / ww
        this.canvasWidth = ww
        this.canvasHeight = ww * ih/iw
      }
      // 初始化画布大小
      imgCanvas.width = this.canvasWidth
      imgCanvas.height = this.canvasHeight
      drawCanvas.width = this.canvasWidth
      drawCanvas.height = this.canvasHeight
      saveCanvas.width = this.canvasWidth
      saveCanvas.height = this.canvasHeight

      // 图片加载绘制
      let img = document.createElement('img')
      img.src =  this.imgUrl
      img.onload = () => {
        console.log('图片已加载')
        this.imgCtx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight)
        this.renderDatas() // 渲染原有数据
      }
    },
    startDraw() { // 绘制区域
      if (this.isDrawing) return
      this.isDrawing = true
      // 绘制逻辑
      drawCanvas.addEventListener("click", this.drawImageClickFn)
      drawCanvas.addEventListener("dblclick", this.drawImageDblClickFn)
      drawCanvas.addEventListener("mousemove", this.drawImageMoveFn)
    },
    clearAll() { // 清空所有绘制区域
      this.saveCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
      this.drawedPoints = []
    },
    getImage() { // 请求图片
      axios.post('/dsj/screenshot/', {
        "source":"rtsp://admin:admin12345@10.10.42.251:554/cam/realmonitor?channel=1&subtype=0"
      }).then(res => {
        this.imgUrl = res.data.img
        this.imgWidth = res.data.width
        this.imgHeight = res.data.height
        this.imgUrl && this.initImgCanvas()
      })
    },
    drawImageClickFn(e) {
      let drawCtx = this.drawCtx
      if (e.offsetX || e.layerX) {
        let pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
        let pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
        let lastPoint = this.drawingPoints[this.drawingPoints.length-1] || []
        if (lastPoint[0] !== pointX || lastPoint[1] !== pointY) {
          this.drawingPoints.push([pointX, pointY])
        }
      }
    },
    drawImageMoveFn(e) {
      let drawCtx = this.drawCtx
      if (e.offsetX || e.layerX) {
        let pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
        let pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
        // 绘制
        drawCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);

        // 绘制点
        drawCtx.fillStyle = 'blue'
        this.drawingPoints.forEach((item, i) => {
          drawCtx.beginPath();
          drawCtx.arc(...item, 6, 0, 180)
          drawCtx.fill(); //填充
        })

        // 绘制动态区域
        drawCtx.save()
        drawCtx.beginPath();
        this.drawingPoints.forEach((item, i) => {
          drawCtx.lineTo(...item)
        })
        drawCtx.lineTo(pointX, pointY)
        drawCtx.lineWidth = "3";
        drawCtx.strokeStyle= "blue";
        drawCtx.fillStyle = 'rgba(255, 0, 0, 0.3)'
        drawCtx.stroke();
        drawCtx.fill(); //填充
        drawCtx.restore()
      }
    },
    drawImageDblClickFn(e) {
      let drawCtx = this.drawCtx
      let saveCtx = this.saveCtx
      if (e.offsetX || e.layerX) {
        let pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
        let pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
        let lastPoint = this.drawingPoints[this.drawingPoints.length-1] || []
        if (lastPoint[0] !== pointX || lastPoint[1] !== pointY) {
          this.drawingPoints.push([pointX, pointY])
        }
      }
      // 清空绘制图层
      drawCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
      // 绘制区域至保存图层
      this.drawSaveArea(this.drawingPoints)

      this.drawedPoints.push(this.drawingPoints)
      this.drawingPoints = []
      this.isDrawing = false

      // 绘制结束逻辑
      drawCanvas.removeEventListener("click", this.drawImageClickFn)
      drawCanvas.removeEventListener("dblclick", this.drawImageDblClickFn)
      drawCanvas.removeEventListener("mousemove", this.drawImageMoveFn)
    },
    drawSaveArea(points) {
      if(!points instanceof Array || points.length === 0) return
      this.saveCtx.save()
      this.saveCtx.beginPath();
      points.forEach((item, i) => {
        this.saveCtx.lineTo(...item)
      })
      this.saveCtx.closePath()
      this.saveCtx.lineWidth="2";
      this.saveCtx.fillStyle = 'rgba(255,0, 255, 0.3)'
      this.saveCtx.strokeStyle= "red";
      this.saveCtx.stroke();
      this.saveCtx.fill(); //填充
      this.saveCtx.restore()
    },

    savePoints() { // 将画布坐标数据转换成提交数据
      let objectPoints = []
      // "object": [{"polygon": {"x1":700,"y1":273,"x2":975,"y2":278,"x3":1107,"y3":368,"x4":718,"y4":354} }]
      objectPoints = this.drawedPoints.map(area => {
        let polygon = {}
        area.forEach((point, i) => {
          polygon[`x${i+1}`] = Math.round(point[0] * this.ratio)
          polygon[`y${i+1}`] = Math.round(point[1] * this.ratio)
        })
        return {
          "polygon": polygon
        }
      })
      this.submitData = objectPoints
      console.log('最终提交数据', objectPoints)
    },
    renderDatas() { // 将提交数据数据转换成画布坐标
      this.drawedPoints = this.submitData.map(item => {
        let polygon = item.polygon
        let points = []
        for(let i=1; i< Object.keys(polygon).length/2 + 1; i++) {
          if (!isNaN(polygon[`x${i}`]) && !isNaN(polygon[`y${i}`])) {
            points.push([polygon[`x${i}`]/this.ratio, polygon[`y${i}`]/this.ratio])
          }
        }
        this.drawSaveArea(points)
        return points
      })
    }
  }
};
</script>
<style lang="scss" scoped>
.tool-box {
  width: 60vw;
  height: 40px;
  padding: 5px 30px;
  margin: 20px auto 0;
  box-sizing: border-box;
  text-align: right;
}
.canvas-wrap {
  // width: 80vw;
  // height: 45vw;
  width: 60vw;
  height: 33.75vw;
  margin: 0px auto;
  background-color: #000;//#fff;
  border: 3px;
  border-color: #333;
  position: relative;
}
#imgCanvas, #drawCanvas, #saveCanvas {
  background: rgba(255, 0, 255, 0);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
#drawCanvas {
  z-index: 2;
}
</style>

大体思路:

        三层canvas,图片canvas、绘制canvas和绘制区域保存canvas。

        canvas画布大小根据加载的图片大小按比例适配至页面操作区域。

        submitData为后端数据存储格式,可根据要求适当调整。

Logo

前往低代码交流专区

更多推荐