vue canvas 区域绘制有绘制多边形,包括拖拽、重置、撤销、判断线段重合、判断点是否在多边形内

 先上效果图

(gif 图, 应该是可以看到的吧)

忘记解释了,

现在绘制的下面之所以是白色的,是因为录gif的时候 我监控没打开,所以监控没显示出来,用图片也是可以的

 层级是这样的,

然后我就直接上代码了,我写了大量的注释,要是看不懂的,我再解释

我是一个白菜,里面还是有部分搞不太懂,建议可以看一下参考文献第一个链接,问问大佬,


这个是DialogAreaDash.vue的

<template>
  <div>
    <el-dialog
      title="区域划线"
      :close-on-click-modal="false"
      :visible.sync="dialogFormVisible"
      :width="D_DIALOG_SIZE_BIG"
    >
      <div
        style="
          width: 100%;
          height: 570px;
          display: flex;
          justify-content: space-around;
        "
      >
        <div class="operation" style="width: 400px">
          <div class="intrusion">
            <div style="line-height: 30px">入侵区域数量</div>
            <el-select
              v-model="intrusion_num"
              @change="intrusionNumChange"
              placeholder="请选择"
            >
              <el-option
                v-for="item in intrusion_options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              >
              </el-option>
            </el-select>
          </div>

          <div
            class="intrusion_item"
            v-show="operation_arr.length != 0"
            v-for="(item, index) in operation_arr"
            :key="index"
          >
            <div class="item_title">{{ "入侵区域" + item.order_num }}</div>
            <div class="item_row">
              <span style="line-height: 35px">入侵时间(秒)</span>
              <div class="block">
                <el-slider v-model="item.timeThreshold" :max="10" show-input>
                </el-slider>
              </div>
            </div>
            <div class="item_row">
              <span style="line-height: 35px">灵敏度</span>
              <div class="block">
                <el-slider v-model="item.sensitivityLevel" show-input>
                </el-slider>
              </div>
            </div>
          </div>
        </div>

        <div class="areaDash">
          <canvasDraw
            ref="canvasDrawRef"
            style="height: 100%; width: 100%"
            @finish="handleDrawFinish"
            :single="true"
            :form="form"
            :limit="4"
          />
          <div class="dialog-footer" style="margin-top: 10px">
            <el-select
              v-model="Draw_item"
              @change="DrawNumChange"
              placeholder="请选择"
              class="DrawStyle"
              value-key="order_num"
            >
              <el-option
                v-for="item in operation_arr"
                :key="item.order_num"
                :label="'入侵区域' + item.order_num"
                :value="item"
              >
              </el-option>
            </el-select>
            <el-button plain v-show="isopen" @click="DrawArea(false)"
              >绘制区域</el-button
            >
            <el-button plain v-show="!isopen" @click="DrawArea(true)"
              >取消</el-button
            >
            <el-button plain v-show="!isopen" @click="resetForm"
              >重 置</el-button
            >
            <el-button type="primary" v-show="!isopen" @click="submitForm"
              >确 定</el-button
            >
          </div>
        </div>
      </div>

      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="configRegion">配置区域围栏</el-button>
        <el-button @click="dialogFormVisible = false">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import config from "../config";
import dialogControlorMixin from "@vcom/mixins/dialogControlorMixin";
import entMixin from "@vcom/mixins/entMixin";
import canvasDraw from "./components/index.vue";

