Vue canvas画图画线例子,数据回显与隔离,点拖拽修改

发布于:2025-02-10 ⋅ 阅读:(33) ⋅ 点赞:(0)

组件

<template>
  <div
    style="display: flex; height: 342px; width: 760px; border: 1px solid #000"
  >
    <div
      style="position: relative; height: 100%; width: 608px; min-width: 608px"
    >
      <canvas
        id="mycanvas"
        ref="mycanvas"
        :width="canvasWidth"
        :height="canvasHeight"
        @mousedown="canvasDown($event)"
        @mousemove="canvasMove($event)"
        @mouseup="canvasUp($event)"
        @dblclick="doubleclick()"
      ></canvas>
    </div>
    <div
      style="
        display: flex;
        flex-direction: column;
        padding: 10px;
        border: 1px solid #cbd2f7;
      "
    >
      <el-button
        size="mini"
        style="margin-left: 0; margin-top: 10px"
        @click="beginDraw = !beginDraw"
        :type="beginDraw == false ? 'primary' : ''"
        >{{ beginDraw ? "结束绘制" : "开始绘制" }}</el-button
      >
      <el-button
        size="mini"
        @click="clearAll"
        style="margin-left: 0; margin-top: 10px"
        >清空绘制区域</el-button
      >
      <div>
        <el-select
          size="mini"
          placeholder="请选择绘制类型"
          v-model="roiType"
          @change="clearAll"
          style="margin-top: 10px"
        >
          <el-option label="区域" value="1" v-if="myRoiType == 1"></el-option>
          <el-option label="线条" value="2" v-if="myRoiType == 2"></el-option>
          <el-option
            label="单区域+单向单拌线"
            value="3"
            v-if="myRoiType == 3"
          ></el-option>
        </el-select>
        <el-select
          size="mini"
          placeholder="请选择线条方向类型"
          v-model="directionType"
          v-show="roiType != '1'"
          :disabled="directionTypeDisabled"
          style="margin-top: 10px"
          @change="clearAll"
        >
          <el-option
            v-for="item in directionTypeOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          ></el-option>
        </el-select>
        <div style="display: flex">
          <el-button
            style="width: 50%; margin-top: 10px"
            size="mini"
            :type="drawType == 1 ? 'primary' : ''"
            @click="drawType = 1"
            v-show="roiType == 3"
            >区域</el-button
          >
          <el-button
            size="mini"
            :type="drawType == 2 ? 'primary' : ''"
            @click="drawType = 2"
            v-show="roiType == 3"
            style="width: 50%; margin-top: 10px"
            >直线</el-button
          >
        </div>
        <!-- </el-button-group> -->
      </div>

      <div class="staticLabels">
        <div
          class="staticLabels_item"
          v-for="(item, index) in allDrawList"
          @mouseenter="canvasMouseenter(item)"
          @mouseleave="canvasMouseleave()"
          v-if="(index >= 0 && !isMultiLine) || (index > 0 && isMultiLine)"
        >
          <span class="staticLabels_item_span">
            {{ item.areaName }}
          </span>
          <i
            class="el-icon-close"
            @click="canvasDeleteOne(item)"
            style="cursor: pointer"
            title="删除"
          ></i>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    canvasKey: {
      type: String,
      default: () => "first",
    },
    // 画图数据
    myDrawList: {
      type: Array,
      default: () => [],
    },
    myRoiType: {
      type: String,
      default: () => "1",
    },
  },
  data() {
    return {
      isMultiLine: false, //是否多拌线
      localMyDrawList: [],
      finalArr: [],

      roiType: "1",
      roiTypeOptions: [
        {
          value: "1",
          label: "区域",
        },
        {
          value: "2",
          label: "线条",
        },
        {
          value: "3",
          label: "单区域+单向单拌线",
        },
      ],
      directionTypeDisabled: false,
      directionType: "1",
      directionTypeOptions: [
        {
          value: "1",
          label: "单向单拌线",
        },
        {
          value: "2",
          label: "双向单拌线",
        },
        {
          value: "3",
          label: "单向多拌线",
        },
        {
          value: "4",
          label: "双向多拌线",
        },
      ],
      //
      canvasId: 1,
      drawType: "1", // 绘制类型 1-多边形 2-线条
      arrowType: "1", // 箭头类型 1-单向 2-双向
      all_line_coordinates: [
        // {
        //   canvasId: 1,
        //   areaName: '直线1',
        //   directionType:1,
        //   points_coordinates: [{ cor_x: 100, cor_y: 100 }],
        // }
      ], //所有线条的信息
      MaxAreaPointsNum: 10, //多边形的最大顶点数
      MaxLinePointsNum: 5, //线条的最大顶点数
      MaxAreaNum: 10, //多边形的最大数量
      MaxLineNum: 10, //线条的最大数量
      //
      isdraw: false, //是否在画图形
      ctx: null, //canvas对象
      coordinates: [], //当前图形的坐标信息
      all_coordinates: [
        // {
        //   canvasId: 1,
        //   areaName: '区域1',
        //   points_coordinates: [{ cor_x: 100, cor_y: 100 }],
        // },
      ], //所有多边形的信息
      isdrag: false, //是否拖拽点
      isdragType: 1, //拖拽类型 1-拖动多边形 2-拖动线条
      drag_index: [-1, -1], // 拖拽索引
      beginDraw: false, //开始作画
      colorList: [
        // "rgba(88,87,86,.4)",
        // "rgba(252,230,202,.4)",
        // "rgba(0,199,140,.4)",
        // "rgba(227,23,13,.4)",
        // "rgba(153,51,250,.4)",
        // "rgba(199,97,20,.4)",
        // "rgba(250,240,230,.4)",
        // "rgba(188,143,143,.4)",
        // "rgba(0,255,0,.4)",
        // "rgba(244,164,95,.4)",

        // "rgba(128,42,42,.4)",
        // "rgba(64,224,205,.4)",
        // "rgba(237,145,33,.4)",
        // "rgba(34,139,34,.4)",
        // "rgba(255,125,64,.4)",
        // "rgba(107,142,35,.4)",
        // "rgba(227,207,87,.4)",
        // "rgba(3,168,158,.4)",
        // "rgba(255,255,255,.4)",
        // "rgba(255,255,0,.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",

        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",
        "rgba(16, 128, 219, 0.4)",

        "rgba(255,0,0,.6)", //上色
      ],
      canvasWidth: 608, //画布宽度
      canvasHeight: 342, //画布高度
      //
      cor_index: 0, //当前多边形的索引 未开发
      isFirst: true,
    };
  },
  watch: {
    canvasKey: {
      handler(val) {
        this.clearAll();
        this.showMyDrawList();
      },
      deep: true,
      immediate: true,
    },
    myRoiType: {
      handler(val) {
        if (val) {
          this.clearAll();
          this.roiType = val;
        //   this.showMyDrawList();
        }
      },
      // deep:true,
      immediate: true,
    },
    // 区域类型:1-区域;2-线条;3-区域+单向线条
    roiType: {
      handler(val) {
        if (val) {
          this.MaxAreaNum = 10;
          this.MaxLineNum = 10;
          this.directionTypeDisabled = false;
          if (val == 1) {
            this.drawType = 1;
          } else if (val == 2) {
            this.drawType = 2;
            this.MaxLineNum = 1;
          }
          // 3-单区域+单向线条
          else if (val == 3) {
            this.MaxAreaNum = 1;
            this.MaxLineNum = 1;
            this.arrowType = 1;
            this.directionType = "1";
            this.directionTypeDisabled = true;
          }
        }
        this.handleMultiLine();
      },
      deep: true,
      immediate: true,
    },
    //线条方向类型:1-单向单拌线,2-双向单拌线,3-单向多拌线,4-双向多拌线
    directionType: {
      handler(val) {
        if (val) {
          // 1-单向单拌线
          if (val == 1) {
            this.arrowType = 1;
            this.MaxLineNum = 1;
          }
          // 2-双向单拌线
          else if (val == 2) {
            this.arrowType = 2;
            this.MaxLineNum = 1;
          }
          // 3-单向多拌线
          else if (val == 3) {
            this.arrowType = 1;
            this.MaxLineNum = 2;
          }
          // 4-双向多拌线
          else if (val == 4) {
            this.arrowType = 2;
            this.MaxLineNum = 2;
          }
        }
        this.handleMultiLine();
      },
      deep: true,
      // immediate: true,
    },
    allDrawList: {
      handler(val) {
        // 处理数据-------------------------------------------------
        var guardAreas = [];
        val &&
          val.length > 0 &&
          val.forEach((item, index) => {
            var areaName = item.areaName;
            // 线段
            if (item.directionType) {
              var directionType = item.directionType;
              var linePoints = [];
              item.points_coordinates &&
                item.points_coordinates.length > 0 &&
                item.points_coordinates.forEach((item2, index2) => {
                  linePoints.push({
                    x: (item2.cor_x / this.canvasWidth).toFixed(4),
                    y: (item2.cor_y / this.canvasHeight).toFixed(4),
                  });
                });
              var obj = {
                areaName: areaName,
                directionType: directionType,
                linePoints: linePoints,
              };
              guardAreas.push(obj);
            }
            // 区域
            else {
              var directionType = item.directionType;
              var points = [];
              item.points_coordinates &&
                item.points_coordinates.length > 0 &&
                item.points_coordinates.forEach((item2, index2) => {
                  points.push({
                    x: (item2.cor_x / this.canvasWidth).toFixed(4),
                    y: (item2.cor_y / this.canvasHeight).toFixed(4),
                  });
                });
              // 闭合区域 补起点
              if (
                points[0].x !== points[points.length - 1].x ||
                points[0].y !== points[points.length - 1].y
              ) {
                points.push({
                  x: points[0].x,
                  y: points[0].y,
                });
              }
              var obj = {
                areaName: areaName,
                points: points,
              };
              guardAreas.push(obj);
            }
          });
        var finalObj = {
          videoCode: this.canvasKey,
          guardAreas: guardAreas,
        };
        // console.log('回传数据',finalObj);
        var myDrawList = JSON.parse(JSON.stringify(this.localMyDrawList));
        var index = myDrawList.findIndex(
          (v) => v.videoCode == finalObj.videoCode
        );
        if (index != -1) {
          myDrawList.splice(index, 1, finalObj);
        } else {
          myDrawList.push(finalObj);
        }
        // this.$emit("drawListChange", myDrawList);
        // console.log("数据回传", myDrawList);
        this.finalArr = [].concat(myDrawList);
        this.localMyDrawList = [].concat(myDrawList);
        // 处理数据----------------------------------------------
        if (val.length == 0) {
          this.canvasId = 1;
        }
      },
      deep: true,
      immediate: true,
    },

    myDrawList: {
      handler(val) {
        this.clearAll();
        if (val) {
          this.localMyDrawList = [].concat(val);
          this.showMyDrawList();
        }
      },
      deep: true,
      immediate: true,
    },

    MaxLineNum: {
      handler(val) {
        this.handleMultiLine();
      },
      // deep: true,
      immediate: true,
    },
  },
  computed: {
    allDrawList: {
      get() {
        return this.all_coordinates.concat(this.all_line_coordinates);
      },
    },
  },
  mounted() {
    this.initDraw();
    document.getElementById("mycanvas").oncontextmenu = function (e) {
      e.preventDefault(); //阻止默认右键菜单
    };
  },
  methods: {
    handleMultiLine() {
      if (this.MaxLineNum == 2 && this.roiType == 2) {
        this.isMultiLine = true;
      } else {
        this.isMultiLine = false;
      }
    },
    // 回显
    showMyDrawList() {
      var val = this.localMyDrawList || [];
      if (this.canvasKey) {
        // console.log("画图回显 localMyDrawList", val);
        val.forEach((itemOut, index) => {
          if (itemOut.videoCode == this.canvasKey) {
            // console.log("当前", itemOut, itemOut.guardAreas);
            itemOut.guardAreas &&
              itemOut.guardAreas.length &&
              itemOut.guardAreas.forEach((item, index) => {
                // 线段
                if (item.directionType) {
                  this.directionType = item.directionType;
                  // console.log("回显切换", item.directionType);
                  if (item.directionType == 1 || item.directionType == 3)
                    this.arrowType = 1;
                  else this.arrowType = 2;
                  var areaName = item.areaName;
                  var directionType = item.directionType;
                  var canvasId = Number(item.areaName.substring(2));
                  var points_coordinates = [];
                  item.linePoints.forEach((points, index) => {
                    // 回显的时候不需要最后一个点,因为他跟第一个点重合
                    if (
                      index != 0 &&
                      points.x == item.linePoints[0].x &&
                      points.y == item.linePoints[0].y
                    ) {
                      return;
                    }
                    points_coordinates.push({
                      // cor_x: (points.x * this.canvasWidth).toFixed(0),
                      // cor_y: (points.y * this.canvasHeight).toFixed(0),
                      cor_x: Math.ceil(points.x * this.canvasWidth),
                      cor_y: Math.ceil(points.y * this.canvasHeight),
                    });
                  });
                  var obj = {
                    canvasId: canvasId,
                    areaName: areaName,
                    directionType: directionType,
                    points_coordinates: points_coordinates,
                  };
                  this.canvasId = ++canvasId;
                  this.all_line_coordinates.push(obj);
                }
                // 区域
                else {
                  var areaName = item.areaName;
                  var canvasId = Number(item.areaName.substring(2));
                  var points_coordinates = [];
                  item.points.forEach((points, index) => {
                    // 回显的时候不需要最后一个点,因为他跟第一个点重合
                    if (
                      index != 0 &&
                      points.x == item.points[0].x &&
                      points.y == item.points[0].y
                    ) {
                      return;
                    }
                    points_coordinates.push({
                      // cor_x: (points.x * this.canvasWidth).toFixed(0),
                      // cor_y: (points.y * this.canvasHeight).toFixed(0),
                      cor_x: Math.ceil(points.x * this.canvasWidth),
                      cor_y: Math.ceil(points.y * this.canvasHeight),
                    });
                  });
                  var obj = {
                    canvasId: canvasId,
                    areaName: areaName,
                    points_coordinates: points_coordinates,
                  };
                  this.canvasId = ++canvasId;
                  this.all_coordinates.push(obj);
                }
              });
            this.$nextTick(() => {
              // console.log("回显函数");
              this.drawAll();
            });
          }
        });
      }
    },
    getData() {
      return this.finalArr;
    },
    // 删除一个图形
    canvasDeleteOne(data) {
      if (this.isMultiLine) {
        this.all_line_coordinates = [];
        this.drawAll();
      } else {
        if (data.directionType) {
          var index = this.all_line_coordinates.findIndex(
            (v) => v.canvasId == data.canvasId
          );
          if (index != -1) this.all_line_coordinates.splice(index, 1);
        } else {
          var index = this.all_coordinates.findIndex(
            (v) => v.canvasId == data.canvasId
          );
          if (index != -1) this.all_coordinates.splice(index, 1);
        }
        this.drawAll();
      }
    },
    // 鼠标悬浮,给指定图形上色
    canvasMouseenter(data) {
      this.drawAll(data);
    },
    // 离开取消上色
    canvasMouseleave() {
      this.drawAll();
    },
    // 绘制所有图形
    drawAll(data) {
      // console.log(this.all_coordinates, this.all_line_coordinates);
      this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
      if (
        this.all_coordinates.length != 0 ||
        this.all_line_coordinates.length != 0
      ) {
        this.drawlines(data);
        this.drawcircles();
        this.fillarea(data);
      }
    },
    // ----------------------------------------------------------
    // 显示箭头
    showArrow(x1, y1, x2, y2) {
      var xm = (x1 + x2) / 2;
      var ym = (y1 + y2) / 2;
      var d = 30; //箭头长度 距离
      if (this.arrowType == 1) {
        var { x, y } = this.getArrowPoint(x1, y1, x2, y2, d);
        this.drawArrow(this.ctx, xm, ym, x, y, 30, 10, 1, "#f36");
      } else {
        var { x3, y3, x4, y4 } = this.getArrowPoint(x1, y1, x2, y2, d);
        this.drawArrow(this.ctx, xm, ym, x3, y3, 30, 10, 1, "#f36");
        this.drawArrow(this.ctx, xm, ym, x4, y4, 30, 10, 1, "#f36");
      }
    },
    // 获取箭头坐标点
    getArrowPoint(x1, y1, x2, y2, d) {
      var x0 = (x1 + x2) / 2;
      var y0 = (y1 + y2) / 2;
      var k1 = (y2 - y1) / (x2 - x1);
      var k2 = -1 / k1;
      var num = Math.sqrt(d ** 2 / (1 + k2 ** 2));
      var x3 = num + x0;
      var y3 = k2 * (x3 - x0) + y0;
      var x4 = 0 - num + x0;
      var y4 = k2 * (x4 - x0) + y0;
      // 平行于x轴
      if (y2 == y1) {
        x3 = x0;
        x4 = x0;
        y3 = y0 + d;
        y4 = y0 - d;
      }
      // 平行于y轴
      else if (x2 == x1) {
        x3 = x0 + d;
        x4 = x0 - d;
        y3 = y0;
        y4 = y0;
      }
      x3 = Math.floor(x3);
      y3 = Math.floor(y3);
      x4 = Math.floor(x4);
      y4 = Math.floor(y4);
      var x, y;
      // 返回向上
      if (x2 > x1) {
        y = y3 < y0 ? y3 : y4;
        x = y == y3 ? x3 : x4;
      }
      // 返回向下
      else {
        y = y3 < y0 ? y4 : y3;
        x = y == y3 ? x3 : x4;
      }
      if (this.arrowType == 1) {
        return { x, y };
      } else {
        return { x3, y3, x4, y4 };
      }
    },
    // ----------------------------------------------------------
    /**
     * @param  dot {{x,y}} 需要判断的点
     * @param  coordinates {{x,y}[]} 多边形点坐标的数组,为保证图形能够闭合,起点和终点必须相等。
     *        比如三角形需要四个点表示,第一个点和最后一个点必须相同。
     */
    // 判断点是否点击图形
    judge(dot, coordinates) {
      var x = dot.x,
        y = dot.y;
      var crossNum = 0;
      // 点在线段的左侧数目
      var leftCount = 0;
      // 点在线段的右侧数目
      var rightCount = 0;
      for (var i = 0; i < coordinates.length - 1; i++) {
        var start = coordinates[i];
        var end = coordinates[i + 1];

        // 起点、终点斜率不存在的情况
        if (start.x === end.x) {
          // 因为射线向右水平,此处说明不相交
          if (x > start.x) continue;

          // 从左侧贯穿
          if (end.y > start.y && y >= start.y && y <= end.y) {
            leftCount++;
            crossNum++;
          }
          // 从右侧贯穿
          if (end.y < start.y && y >= end.y && y <= start.y) {
            rightCount++;
            crossNum++;
          }
          continue;
        }
        // 斜率存在的情况,计算斜率
        var k = (end.y - start.y) / (end.x - start.x);
        // 交点的x坐标
        var x0 = (y - start.y) / k + start.x;
        // 因为射线向右水平,此处说明不相交
        if (x > x0) continue;

        if (end.x > start.x && x0 >= start.x && x0 <= end.x) {
          crossNum++;
          if (k >= 0) leftCount++;
          else rightCount++;
        }
        if (end.x < start.x && x0 >= end.x && x0 <= start.x) {
          crossNum++;
          if (k >= 0) rightCount++;
          else leftCount++;
        }
      }
      return leftCount - rightCount !== 0;
    },
    // 判断点是否在直线上
    judgeLine(x1, y1, x2, y2, x, y) {
      var crossProduct = (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1);
      // 如果不等于0,说明不共线,直接返回false
      if (crossProduct !== 0) {
        return false;
      }
      // 否则,检查c点是否在ab线段的范围内
      return (
        Math.min(x1, x2) <= x &&
        x <= Math.max(x1, x2) &&
        Math.min(y1, y2) <= y &&
        y <= Math.max(y1, y2)
      );
    },
    initDraw() {
      //初始化画布对象
      const canvas = document.querySelector("#mycanvas");
      this.ctx = canvas.getContext("2d");
      // this.ctx.strokeStyle = "rgb(0, 195, 155)";
      this.ctx.strokeStyle = "#f36";
    },
    clearAll() {
      console.log("clearAll");
      this.all_coordinates = [];
      this.all_line_coordinates = [];
      this.coordinates = [];
      this.isdraw = false;
      this.canvasId = 1;
      this.$nextTick(() => {
        this.ctx &&
          this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
      });
    },
    // 判断是否在拖拽点
    isdragpoint(x, y) {
      if (
        this.all_coordinates.length == 0 &&
        this.all_line_coordinates.length == 0
      ) {
        return false;
      }
      for (var i = 0; i < this.all_coordinates.length; i++) {
        for (
          var j = 0;
          j < this.all_coordinates[i].points_coordinates.length;
          j++
        ) {
          var px = this.all_coordinates[i].points_coordinates[j].cor_x;
          var py = this.all_coordinates[i].points_coordinates[j].cor_y;
          //   允许偏移量5
          if (Math.abs(x - px) <= 5 && Math.abs(y - py) <= 5) {
            this.drag_index[0] = i;
            this.drag_index[1] = j;
            this.isdragType = 1;
            return true;
          }
        }
      }
      for (var i = 0; i < this.all_line_coordinates.length; i++) {
        for (
          var j = 0;
          j < this.all_line_coordinates[i].points_coordinates.length;
          j++
        ) {
          var px = this.all_line_coordinates[i].points_coordinates[j].cor_x;
          var py = this.all_line_coordinates[i].points_coordinates[j].cor_y;
          //   允许偏移量5
          if (Math.abs(x - px) <= 5 && Math.abs(y - py) <= 5) {
            this.drag_index[0] = i;
            this.drag_index[1] = j;
            this.isdragType = 2;
            return true;
          }
        }
      }
      return false;
    },
    // 鼠标按下事件
    canvasDown(e) {
      var x = e.offsetX;
      var y = e.offsetY;
      // 鼠标右键
      if (e.button == 2) {
        if (!this.beginDraw) {
          return;
        }
        if (this.coordinates.length) {
          var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
          var last_y = this.coordinates[this.coordinates.length - 1].cor_y;
          if (last_x == x && last_y == y) {
            // this.$message.error("不能重复点击");
            // 同一个点 不记录
            this.doubleclick();
            return; //同步放开 二选一
          } else {
            this.coordinates.push({ cor_x: x, cor_y: y });
            this.doubleclick();
            return;
          }
        }
        // 点击顶点
        if (this.isdragpoint(x, y)) {
          // 开启弹窗
          console.log("开启弹窗");
          if (this.isdragType == 1) {
            this.all_coordinates.splice(this.drag_index[0], 1);
          } else {
            this.all_line_coordinates.splice(this.drag_index[0], 1);
          }
          this.drawAll();
          return;
        }
        // 点击图像
        if (this.all_coordinates.length) {
          let dot = { x: x, y: y };
          var flag = false;
          var arr = JSON.parse(
            JSON.stringify(
              this.all_coordinates.map((v) => v.points_coordinates)
            )
          );
          for (var i = 0; i < arr.length; i++) {
            arr[i].push(arr[i][0]);
            arr[i].forEach((item) => {
              (item.x = item.cor_x), (item.y = item.cor_y);
            });
            if (this.judge(dot, arr[i])) {
              flag = true;
              console.log("点击到了多边形上", i);
              this.all_coordinates.splice(i, 1);
              this.drawAll();
              break;
            }
          }
          if (flag) {
            return;
          }
        }
        // 点击直线 人手很难做到
        if (this.all_line_coordinates.length) {
          var flag = false;
          for (var i = 0; i < this.all_line_coordinates.length; i++) {
            for (
              var j = 0;
              j < this.all_line_coordinates[i].points_coordinates.length - 1;
              j++
            ) {
              var x1 = this.all_line_coordinates[i].points_coordinates[j].cor_x;
              var y1 = this.all_line_coordinates[i].points_coordinates[j].cor_y;
              var x2 =
                this.all_line_coordinates[i].points_coordinates[j + 1].cor_x;
              var y2 =
                this.all_line_coordinates[i].points_coordinates[j + 1].cor_y;
              if (this.judgeLine(x1, y1, x2, y2, x, y)) {
                //判断是否点击到了线上
                console.log("点击到了线上", i);
                this.all_line_coordinates.splice(i, 1);
                this.drawAll();
                flag = true;
                break;
              }
            }
          }
          if (flag) {
            return;
          }
        }
      }
      // 鼠标左键
      else if (e.button == 0) {
        if (this.isdragpoint(x, y)) {
          this.isdrag = true; //开启拖拽
          return;
        }

        if (!this.beginDraw) {
          return;
        }
        // 同一个点 不记录
        if (this.coordinates.length) {
          var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
          var last_y = this.coordinates[this.coordinates.length - 1].cor_y;
          if (last_x == x && last_y == y) {
            // this.$message.error("不能重复点击");
            return; //同步放开 二选一
          }
        }

        //获取鼠标按下的坐标,放入数组中

        if (this.drawType == 1) {
          if (this.all_coordinates.length == this.MaxAreaNum) {
            this.$message.error("最多只能画" + this.MaxAreaNum + "个多边形");
            return;
          }
          if (this.coordinates.length + 1 == this.MaxAreaPointsNum) {
            this.$message.error(
              "多边形最多只能画" + this.MaxAreaPointsNum + "个点"
            );
            this.coordinates.push({ cor_x: x, cor_y: y });
            // this.coordinates.push({ cor_x: x, cor_y: y });
            this.doubleclick();
            return;
          }
        } else if (this.drawType == 2) {
          if (this.all_line_coordinates.length == this.MaxLineNum) {
            this.$message.error("最多只能画" + this.MaxLineNum + "条线条");
            return;
          }
          if (this.coordinates.length + 1 == this.MaxLinePointsNum) {
            this.$message.error(
              "线条最多只能画" + this.MaxLinePointsNum + "个点"
            );
            this.coordinates.push({ cor_x: x, cor_y: y });
            // this.coordinates.push({ cor_x: x, cor_y: y });
            this.doubleclick();
            return;
          }
        }
        this.coordinates.push({ cor_x: x, cor_y: y });
        this.isdraw = true; //正在画多边形
      }
    },
    // 鼠标松开事件
    canvasUp(e) {
      // if (!this.beginDraw) {
      //   return;
      // }
      if (this.isdrag) {
        this.isdrag = false; //关闭拖拽点状态
      }
      this.drag_index = [-1, -1];
      this.drawcircle(); //松开画点
    },
    //鼠标移动事件
    canvasMove(e) {
      //没开始画或者结束画之后不进行操作
      var x = e.offsetX;
      var y = e.offsetY;

      const canvas = document.querySelector("#mycanvas");
      if (this.isdragpoint(x, y)) {
        canvas.style.cursor = "pointer";
      } else {
        canvas.style.cursor = "default";
      }

      if (this.isdrag) {
        if (this.isdragType == 1) {
          this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
          this.all_coordinates[this.drag_index[0]].points_coordinates[
            this.drag_index[1]
          ].cor_x = x;
          this.all_coordinates[this.drag_index[0]].points_coordinates[
            this.drag_index[1]
          ].cor_y = y;
          this.drawlines();
          this.drawcircles();
          this.fillarea();
        } else {
          this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
          this.all_line_coordinates[this.drag_index[0]].points_coordinates[
            this.drag_index[1]
          ].cor_x = x;
          this.all_line_coordinates[this.drag_index[0]].points_coordinates[
            this.drag_index[1]
          ].cor_y = y;
          this.drawlines();
          this.drawcircles();
          this.fillarea();
        }
      }

      if (!this.beginDraw) {
        return;
      }

      if (this.coordinates.length == 0 || !this.isdraw) {
        return 0;
      }

      this.drawAll();
      this.drawline(); //把之前的点连线
      this.drawcircle();

      //获取上一个点
      var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
      var last_y = this.coordinates[this.coordinates.length - 1].cor_y;

      if (this.drawType == "2") {
        // 显示箭头
        this.showArrow(last_x, last_y, x, y);
      }

      //获取鼠标移动时的点,画线,实现线段跟踪效果。
      this.ctx.beginPath();
      this.ctx.moveTo(last_x, last_y);
      this.ctx.lineTo(x, y);

      if (this.drawType == "1") {
        // 连接起点 更直观
        var x0 = this.coordinates[0].cor_x;
        var y0 = this.coordinates[0].cor_y;
        this.ctx.lineTo(x0, y0);
      }

      this.ctx.stroke();
      this.ctx.closePath();
    },
    // 鼠标双击事件
    doubleclick() {
      if (!this.beginDraw) {
        return;
      }
      // this.coordinates.pop(); // 同步注释 二选一

      this.isdraw = false;
      if (this.drawType == 1) {
        // 限制图形最少三个点
        if (this.coordinates.length < 3) {
          this.$message.error("绘制区域请至少绘制三个点");
          this.coordinates = [];
          this.drawAll();
          return;
        }
        this.all_coordinates.push({
          canvasId: this.canvasId,
          areaName: "区域" + this.canvasId++,
          points_coordinates: this.coordinates,
        });
      } else {
        // 限制图形最少两个点
        if (this.coordinates.length < 2) {
          this.$message.error("绘制直线请至少绘制两个点");
          this.coordinates = [];
          this.drawAll();
          return;
        }
        var name = "";
        if (this.isMultiLine) {
          name = "区域1";
        } else {
          name = "直线" + this.canvasId++;
        }
        this.all_line_coordinates.push({
          canvasId: this.canvasId,
          // areaName: "直线" + this.canvasId++,
          areaName: name,
          directionType: this.directionType,
          points_coordinates: this.coordinates,
        });
      }
      this.drawAll();
      this.ctx.closePath();
      // console.log(this.coordinates);
      this.coordinates = [];
      // 外部调用智能切换
      if (this.roiType == 3) {
        if (this.drawType == 1 && this.all_line_coordinates.length == 0) {
          this.drawType = 2;
        } else if (this.drawType == 2 && this.all_coordinates.length == 0) {
          this.drawType = 1;
        }
      }
      if (!this.isMultiLine) this.beginDraw = false;
      if (this.isMultiLine && this.all_line_coordinates.length == 2)
        this.beginDraw = false;
    },
    rightClick(e) {
      console.log("右键", e);
    },
    drawlines(data) {
      //把所有多边形画出来
      for (var i = 0; i < this.all_coordinates.length; i++) {
        this.ctx.strokeStyle = "#f36";
        var cors = this.all_coordinates[i].points_coordinates;
        //前后坐标连线
        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();
      }
      //把所有线段画出来
      for (var i = 0; i < this.all_line_coordinates.length; i++) {
        this.ctx.strokeStyle = "#f36";
        // 悬浮上色
        // this.ctx.strokeStyle = this.colorList[(data.canvasId - 1) % 20];
        if (data && this.all_line_coordinates[i].canvasId == data.canvasId) {
          // console.log("线段上色", i, data);
          // this.ctx.strokeStyle = this.colorList[20];
          this.ctx.strokeStyle = "white";
        }
        // 悬浮上色
        var cors = this.all_line_coordinates[i].points_coordinates;
        //前后坐标连线
        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.showArrow(x0, y0, x1, y1);

          this.ctx.moveTo(x0, y0);
          this.ctx.lineTo(x1, y1);
          this.ctx.stroke();
          this.ctx.closePath();
        }
      }
    },
    drawline() {
      this.ctx.strokeStyle = "#f36";
      //把当前绘制的多边形之前的坐标线段绘制出来
      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;

        if (this.drawType == 2) {
          this.showArrow(x0, y0, x1, y1);
        }

        this.ctx.moveTo(x0, y0);
        this.ctx.lineTo(x1, y1);
        this.ctx.stroke();
        this.ctx.closePath();
      }
    },
    drawcircle() {
      //为当前的多边形端点画圆
      this.ctx.fillStyle = "rgb(0, 195, 155)";
      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, 5, 0, Math.PI * 2);
        this.ctx.fill();
        this.ctx.closePath();
      }
    },
    drawcircles() {
      //为所有的多边形端点画圆
      this.ctx.fillStyle = "rgb(0, 195, 155)";
      for (var i = 0; i < this.all_coordinates.length; i++) {
        var cors = this.all_coordinates[i].points_coordinates;
        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, 5, 0, Math.PI * 2);
          this.ctx.fill();
          this.ctx.closePath();
        }
      }
      for (var i = 0; i < this.all_line_coordinates.length; i++) {
        var cors = this.all_line_coordinates[i].points_coordinates;
        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, 5, 0, Math.PI * 2);
          this.ctx.fill();
          this.ctx.closePath();
        }
      }
    },
    fillarea(data) {
      // this.ctx.fillStyle = "rgba(0, 195, 155,0.4)";
      for (var i = 0; i < this.all_coordinates.length; i++) {
        this.ctx.fillStyle = "rgba(16, 128, 219, 0.4)";
        // 悬浮上色
        // this.ctx.fillStyle = this.colorList[(data.canvasId - 1) % 20];
        if (data && this.all_coordinates[i].canvasId == data.canvasId) {
          this.ctx.fillStyle = this.colorList[20];
        }
        // 悬浮上色
        var cors = this.all_coordinates[i].points_coordinates;
        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();
      }
    },
    /*
        ctx:Canvas绘图环境
        fromX, fromY:起点坐标(也可以换成p1,只不过它是一个数组)
        toX, toY:终点坐标 (也可以换成p2,只不过它是一个数组)
        theta:三角斜边一直线夹角
        headlen:三角斜边长度
        width:箭头线宽度
        color:箭头颜色
      */
    // 绘制箭头
    drawArrow(ctx, fromX, fromY, toX, toY, theta, headlen, width, color) {
      theta = typeof theta != "undefined" ? theta : 30;
      headlen = typeof theta != "undefined" ? headlen : 10;
      width = typeof width != "undefined" ? width : 1;
      color = typeof color != "color" ? color : "#000";

      // 计算各角度和对应的P2,P3坐标
      var angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
        angle1 = ((angle + theta) * Math.PI) / 180,
        angle2 = ((angle - theta) * Math.PI) / 180,
        topX = headlen * Math.cos(angle1),
        topY = headlen * Math.sin(angle1),
        botX = headlen * Math.cos(angle2),
        botY = headlen * Math.sin(angle2);

      // ctx.save();
      ctx.beginPath();

      var arrowX = fromX - topX,
        arrowY = fromY - topY;

      ctx.moveTo(arrowX, arrowY);
      ctx.moveTo(fromX, fromY);
      ctx.lineTo(toX, toY);
      arrowX = toX + topX;
      arrowY = toY + topY;
      ctx.moveTo(arrowX, arrowY);
      ctx.lineTo(toX, toY);
      arrowX = toX + botX;
      arrowY = toY + botY;
      ctx.lineTo(arrowX, arrowY);
      // ctx.strokeStyle = color;
      ctx.lineWidth = width;
      ctx.stroke();
      // ctx.restore();
    },
  },
};
</script>

