vue项目使用canvas实现电子围栏

694 阅读1分钟

需求分析

1、电子围栏技术是采用基于深度神经网络的人工智能目标检测技术,通过在打包机四周监控范围,形成一个立体的电子围栏区域,根据视频场景中的信息准确识别目标区域内是否出现人员及其位置。

2、电子围栏系统通过AI 识别、人物自动检测等功能将区域形成电子围栏,在电子围栏区域检测到有人员活动时及时发出报警信号,并将打包机自动切换成无法强制工作的停止状态,提高打包机运行时的安全性,消除加护垫、整包、调试、检修作业安全隐患。

功能预览

媒体1.gif

<template>
  <div class="mains">
    <canvas
      class="mycanvas"
      ref="mycanvas"
      :width="canvasWidth"
      :height="canvasHeight"
      @mousedown="canvasDown($event)"
      @mousemove="canvasMove($event)"
      @mouseup="canvasUp($event)"
      >浏览器不持之canvas</canvas
    >
  </div>
</template>

<script>
import { message } from 'ant-design-vue';
export default {
  data() {
    return {
      isdraw: false, //是否在画图形
      ctx: null, //canvas对象
      coordinates: [], //一个多边形的坐标信息
      cor_index: 0, //当前多边形的索引
      endtip: false, //是否结束一个多边形的绘制
      all_coordinates: [], //所有多边形的信息
      isdrag: false,
      drag_index: [-1, -1],
      img: '',
      val: { cor_x: '', cor_y: '' }, //鼠标按下
      upVal: { cor_x: '', cor_y: '' }, //鼠标抬起
      isShowBox: false,
    };
  },
  props: {
    coordinatesData: {
      type: Array, //需要展示的坐标
    },
    showBox: {
      type: Boolean, //是否显示圆圈
    },
    canvasWidth: {
      type: Number, //画布宽度
    },
    canvasHeight: {
      type: Number, //画布高度
    },
  },
  watch: {
    showBox: {
      // 监听是否编辑
      handler(a) {
        this.isShowBox = a;
        this.initDraw();
      },
    },
  },
  methods: {
    //清空
    test() {
      this.all_coordinates = [];
      this.coordinates = [];
      this.isdraw = false;
      this.endtip = false;
      this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
    },
    //填充
    fillarea() {
      this.ctx.fillStyle = 'rgba(255, 77, 79,0.1)';
      for (var i = 0; i < this.all_coordinates.length; i++) {
        var cors = this.all_coordinates[i];
        var x0 = cors[0].cor_x;
        var y0 = cors[0].cor_y;
        this.ctx.beginPath();
        this.ctx.moveTo(x0, y0);
        for (var j = 1; j < cors.length; j++) {
          var x = cors[j].cor_x;
          var y = cors[j].cor_y;
          this.ctx.lineTo(x, y);
        }
        this.ctx.fill();
        this.ctx.closePath();
      }
    },
    //判断是否交叉
    move(val) {
      let flag1 = false;
      let flag2 = false;
      if (this.all_coordinates[0]) {
        // console.log(69, this.all_coordinates[0], this.val);
        let ind = this.isdragpoint(val.cor_x, val.cor_y);
        let arr = [...this.all_coordinates[0]];
        arr.push(arr[0]);
        console.log(79, arr);
        if (ind != -1) {
          arr[ind] = val;
          arr.some((item, index) => {
            if (index < arr.length - 1) {
              arr.some((v, i) => {
                if (i < arr.length - 1) {
                  if (
                    this.isIntersect(
                      {
                        x1: item.cor_x,
                        y1: item.cor_y,
                        x2: arr[index + 1].cor_x,
                        y2: arr[index + 1].cor_y,
                      },
                      {
                        x1: v.cor_x,
                        y1: v.cor_y,
                        x2: arr[i + 1].cor_x,
                        y2: arr[i + 1].cor_y,
                      },
                    )
                  ) {
                    flag2 = true;
                  }
                }
              });
            }
          });
        }
      } else {
        flag1 = this.coordinates.some((v, i) => {
          if (i < this.coordinates.length - 2) {
            return this.isIntersect(
              { x1: v.cor_x, y1: v.cor_y, x2: this.coordinates[i + 1].cor_x, y2: this.coordinates[i + 1].cor_y },
              {
                x1: this.coordinates[this.coordinates.length - 1].cor_x,
                y1: this.coordinates[this.coordinates.length - 1].cor_y,
                x2: val.cor_x,
                y2: val.cor_y,
              },
            );
          }
        });
      }

      console.log(flag1, flag2);
      if (flag1 == false && flag2 == false) {
        return false;
      } else {
        message.warning('线段不能交叉');
        return true;
      }
    },
    initDraw() {
      //初始化画布对象
      const canvas = document.querySelector('.mycanvas');
      this.ctx = canvas.getContext('2d');
      this.ctx.strokeStyle = 'rgb(235, 57, 65)';
      console.log(147, this.coordinatesData);
      if (this.coordinatesData.length > 0) {
        this.all_coordinates = [];
        this.coordinates = [];
        //把传过来的百分比坐标转换为px
        this.coordinatesData.forEach(e => {
          this.coordinates.push({
            cor_x: Math.round((this.canvasWidth * e[0]) / 100),
            cor_y: Math.round((this.canvasHeight * e[1]) / 100),
          });
        });
        console.log(189, this.coordinates);
        this.drawClosePath();
      }
    },
    //判断的是否是同一个点
    isdragpoint(x, y) {
      if (this.all_coordinates.length == 0) {
        return false;
      }
      for (var i = 0; i < this.all_coordinates.length; i++) {
        for (var j = 0; j < this.all_coordinates[i].length; j++) {
          var px = this.all_coordinates[i][j].cor_x;
          var py = this.all_coordinates[i][j].cor_y;
          // console.log(j + ':', x, y, px, py);
          if (Math.abs(x - px) <= 8 && Math.abs(y - py) <= 8) {
            this.drag_index[0] = i;
            this.drag_index[1] = j;
            // this.val.cor_x = px;
            // this.val.cor_y = py;
            // console.log(px, py);
            return j;
          }
        }
      }
      return false;
    },
    //手动闭合路径
    handleClosePath(x, y) {
      let arr = [this.coordinates];
      for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < arr[i].length; j++) {
          var px = arr[i][j].cor_x;
          var py = arr[i][j].cor_y;
          if (Math.abs(x - px) <= 8 && Math.abs(y - py) <= 8) {
            console.log(this.isdraw, j === 0);
            if (this.isdraw && j === 0 && !this.move(this.upVal)) {
              this.drawClosePath();

              return true;
            }
          }
        }
      }
      return false;
    },
    //鼠标按下
    canvasDown(e) {
      if (!this.isShowBox) return;
      var x = e.offsetX;
      var y = e.offsetY;
      this.val.cor_x = x;
      this.val.cor_y = y;

      if (this.handleClosePath(x, y)) return;
      console.log(267, this.val);
      this.isdragpoint(x, y);
      if (this.move(this.val)) return;
      if (this.isdragpoint(x, y) === 0 || this.isdragpoint(x, y)) {
        this.isdrag = true;
        return 0;
      }

      //画布中鼠标按下
      if (this.endtip) {
        //已经结束了上个多边形的绘制,把上个多边形的坐标放入数组,同时清空单个多边形数组信息
        this.endtip = false;
      }
      //获取鼠标按下的坐标,放入数组中
      if (this.all_coordinates.length == 1) {
        return;
      }
      this.coordinates.push({ cor_x: x, cor_y: y });
      this.isdraw = true; //正在画多边形
    },
    drawlines() {
      //把所有多边形画出来
      for (var i = 0; i < this.all_coordinates.length; i++) {
        var cors = this.all_coordinates[i];
        //前后坐标连线
        for (var j = 0; j < cors.length - 1; j++) {
          this.ctx.beginPath();
          var x0 = cors[j].cor_x;
          var y0 = cors[j].cor_y;
          var x1 = cors[j + 1].cor_x;
          var y1 = cors[j + 1].cor_y;
          this.ctx.moveTo(x0, y0);
          this.ctx.lineTo(x1, y1);
          this.ctx.stroke();
          this.ctx.closePath();
        }
        //最后一个与第一个连线
        var begin_x = cors[0].cor_x;
        var begin_y = cors[0].cor_y;
        var end_x = cors[cors.length - 1].cor_x;
        var end_y = cors[cors.length - 1].cor_y;
        this.ctx.beginPath();
        this.ctx.moveTo(begin_x, begin_y);
        this.ctx.lineTo(end_x, end_y);
        this.ctx.stroke();
        this.ctx.closePath();
      }
    },
    //绘制当前多边形线段
    drawline() {
      //把当前绘制的多边形之前的坐标线段绘制出来
      for (var i = 0; i < this.coordinates.length - 1; i++) {
        this.ctx.beginPath();
        var x0 = this.coordinates[i].cor_x;
        var y0 = this.coordinates[i].cor_y;
        var x1 = this.coordinates[i + 1].cor_x;
        var y1 = this.coordinates[i + 1].cor_y;
        this.ctx.moveTo(x0, y0);
        this.ctx.lineTo(x1, y1);
        this.ctx.stroke();
        this.ctx.closePath();
      }
    },
    //为当前的多边形端点画圆
    drawcircle() {
      this.ctx.strokeStyle = 'rgb(235, 57, 65)';
      this.ctx.fillStyle = '#fff';
      for (var i = 0; i < this.coordinates.length; i++) {
        var x = this.coordinates[i].cor_x;
        var y = this.coordinates[i].cor_y;
        this.ctx.beginPath();
        this.ctx.moveTo(x, y);
        this.ctx.arc(x, y, 8, 0, Math.PI * 2);
        this.ctx.stroke();
        this.ctx.fill();
        this.ctx.closePath();
      }
    },
    //为所有的多边形端点画圆
    drawcircles() {
      this.ctx.strokeStyle = 'rgb(235, 57, 65)';
      this.ctx.fillStyle = '#fff';
      for (var i = 0; i < this.all_coordinates.length; i++) {
        var cors = this.all_coordinates[i];
        for (var j = 0; j < cors.length; j++) {
          var x = cors[j].cor_x;
          var y = cors[j].cor_y;
          this.ctx.beginPath();
          this.ctx.moveTo(x, y);
          this.ctx.arc(x, y, 8, 0, Math.PI * 2);
          this.ctx.stroke();
          this.ctx.fill();
          this.ctx.closePath();
        }
      }
    },
    //鼠标抬起
    canvasUp(e) {
      if (!this.isShowBox) return;
      this.drag_index = [-1, -1];
      if (this.isdrag) {
        if (this.all_coordinates.length) {
          this.upVal.cor_x = e.offsetX;
          this.upVal.cor_y = e.offsetY;

          if (this.move(this.upVal)) {
            console.log(273);
            this.canvasMove({ offsetX: this.val.cor_x, offsetY: this.val.cor_y });
            this.isdrag = false;

            return;
          }
        }
        this.isdrag = false;
      }
      //   console.log(188, this.upVal, this.coordinates, this.all_coordinates);
    },
    // 辅助函数 检查两个线是否交叉
    isIntersect(line1, line2) {
      console.log(305, line1, line2);
      // 转换成一般式: Ax+By = C
      //   console.log(305, line1, line2);
      var a1 = line1.y2 - line1.y1;
      var b1 = line1.x1 - line1.x2;
      var c1 = a1 * line1.x1 + b1 * line1.y1;

      //转换成一般式: Ax+By = C
      var a2 = line2.y2 - line2.y1;
      var b2 = line2.x1 - line2.x2;
      var c2 = a2 * line2.x1 + b2 * line2.y1;

      // 计算交点
      var d = a1 * b2 - a2 * b1;
      // 当d==0时,两线平行
      if (d == 0) {
        return false;
      } else {
        var x = (b2 * c1 - b1 * c2) / d;
        var y = (a1 * c2 - a2 * c1) / d;

        // 检测交点是否在两条线段上
        if (
          (this.isInBetween(line1.x1, x, line1.x2) || this.isInBetween(line1.y1, y, line1.y2)) &&
          (this.isInBetween(line2.x1, x, line2.x2) || this.isInBetween(line2.y1, y, line2.y2))
        ) {
          console.log(331);
          return true;
        }
      }
      console.log(335);

      return false;
    },
    isInBetween(a, b, c) {
      // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
      if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
        return false;
      }

      return (a < b && b < c) || (c < b && b < a);
    },
    canvasMove(e) {
      //画布中鼠标移动
      //没开始画或者结束画之后不进行操作
      var x = e.offsetX;
      var y = e.offsetY;
      if (this.isdrag) {
        this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
        this.all_coordinates[this.drag_index[0]][this.drag_index[1]].cor_x = x;
        this.all_coordinates[this.drag_index[0]][this.drag_index[1]].cor_y = y;
        this.drawlines();
        this.drawcircles();
        this.fillarea();
      }
      //   console.log(338, this.coordinates.length == 0, !this.isdraw, this.endtip);
      if (this.coordinates.length == 0 || !this.isdraw || this.endtip) {
        return 0;
      }

      //获取上一个点
      var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
      var last_y = this.coordinates[this.coordinates.length - 1].cor_y;
      this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); //清空画布
      this.drawline(); //把之前的点连线
      this.drawcircle();
      if (this.all_coordinates.length != 0) {
        //不止一个多边形,把多边形们画出来
        this.drawlines();
        this.drawcircles();
        this.fillarea();
      }
      //获取鼠标移动时的点,画线,实现线段跟踪效果。
      this.ctx.beginPath();
      this.ctx.moveTo(last_x, last_y);

      this.ctx.lineTo(x, y);
      this.ctx.stroke();
      this.ctx.closePath();
    },
    //绘制闭合路径
    drawClosePath() {
      this.isdraw = false;
      this.endtip = true;
      this.all_coordinates.push(this.coordinates);

      this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
      this.ctx.fillStyle = 'rgba(255, 77, 79,0.1)';
      var bx = this.coordinates[0].cor_x;
      var by = this.coordinates[0].cor_y;
      this.ctx.beginPath();
      this.ctx.moveTo(bx, by);
      for (var k = 1; k < this.coordinates.length; k++) {
        var x = this.coordinates[k].cor_x;
        var y = this.coordinates[k].cor_y;
        this.ctx.lineTo(x, y);
      }
      this.ctx.lineTo(bx, by);
      this.ctx.stroke();
      this.ctx.fill();
      if (this.isShowBox) {
        this.drawcircles();
      }
      this.ctx.closePath();
      this.coordinates = [];
    },
  },
  mounted() {
    this.initDraw();
  },
};
</script>

<style scoped>
.mains {
  width: 100%;
  height: 100%;
  color: black;
  /* background: white; */
}
.mycanvas {
  border: 1px solid red;
  margin: auto;
  display: block;
  box-sizing: border-box;
}
</style>