export default {
  name: "DialogAreaDash",

  mixins: [dialogControlorMixin, entMixin],

  components: { canvasDraw },

  data() {
    return {
      config: config,
      form: {},
      formLabelWidth: "120px",

      intrusion_num: 0,
      intrusion_options: [
        { label: 1, value: 1 },
        { label: 2, value: 2 },
        { label: 3, value: 3 },
        { label: 4, value: 4 },
      ],
      operation_arr: [],
      isopen: true, // 为true时画板不可点击
      Draw_item: {}, // 当前选中的绘画区域
      endPoint: [], //绘画结束时的点位
    };
  },

  methods: {
    // 注册表单数据
    async init() {
      if (this.$refs["canvasDrawRef"]) {
        this.$refs["canvasDrawRef"].reset();
        this.$Bus.$emit("CHANGE_OPEN", true);
      }
      // 不可直接关联store
      this.form = this._.cloneDeep(
        this.$store.state[config.MODULE_NAME].dialogFormData
      );

      // 发起请求
      const res = await this.$api.ballVideo2.queryFences({
        device_uuid: this.form.uuid,
      });
      if (res.data && res.data.length != 0) {
        let allData = res.data;
        allData.sort(function (a, b) {
          //  a - b 是升序
          return a.order_num - b.order_num;
        });
        for (let i = 0; i < allData.length; i++) {
          if (allData[allData.length - 1 - i].is_enabled == 1) {
            // 倒序表明应用了几个
            for (let j = 0; j < allData.length - i; j++) {
              this.operation_arr.push({
                timeThreshold: allData[j].timeThreshold,
                sensitivityLevel: allData[j].sensitivityLevel,
                order_num: allData[j].order_num,
                is_enabled: allData[j].is_enabled,
                fenceDetailDTOS:allData[j].fenceDetailDTOS.map(e=>{
                  return {
                    order_num: e.order_num,
                    x: ( +e.x)/2,
                    y: ( +e.y)/2,
                  }
                })
              });
            }
            this.operation_arr.forEach((item) => {
              item.is_enabled == 1;
              item.fenceDetailDTOS.sort(function (a, b) {
                //  a - b 是升序
                return a.order_num - b.order_num;
              });
              this.$Bus.$emit("START_DRAWING", item);
            });
            this.intrusion_num = allData.length - i;
            break;
          }
        }
      }
    },

    DrawArea(e) {
      // 绘制区域
      if (e == false) {
        if (!(this.Draw_item && this.Draw_item.order_num)) {
          this.$alert("请选择入侵区域", "提示", {
            confirmButtonText: "确定",
          });
          return;
        }
        this.endPoint = this.Draw_item.fenceDetailDTOS;
      }
      this.isopen = e;
      this.$Bus.$emit("CHANGE_OPEN", e);
      // 取消
      if (e == true) {
        this.$refs["canvasDrawRef"].reset();
        this.$Bus.$emit("CHANGE_OPEN", e);
        this.$Bus.$emit("START_DRAWING", this.Draw_item);
      }
    },
    // 重置
    resetForm() {
      this.$refs["canvasDrawRef"].reset();
    },
    // 确定
    submitForm() {
      console.log(this.endPoint);
      this.isopen = true;
      this.$Bus.$emit("CHANGE_OPEN", true);
      this.endPoint.forEach((ele, index) => {
        ele.order_num = index + 1;
      });
      this.operation_arr[this.Draw_item.order_num - 1].fenceDetailDTOS =
        this.endPoint;
      this.$Bus.$emit(
        "START_DRAWING",
        this.operation_arr[this.Draw_item.order_num - 1]
      );
    },
    // 最后的确定,配置区域围栏
    async configRegion() {
      if (this.operation_arr.length == 0) {
        this.$alert("至少配置一个区域", "提示", {
          confirmButtonText: "确定",
        });
        return;
      }

      this.operation_arr.forEach((item) => {
        item.fenceDetailDTOS.forEach((ele) => {
          ele.x = ele.x * 2;
          ele.y = ele.y * 2;
        });
      });

      console.log(this.operation_arr);

      let sendData = {
        device_uuid: this.form.uuid,
        fenceDTOS: this.operation_arr,
      };

      // 发起请求
      const res = await this.$api.ballVideo2.configFences(sendData);

      this.dialogFormVisible = false;
    },
    // 绘画最后一笔结束时的数据
    handleDrawFinish(e) {
      console.log(this.endPoint);
      this.endPoint = e;
    },

    // 选中 绘画的入侵区域 的改变
    DrawNumChange(item) {
      if (item) {
        let drawArr = item;
        console.log(drawArr);

        this.$refs["canvasDrawRef"].reset();
        this.endPoint = [];
        this.isopen = true;
        this.$Bus.$emit("CHANGE_OPEN", true);

        if (drawArr.fenceDetailDTOS && drawArr.fenceDetailDTOS.length != 0) {
          // drawArr.fenceDetailDTOS.sort(function (a, b) {
          //   //  a - b 是升序
          //   return a.order_num - b.order_num;
          // });
          this.$Bus.$emit("START_DRAWING", drawArr);
        }
      }
    },

    // 入侵区域数量的改动
    intrusionNumChange(num) {
      if (this.operation_arr.length >= num) {
        this.operation_arr.length = num;
      } else {
        for (let i = this.operation_arr.length; i < num; i++) {
          this.operation_arr.push({
            order_num: +i + 1,
            is_enabled: 1,
            timeThreshold: 1,
            sensitivityLevel: 100,
            fenceDetailDTOS: [
              { order_num: 1, x: 375.5, y: 48.703125 },
              { order_num: 2, x: 376.5, y: 353.703125 },
              { order_num: 3, x: 54.5, y: 351.703125 },
              { order_num: 4, x: 53.5, y: 49.703125 },
            ],
          });
        }
      }
      this.$refs["canvasDrawRef"].reset();
      this.$Bus.$emit("CHANGE_OPEN", true);
      for (let i = 0; i < this.operation_arr.length; i++) {
        this.$Bus.$emit("START_DRAWING", this.operation_arr[i]);
      }
      this.Draw_item = {}
    },
    // 页面销毁
    copDestroy() {
      this.isopen = true;
      this.operation_arr = [];
      this.Draw_item = {};
      this.intrusion_num = 0;
    },
  },
  beforeDestroy() {
    this.$Bus.$off("CHANGE_OPEN");
    this.$Bus.$off("START_DRAWING");
  },
};
</script>