<style lang="scss" scoped>
#mycanvas {
  /* border: 1px solid red; */
  // background-color: #2d303b;
}
.staticLabels {
  flex-grow: 1;
  overflow: auto;
  margin-top: 10px;
  background: rgba(247, 250, 255, 0.8);
  border-radius: 2px;
  border: 1px solid #eaecf6;
  padding-left: 5px;
  font-size: 12px;
  line-height: 24px;
  .staticLabels_item {
    float: left;
    height: 24px;
    background: #ecf3fe;
    border-radius: 2px;
    border: 1px solid #2c7be5;
    padding: 0px 4px;
    margin-right: 6px;
    margin-top: 5px;
    user-select: none;
  }
}
</style>

使用

<template>
  <div>
    <Canvas
      ref="drawArea"
      :canvasKey="canvasKey"
      :myDrawList="myDrawList"
      :myRoiType="myRoiType"
    />
    <el-select v-model="canvasKey" style="width: 100px" size="mini">
      <el-option value="first" label="数据1"></el-option>
      <el-option value="second" label="数据2"></el-option>
      <el-option value="third" label="数据3"></el-option>
    </el-select>
    <el-select
      v-model="myRoiType"
      style="width: 100px; margin: 10px"
      size="mini"
    >
      <el-option value="1" label="区域"></el-option>
      <el-option value="2" label="线条"></el-option>
      <el-option value="3" label="区域加线条"></el-option>
    </el-select>
    <el-button type="primary" @click="saveFunc" size="mini">保存</el-button>
  </div>
</template>

<script>
import Cookies from "js-cookie";
import Canvas from "./canvas.vue";
export default {
  data() {
    return {
      canvasKey: "first",
      myDrawList: [],
      myRoiType: "1",
    };
  },

  components: { Canvas },
  created() {
    this.myDrawList = JSON.parse(Cookies.get("myDrawList") || "[]");
    console.log("取值", this.myDrawList);
  },
  methods: {
    saveFunc() {
      var arr = this.$refs.drawArea.getData();
      Cookies.set("myDrawList", JSON.stringify(arr));
      console.log("保存", arr);
    },
  },
};
</script>
<style scoped></style>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到