SOURCE

console 命令行工具 X clear

                    
>
console
var demo = new Vue({
  'el': '#demo',
  components: {},
  data: {
     
      rectType: 1,
      rectList: [],
      myCanvas: null,
      cxt: null,
      mouseInit: null,
      selectedRect: null,
      changeRect: null,
      nowBorderRect: null,
      selectedId: null,
      moved: false,
      mouse: false,
      nowId: null,
      opacity: 0.2,
      color1: "#0f0",
      color2: "#f00",
      circlsRadius: 5,
      lineWidth: 2,
      min: 50,
      minAngle: 10 //最小角度
    
  },
  watch: {},
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.$nextTick(() => {
        this.myCanvas = this.$refs.myCanvas;
        this.cxt = this.myCanvas.getContext("2d");
        this.myCanvas.onmousedown = e => {
          this.onMouseDown(e);
        };
        this.myCanvas.onmouseup = e => {
          // this.onMouseUp(e);
        };
        this.myCanvas.onmousemove = e => {
          this.onMouseMove(e);
        };
        window.addEventListener("mouseup", this.onMouseUp, false);
      });
    },
    throttle(fn, time = 10) {
      let timer = null;
      return (...args) => {
        if (!timer) {
          timer = setTimeout(() => {
            fn.apply(null, args);
            timer = null;
          }, time);
        }
      };
    },
    //鼠标按下事件
    onMouseDown(e) {
      this.mouseInit = {
        x: e.offsetX,
        y: e.offsetY
      };

      let nowRect = null;
      let changeRect = null;
      if (this.isrCircleIn(this.mouseInit).rect) {
        this.changeRect = this.isrCircleIn(this.mouseInit).rect;
      } else {
        this.rectList.forEach(item => {
          if (this.pointInPoly(this.mouseInit, item.points)) {
            nowRect = item;
          }
        });
        if (nowRect) {
          this.selectedId = nowRect.id;
          this.selectedRect = nowRect;

          this.nowId = null;
        } else {
          this.nowId = this.rectList.length;
          this.selectedId = null;
          this.nowBorderRect=null
          this.mouse = true;
        }
        this.drawRect();

        if (this.selectedId || this.selectedId === 0) {
          this.moved = true;
        } else {
          this.moved = false;
        }
      }
    },
    //鼠标移动事件
    async onMouseMove(e) {
      //判断鼠标位置
      let pt = {
        x: e.offsetX,
        y: e.offsetY
      };
      await this.throttle(this.isRectIn)(pt);
      //新建矩形
      if (this.mouse) {
        this.rectList[this.nowId] = await this.setRectPoints(e);
        this.selectedRect = this.rectList[this.nowId];
        this.drawRect();
      }
      //移动四边形

      if (this.moved) {
        this.moveRect(e);
      }
      //改变多边形形状
      if (this.changeRect) {
        this.moveCircle(
          e,
          this.changeRect.id,
          this.isrCircleIn(this.mouseInit).index
        );
      }
    },
    //鼠标抬起事件
    onMouseUp(e) {
      this.moved = false;
      this.mouse = false;
      this.nowId = null;
      this.selectedId = null;
      this.selectedRect = null;
      this.changeRect = null;
      this.mouseInit = null;
      if (this.nowBorderRect) {
        this.isMin(this.nowBorderRect);
      }
    },

    //画带边框的四边形
    borderRect(rect) {
      this.cxt.beginPath();
      this.cxt.moveTo(rect.points[0].x, rect.points[0].y);
      rect.points.map(i => {
        this.cxt.lineTo(i.x, i.y);
      });
      this.cxt.strokeStyle = rect.type === 2 ? this.color2 : this.color1;
      this.cxt.lineWidth = this.lineWidth;
      this.cxt.globalAlpha = 1;
      this.cxt.closePath();
      this.cxt.stroke();
      this.borderCircle(rect);
    },
    //画带边框的圆
    borderCircle(rect) {
      rect.points.map(i => {
        this.cxt.beginPath();
        this.cxt.arc(i.x, i.y, this.circlsRadius, 2 * Math.PI, 0);
        this.cxt.fillStyle = "#fff";
        this.cxt.fill();
        this.cxt.strokeStyle = rect.type === 2 ? this.color2 : this.color1;
        this.cxt.lineWidth = this.lineWidth;
        this.cxt.globalAlpha = 1;
        this.cxt.closePath();
        this.cxt.stroke();
      });
      this.nowBorderRect = rect;
    },

    //画矩形
    async drawRect() {
      this.cxt.clearRect(0, 0, this.myCanvas.width, this.myCanvas.height);

      this.rectList.map(item => {
        this.cxt.beginPath();
        this.cxt.fillStyle = item.type === 2 ? this.color2 : this.color1;
        this.cxt.globalAlpha = this.opacity;
        this.cxt.moveTo(item.points[0].x, item.points[0].y);
        item.points.forEach(i => {
          this.cxt.lineTo(i.x, i.y);
        });
        this.cxt.closePath();
        this.cxt.fill();
      });

      if (this.selectedRect || this.changeRect || this.nowBorderRect) {
        let rect = this.selectedRect || this.changeRect || this.nowBorderRect;
        this.borderRect(rect);
      } 
    },
    //移动四边形,
    moveRect(e) {
      this.rectList.map(item => {
        if (item.id === this.selectedId) {
          let movex = e.offsetX - this.mouseInit.x;
          let movey = e.offsetY - this.mouseInit.y;
          this.mouseInit.x = e.offsetX;
          this.mouseInit.y = e.offsetY;
          item.points.map(i => {
            i.x += movex;
            i.y += movey;
          });
          this.selectedRect = item;
        }
      });
      this.drawRect();
    },
    //拖拽点改变多边形形状
    moveCircle(e, id, index) {
      let movex = e.offsetX - this.mouseInit.x;
      let movey = e.offsetY - this.mouseInit.y;
      this.mouseInit.x = e.offsetX;
      this.mouseInit.y = e.offsetY;
      this.rectList[id].points[index].x += movex;
      this.rectList[id].points[index].y += movey;

      this.drawRect();
    },

    //计算鼠标是否在多边形里,参数:点,多边形数组
    pointInPoly(pt, poly) {
      for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) {
        ((poly[i].y <= pt.y && pt.y < poly[j].y) ||
          (poly[j].y <= pt.y && pt.y < poly[i].y)) &&
          pt.x <
            ((poly[j].x - poly[i].x) * (pt.y - poly[i].y)) /
              (poly[j].y - poly[i].y) +
              poly[i].x &&
          (c = !c);
      }
      return c;
    },
    //判断鼠标是否圆内
    isrCircleIn(pt) {
      let t = false;
      let rect = null;
      let index = null;
      this.rectList.forEach(item => {
        item.points.forEach((i, idx) => {
          let line = Math.abs(
            Math.sqrt(Math.pow(i.x - pt.x, 2) + Math.pow(i.y - pt.y, 2))
          );
          if (line - this.circlsRadius < 0) {
            t = true;
            rect = item;
            index = idx;
          }
        });
      });

      return {
        t: t,
        rect: rect,
        index: index
      };
    },
    //判断鼠标位置改变鼠标形状
    isRectIn(pt) {
      if (this.rectList.length > 0) {
        //判断是否在圆内
        if (this.isrCircleIn(pt).t) {
          this.myCanvas.style.cursor = "pointer";
        } else if (
          this.rectList.find(item => this.pointInPoly(pt, item.points))
        ) {
          this.myCanvas.style.cursor = "move";
        } else {
          this.myCanvas.style.cursor = "default";
        }
      }
    },
    //判断是否是最小
    isMin(rect) {
      if (rect) {
        let line1 = Math.abs(
          Math.sqrt(
            Math.pow(rect.points[0].x - rect.points[2].x, 2) +
              Math.pow(rect.points[0].y - rect.points[2].y, 2)
          )
        );
        let line2 = Math.abs(
          Math.sqrt(
            Math.pow(rect.points[1].x - rect.points[3].x, 2) +
              Math.pow(rect.points[1].y - rect.points[3].y, 2)
          )
        );
        if (line1 < this.min || line2 < this.min) {
          alert("框选区域太小");
          this.deleteRect(rect.id);
        } else if (this.isRuleRect(rect)) {
          alert("框选区域不合法请重新绘制");
          this.deleteRect(rect.id);
        } else {
          this.isCanvasIn();
        }
      }
    },
    //判断区域是否合法
    //合法区域(四边形的每个角都大于minAngle且是凸多边形)
    isRuleRect(rect) {
      let A = rect.points[0];
      let B = rect.points[1];
      let C = rect.points[2];
      let D = rect.points[3];
      let AB = Math.sqrt(
        Math.pow(A.x - B.x, 2) + Math.pow(A.y - B.y, 2)
      ).toFixed(2);
      let BC = Math.sqrt(
        Math.pow(B.x - C.x, 2) + Math.pow(B.y - C.y, 2)
      ).toFixed(2);
      let AD = Math.sqrt(
        Math.pow(A.x - D.x, 2) + Math.pow(A.y - D.y, 2)
      ).toFixed(2);
      let CD = Math.sqrt(
        Math.pow(C.x - D.x, 2) + Math.pow(C.y - D.y, 2)
      ).toFixed(2);
      let BD = Math.sqrt(
        Math.pow(B.x - D.x, 2) + Math.pow(B.y - D.y, 2)
      ).toFixed(2);
      let AC = Math.sqrt(
        Math.pow(A.x - C.x, 2) + Math.pow(A.y - C.y, 2)
      ).toFixed(2);

      let cosA =
        (Math.pow(AB, 2) + Math.pow(AD, 2) - Math.pow(BD, 2)) / (2 * AB * AD);

      let cosB =
        (Math.pow(AB, 2) + Math.pow(BC, 2) - Math.pow(AC, 2)) / (2 * AB * BC);
      let cosC =
        (Math.pow(BC, 2) + Math.pow(CD, 2) - Math.pow(BD, 2)) / (2 * BC * CD);
      let cosD =
        (Math.pow(AD, 2) + Math.pow(CD, 2) - Math.pow(AC, 2)) / (2 * AD * CD);
      let angleA = Math.round((Math.acos(cosA) * 180) / Math.PI);
      let angleB = Math.round((Math.acos(cosB) * 180) / Math.PI);
      let angleC = Math.round((Math.acos(cosC) * 180) / Math.PI);
      let angleD = Math.round((Math.acos(cosD) * 180) / Math.PI);

      return (
        [angleA, angleB, angleC, angleD].find(item => item < this.minAngle) ||
        this.convex(rect.points, 4) === -1
      );
    },
    //删除矩形
    deleteRect(id) {
      this.selectedId = null;
      this.selectedRect = null;
      this.changeRect = null;
      this.nowBorderRect = null;
      this.mouseInit = null;
      let arr = this.rectList.filter(item => item.id !== id);
      if (arr && arr.length > 0) {
        this.rectList = arr.map((item, index) => {
          return {
            id: index,
            type: item.type,
            points: item.points
          };
        });
      } else {
        this.rectList = [];
      }

      this.drawRect();
    },
    //判断是否在canvas内
    isCanvasIn() {
      this.rectList.map(item => {
        item.points.map(i => {
          if (i.x < 0) {
            i.x = 0;
          }
          if (i.x > this.myCanvas.width) {
            i.x = this.myCanvas.width;
          }
          if (i.y < 0) {
            i.y = 0;
          }
          if (i.y > this.myCanvas.height) {
            i.y = this.myCanvas.height;
          }
        });
      });
      this.drawRect();
    },
    //--------------判断是否是凸多边形函数-------------------------
    // p:顶点数组(数组对象) n:顶点个数;1:凸集;-1:凹集;0:曲线不符合要求无法计算
    convex(p, n) {
      var j, k, z;
      var flag = 0;
      if (n < 3) {
        return 0;
      }
      for (var i = 0; i < n; i++) {
        j = (i + 1) % n;
        k = (i + 2) % n;
        z = (p[j].x - p[i].x) * (p[k].y - p[j].y);
        z -= (p[j].y - p[i].y) * (p[k].x - p[j].x);
        if (z < 0) {
          flag |= 1;
        } else if (z > 0) {
          flag |= 2;
        }
        if (flag == 3) {
          // console.log("凹多边形,不符合要求")
          return -1; //CONCAVE
        }
      }
      if (flag != 0) {
        // console.log("凸多边形")
        return 1; //CONVEX
      } else {
        return 0;
      }
    },
    //判断怎么画矩形
    setRectPoints(e) {
      if (this.mouseInit.x <= e.offsetX && this.mouseInit.y <= e.offsetY) {
        //右下画矩形
        return {
          id: this.nowId,
          type: this.rectType,
          points: [
            {
              x: this.mouseInit.x,
              y: this.mouseInit.y,
              isSelected: false
            },
            {
              x: e.offsetX,
              y: this.mouseInit.y,
              isSelected: false
            },
            {
              x: e.offsetX,
              y: e.offsetY,
              isSelected: false
            },
            {
              x: this.mouseInit.x,
              y: e.offsetY,
              isSelected: false
            }
          ]
        };
      } else if (
        this.mouseInit.x >= e.offsetX &&
        this.mouseInit.y >= e.offsetY
      ) {
        //左上画矩形
        return {
          id: this.nowId,
          type: this.rectType,
          points: [
            {
              x: e.offsetX,
              y: e.offsetY,
              isSelected: false
            },
            {
              x: this.mouseInit.x,
              y: e.offsetY,
              isSelected: false
            },
            {
              x: this.mouseInit.x,
              y: this.mouseInit.y,
              isSelected: false
            },
            {
              x: e.offsetX,
              y: this.mouseInit.y,
              isSelected: false
            }
          ]
        };
      } else if (
        this.mouseInit.x >= e.offsetX &&
        this.mouseInit.y <= e.offsetY
      ) {
        //左下画矩形
        return {
          id: this.nowId,
          type: this.rectType,
          points: [
            {
              x: e.offsetX,
              y: this.mouseInit.y,
              isSelected: false
            },
            {
              x: this.mouseInit.x,
              y: this.mouseInit.y,
              isSelected: false
            },
            {
              x: this.mouseInit.x,
              y: e.offsetY,
              isSelected: false
            },
            {
              x: e.offsetX,
              y: e.offsetY,
              isSelected: false
            }
          ]
        };
      } else {
        //右上画矩形
        return {
          id: this.nowId,
          type: this.rectType,
          points: [
            {
              x: this.mouseInit.x,
              y: e.offsetY,
              isSelected: false
            },
            {
              x: e.offsetX,
              y: e.offsetY,
              isSelected: false
            },
            {
              x: e.offsetX,
              y: this.mouseInit.y,
              isSelected: false
            },
            {
              x: this.mouseInit.x,
              y: this.mouseInit.y,
              isSelected: false
            }
          ]
        };
      }
    }
  },
  beforeDestroy() {
    window.removeEventListener("mouseup", this.onMouseUp);
    delete this.onMouseUp;
  }
});
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui@2.0.11/lib/index.js"></script>
<div id="demo" class="canvas-demo">
    <div class="top-box">
      <el-radio-group v-model="rectType">
        <el-radio :label="1">绿色框</el-radio>
        <el-radio :label="2">红色框</el-radio>
      </el-radio-group>
    </div>
    <div class="draw-box">
      <canvas ref="myCanvas" width="640" height="480"></canvas>
    </div>
    <div class="btn-box">
      <el-button type="primary" size="mini" @click="deleteRect(nowBorderRect.id)">删除选中</el-button>
    </div>
</div>
@import url("//unpkg.com/element-ui@2.0.11/lib/theme-chalk/index.css");
.canvas-demo{
    width:100%;
}
.canvas-demo .draw-box {
    display: flex;
    justify-content: center;
  }
 .canvas-demo canvas {
    background-color: #e1e1e1;
  }
  .canvas-demo .top-box {
    display: flex;
    justify-content: center;
    margin: 10px auto;
  }
 .canvas-demo .btn-box {
    margin: 20px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
  }



本项目引用的自定义外部资源