<style>
.intrusion {
  display: flex;
  justify-content: space-between;
}

.item_title {
  background-color: aliceblue;
  margin-top: 10px;
}

.item_row {
  display: flex;
  justify-content: space-between;
}

.block .show-input {
  width: 55% !important;
  margin-right: 250px !important;
}
.block .el-slider__input {
  width: 100px !important;
}
.block .el-slider__input .el-input {
  width: 100% !important;
}
.areaDash {
  width: 500px;
  height: 500px;
}
.DrawStyle {
  width: 150px !important;
}
</style>

对了,解释一下,我这个里面多排序的部分,是因为划线你肯定要有顺序,因为我从接口拿到的顺序是打乱的,所以我按照升序 整理了一下。


index的

<template>
  <div>
    <div class="canvas-box solid" ref="canvasBox">
      <!-- <img
        src="@/assets/bg1.png"
        style="
          width: 100%;
          height: 100%;
          position: absolute;
          top: 0;
          left: 0;
          object-fit: fill;
        "
      /> -->
      <video
        v-if="videoUrl"
        style="
          width: 100%;
          height: 100%;
          position: absolute;
          top: 0;
          left: 0;
          object-fit: fill;
        "
        :src="videoUrl"
        :autoplay="true"
      ></video>
      <!-- :controls="true" -->

      <!--用来和鼠标进行交互操作的canvas-->
      <canvas ref="canvas"></canvas>
      <!--存储已生成的点线,避免被清空-->
      <canvas
        ref="canvasSave"
        @click="handleCanvasSaveClick"
        @mousemove="handleCanvasSaveMove"
        @mouseup="handleCanvasMouseUp"
        @mousedown="handleCanvasMouseDown"
      ></canvas>
      <div
        style="width: 100%; height: 100%; position: absolute; top: 0; left: 0"
        v-show="open && single"
      ></div>
    </div>
  </div>
</template>

<script>
import FlvCop from "@/viewsCommon/components/FlvCop.vue";
export default {
  name: "DialogAreaDashIndex",
  data() {
    return {
      coincidentDistance: 10, // 重合距离
      oIndex: -1, //判断鼠标是否移动到起始点处,-1为否,1为是
      pointArr: [], //存放坐标的数组
      MovePointArr: [], //存放 拖拽移动时 坐标的数组
      pointX: "",
      pointY: "",
      ctxSave: "",
      canSave: "",
      ctx: "",
      can: "",
      lineColor: "rgba(64, 158, 255, 1)",
      open: true,
      completedDraw: false, // 是否已经完成了绘图
      clickMove: false, //表明 正在拖拽的状态
      videoUrl: "",
    };
  },
  components: { FlvCop },
  props: {
    // background:{
    //   type: [String],
    //   default: "",
    // },
    single: {
      type: [Boolean],
      default: true,
    },
    form: {
      type: [Object],
      default: {},
    },
    limit: {
      type: [Number],
      default: 4,
    },
  },
  watch: {
    form: {
      handler: function () {
        this.getVideo();
      },
      deep: true,
    },
  },
  beforeMount() {
    this.$Bus.$on("CHANGE_OPEN", (e) => {
      this.open = e;
      if (e == false) {
        // 开始绘制
        this.completedDraw = true;
      }
    });
    this.$Bus.$on("START_DRAWING", (area) => {
      this.ctx.beginPath();
      this.ctx.moveTo(area.fenceDetailDTOS[0].x, area.fenceDetailDTOS[0].y);
      for (var i = 1; i < area.fenceDetailDTOS.length; i++) {
        this.ctx.lineTo(area.fenceDetailDTOS[i].x, area.fenceDetailDTOS[i].y);
      }
      this.ctx.lineTo(area.fenceDetailDTOS[0].x, area.fenceDetailDTOS[0].y);
      this.ctx.fillStyle = "rgba(161,195,255,1)"; //填充颜色
      this.ctx.fill(); //填充
      this.ctx.stroke(); //绘制

      this.ctx.font = "20px 宋体";
      this.ctx.fillStyle = "rgb(255,165,0)";
      let heavyHeart = this.centerPoint(area.fenceDetailDTOS);
      // y向下为正
      this.ctx.fillText(
        "入侵区域 " + area.order_num,
        heavyHeart.x - 40,
        heavyHeart.y
        // 随便找两个不相邻的点的中点好了
        // (area.fenceDetailDTOS[0].x + area.fenceDetailDTOS[2].x) / 2,
        // (area.fenceDetailDTOS[0].y + area.fenceDetailDTOS[2].y) / 2
      );
      this.pointArr = area.fenceDetailDTOS
    });
  },
  methods: {
    // 计算向量叉乘
    crossMul(v1, v2) {
      return v1.x * v2.y - v1.y * v2.x;
    },
    // 判断两条线段是否相交
    checkCross(p1, p2, p3, p4) {
      let v1 = { x: p1.x - p3.x, y: p1.y - p3.y },
        v2 = { x: p2.x - p3.x, y: p2.y - p3.y },
        v3 = { x: p4.x - p3.x, y: p4.y - p3.y },
        v = this.crossMul(v1, v3) * this.crossMul(v2, v3);
      v1 = { x: p3.x - p1.x, y: p3.y - p1.y };
      v2 = { x: p4.x - p1.x, y: p4.y - p1.y };
      v3 = { x: p2.x - p1.x, y: p2.y - p1.y };
      return v <= 0 && this.crossMul(v1, v3) * this.crossMul(v2, v3) <= 0
        ? true
        : false;
    },
    // 判断点是否在多边形内
    checkPP(point, polygon) {
      let p1, p2, p3, p4;
      p1 = point;
      p2 = { x: -100, y: point.y };
      let count = 0;
      // 对每条边都和射线作对比
      for (let i = 0; i < polygon.length - 1; i++) {
        p3 = polygon[i];
        p4 = polygon[i + 1];
        if (this.checkCross(p1, p2, p3, p4) == true) {
          count++;
        }
      }
      p3 = polygon[polygon.length - 1];
      p4 = polygon[0];
      if (this.checkCross(p1, p2, p3, p4) == true) {
        count++;
      }
      // 如果为偶数 说明在多边形外面,如果为奇数,说明在多边形内部
      return count % 2 == 0 ? false : true;
    },

    // 通过多边形的各点位获得重心
    centerPoint(pointArr) {
      let X = 0,
        Y = 0;
      for (let i = 0; i < pointArr.length; i++) {
        X = X + +pointArr[i].x;
        Y = Y + +pointArr[i].y;
      }
      X = X / pointArr.length;
      Y = Y / pointArr.length;
      return { x: X, y: Y };
    },
    // 监控
    async getVideo() {
      // 发起请求
      // const res = await this.$api.ballVideo2.live({
      //   // device_uuid: "937adeb238944162b23961d0d216c962",
      //   device_uuid: this.form.uuid,
      // });
      // if (res && res.data) {
      //   this.videoUrl = res.data.fmp4;
      // }
    },
    initCanvas() {
      this.can = this.$refs["canvas"];
      this.can.width = this.$refs["canvasBox"].clientWidth;
      this.can.height = this.$refs["canvasBox"].clientHeight;
      // console.log(can.width, can.height);
      this.ctx = this.can.getContext("2d");
      this.ctx.strokeStyle = this.lineColor; //线条颜色
      this.ctx.fillStyle = "rgba(64, 158, 255,0.1)"; //填充颜色
      this.ctx.lineWidth = 4; //线条粗细
      this.ctx.globalAlpha = 0.7; //半透明

      this.canSave = this.$refs["canvasSave"];
      this.canSave.width = this.$refs["canvasBox"].clientWidth;
      this.canSave.height = this.$refs["canvasBox"].clientHeight;
      this.ctxSave = this.canSave.getContext("2d");
      this.ctxSave.strokeStyle = this.lineColor; //线条颜色
      this.ctxSave.fillStyle = "rgba(64, 158, 255,0.1)"; //填充颜色
      this.ctxSave.lineWidth = 4; //线条粗细
      this.ctxSave.globalAlpha = 0.7; //半透明
      // console.log(can);
      // console.log(canSave);
    },
    handleCanvasMouseUp() {
      if (this.clickMove) {
        this.clickMove = false;
        this.pointArr = this.MovePointArr;
        this.$emit("finish", this.pointArr);
      }
    },
    handleCanvasMouseDown(e) {
      if (e && e.layerX) {
        if (
          this.completedDraw &&
          this.checkPP(
            {
              x: e.offsetX == undefined ? e.layerX : e.offsetX,
              y: e.offsetY == undefined ? e.layerY : e.offsetY,
            },
            this.pointArr
          )
        ) {
          this.clickMove = true;
          this.pointX = e.offsetX;
          this.pointY = e.offsetY;
        }
      }
    },
    handleCanvasSaveClick(e) {
      if (this.completedDraw) {
        return;
      }
      if (e.layerX) {
        this.pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
        this.pointY = e.offsetY == undefined ? e.layerY : e.offsetY;

        var piX, piY;
        if (this.oIndex > 0 && this.pointArr.length > 0) {
          piX = this.pointArr[0].x;
          piY = this.pointArr[0].y;
          //画点
          this.makearc(
            this.ctx,
            piX,
            piY,
            this.GetRandomNum(2, 2),
            0,
            180,
            this.lineColor
          );
          this.pointArr.push({ x: piX, y: piY });
          if (this.isExistIntersection()) {
            this.$alert("存在交叉线段,请重新绘制", "提示", {
              confirmButtonText: "确定",
            });
            this.revoke();
            return;
          }
          this.canvasSave(this.pointArr); //保存点线同步到另一个canvas
          this.saveCanvas(); //生成画布
        } else {
          piX = this.pointX;
          piY = this.pointY;
          this.makearc(
            this.ctx,
            piX,
            piY,
            this.GetRandomNum(2, 2),
            0,
            180,
            this.lineColor
          );
          this.pointArr.push({ x: piX, y: piY });
          if (this.isExistIntersection()) {
            this.$alert("存在交叉线段,请重新绘制", "提示", {
              confirmButtonText: "确定",
            });
            this.revoke();
            return;
          }
          this.canvasSave(this.pointArr); //保存点线同步到另一个canvas
        }
      }
    },
    handleCanvasSaveMove(e) {
      if (this.completedDraw) {
        // 分成两个if是因为完成绘图后鼠标移动不产生线条
        if (this.clickMove) {
          let flag = true;
          // 多边形不可出画板
          for (let i = 0; i < this.pointArr.length; i++) {
            if (
              this.pointArr[i].x + e.offsetX - this.pointX >= this.can.width ||
              this.pointArr[i].x + e.offsetX - this.pointX <= 0 ||
              this.pointArr[i].y + e.offsetY - this.pointY >= this.can.height ||
              this.pointArr[i].y + e.offsetY - this.pointY <= 0
            ) {
              flag = false;
              this.handleCanvasMouseUp()
            }
          }
          if (flag == false) return;

          // 表示点击之后移动
          this.MovePointArr = this.pointArr.map((ele) => {
            return {
              x: ele.x + e.offsetX - this.pointX,
              y: ele.y + e.offsetY - this.pointY,
            };
          });

          // // 清空画布
          this.ctx.clearRect(0, 0, this.can.width, this.can.height);
          this.ctxSave.clearRect(0, 0, this.canSave.width, this.canSave.height);

          // /*开始绘制*/
          this.ctx.beginPath();
          this.ctx.moveTo(this.MovePointArr[0].x, this.MovePointArr[0].y);
          if (this.MovePointArr.length > 1) {
            for (var i = 1; i < this.MovePointArr.length; i++) {
              this.ctx.lineTo(this.MovePointArr[i].x, this.MovePointArr[i].y);
            }
          }
          this.ctx.lineTo(this.MovePointArr[0].x, this.MovePointArr[0].y);
          this.ctx.fillStyle = "rgba(161,195,255,1)"; //填充颜色
          this.ctx.fill(); //填充
          this.ctx.stroke(); //绘制
        }
        return;
      }
      if (e.offsetX || e.layerX) {
        this.pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
        this.pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
        var piX, piY;
        /*清空画布*/
        this.ctx.clearRect(0, 0, this.can.width, this.can.height);
        /*鼠标下跟随的圆点*/
        this.makearc(
          this.ctx,
          this.pointX,
          this.pointY,
          this.GetRandomNum(4, 4),
          0,
          180,
          this.lineColor
        );

        if (this.pointArr.length > 0) {
          if (
            this.pointX > this.pointArr[0].x - this.coincidentDistance &&
            this.pointX < this.pointArr[0].x + this.coincidentDistance &&
            this.pointY > this.pointArr[0].y - this.coincidentDistance &&
            this.pointY < this.pointArr[0].y + this.coincidentDistance
          ) {
            if (this.pointArr.length > 1) {
              piX = this.pointArr[0].x;
              piY = this.pointArr[0].y;
              // console.log(piX, piY);

              this.ctx.clearRect(0, 0, this.can.width, this.can.height);
              this.makearc(
                this.ctx,
                piX,
                piY,
                this.GetRandomNum(4, 4),
                0,
                180,
                this.lineColor
              );
              this.oIndex = 1;
            }
          } else {
            piX = this.pointX;
            piY = this.pointY;
            this.oIndex = -1;
          }
          /*开始绘制*/
          this.ctx.beginPath();
          this.ctx.moveTo(this.pointArr[0].x, this.pointArr[0].y);
          if (this.pointArr.length > 1) {
            for (var i = 1; i < this.pointArr.length; i++) {
              this.ctx.lineTo(this.pointArr[i].x, this.pointArr[i].y);
            }
          }
          this.ctx.lineTo(piX, piY);
          this.ctx.fillStyle = "rgba(161,195,255,1)"; //填充颜色
          this.ctx.fill(); //填充
          this.ctx.stroke(); //绘制
        }
      }
    },
    // 存储已生成的点线
    canvasSave(pointArr) {
      this.ctxSave.clearRect(0, 0, this.ctxSave.width, this.ctxSave.height);
      this.ctxSave.beginPath();
      if (pointArr.length > 1) {
        this.ctxSave.moveTo(pointArr[0].x, pointArr[0].y);
        for (var i = 1; i < pointArr.length; i++) {
          this.ctxSave.lineTo(pointArr[i].x, pointArr[i].y);
          this.ctxSave.fillStyle = "rgba(161,195,255,1)"; //填充颜色
          //ctxSave.fill();
          this.ctxSave.stroke(); //绘制
        }
        this.ctxSave.closePath();
      }
    },
    /*生成画布 结束绘画*/
    saveCanvas() {
      if (this.pointArr.length <= 3) {
        this.$alert("检查区域至少为三边形,请重新绘制", "提示", {
          confirmButtonText: "确定",
        });
        this.revoke();
        return;
      }
      this.ctx.clearRect(0, 0, this.can.width, this.can.height);
      this.ctxSave.closePath(); //结束路径状态,结束当前路径,如果是一个未封闭的图形,会自动将首尾相连封闭起来
      this.ctxSave.fill(); //填充
      this.ctxSave.stroke(); //绘制

      this.completedDraw = true;
      this.$emit("finish", this.pointArr.slice(1));
    },
    /*验证canvas画布是否为空函数*/
    isCanvasBlank(canvas) {
      var blank = document.createElement("canvas"); //创建一个空canvas对象
      blank.width = canvas.width;
      blank.height = canvas.height;
      return canvas.toDataURL() == blank.toDataURL(); //为空 返回true
    },
    /*canvas生成圆点*/
    GetRandomNum(Min, Max) {
      var Range = Max - Min;
      var Rand = Math.random();
      return Min + Math.round(Rand * Range);
    },
    makearc(ctx, x, y, r, s, e, color) {
      ctx.clearRect(0, 0, 199, 202); //清空画布
      ctx.beginPath();
      ctx.fillStyle = color;
      ctx.arc(x, y, r, s, e);
      ctx.fill();
    },
    reset() {
      this.ctx.clearRect(0, 0, this.can.width, this.can.height);
      this.ctxSave.clearRect(0, 0, this.canSave.width, this.canSave.height);
      this.pointArr = [];
      this.completedDraw = false;
      this.open = false;
    },
    // 撤销上一步
    revoke() {
      if (this.open && this.single) {
        this.reset();
      }
      this.ctx.clearRect(0, 0, this.can.width, this.can.height);
      this.ctxSave.clearRect(0, 0, this.canSave.width, this.canSave.height);
      this.pointArr.pop();
      this.pointArr.map((item) => {
        let _e = {
          layerX: item.x,
          layerY: item.y,
        };
        this.handleCanvasSaveMove(_e);
      });
    },
    isExistIntersection() {
      let result = false;
      for (let i = 0; i < this.pointArr.length - 1; i++) {
        let a = this.pointArr[i];
        let b = this.pointArr[i + 1];
        for (let j = 0; j < this.pointArr.length - 1; j++) {
          if (i == j) {
            continue;
          }
          let c = this.pointArr[j];
          let d = this.pointArr[j + 1];
          result = this.isIntersection(a, b, c, d);
          if (result) {
            return result;
          }
          // console.log(result)
        }
      }
      return result;
    },
    //判断两个线段是否相交
    isIntersection(a, b, c, d) {
      /** 1 解线性方程组, 求线段交点. **/
      // 如果分母为0 则平行或共线, 不相交
      var denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
      if (denominator == 0) {
        return false;
      }

      // 线段所在直线的交点坐标 (x , y)
      var x =
        ((b.x - a.x) * (d.x - c.x) * (c.y - a.y) +
          (b.y - a.y) * (d.x - c.x) * a.x -
          (d.y - c.y) * (b.x - a.x) * c.x) /
        denominator;
      var y =
        -(
          (b.y - a.y) * (d.y - c.y) * (c.x - a.x) +
          (b.x - a.x) * (d.y - c.y) * a.y -
          (d.x - c.x) * (b.y - a.y) * c.y
        ) / denominator;

      /** 2 判断交点是否在两条线段上 **/
      if (
        // 交点在线段1上
        (x - a.x) * (x - b.x) < 0 &&
        (y - a.y) * (y - b.y) < 0 &&
        // 且交点也在线段2上
        (x - c.x) * (x - d.x) < 0 &&
        (y - c.y) * (y - d.y) < 0
      ) {
        // // 返回交点p
        // return {
        //   x: x,
        //   y: y,
        // };
        return true;
      }
      //否则不相交
      return false;
    },
  },
  async mounted() {
    this.initCanvas();
    this.getVideo();
  },
  beforeDestroy() {
    this.$Bus.$off("CHANGE_OPEN");
    this.$Bus.$off("START_DRAWING");
    this.videoUrl = "";
  },
};
</script>

<style lang="scss" scoped>
.canvas-box {
  position: relative;
  width: 100%;
  height: 100%;
  > canvas {
    position: absolute;
    top: 0;
    left: 0;
    // width: 1000px;
    // height: 800px;
    width: 100%;
    height: 100%;
  }
}
</style>

参考文献:

Vue3 区域绘制,有撤销、重置和判断线段重合功能_瞎跑的uice的博客-CSDN博客

js判断一个点是否在多边形内(重要)[整理] - 豆丁网

这篇文章,我是综合了文献里的各种方法,以及自己修改 添加一部分

感觉这篇文章不太好,侵权啥的,私信我,立刻删

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值