利用Fabirc.js画封闭多边形

引言

  • 最近接到一个需求,要求在电脑端画电子围栏,其实质就是画封闭的多边形,而且不能相交。经过多方查找,找到了一个canvas的库Fabirc.js画多边形
  • 参考Fabirc.js中文文档
  • Fabirc.js博客参考

效果演示

在这里插入图片描述

代码

<!--  -->
<template>
  <div class="canvasbox">
    <canvas id="label-canvas" class="canvas" width="500" height="500"></canvas>
  </div>
</template>

<script>
import { fabric } from "fabric";
export default {
  components: {},
  data() {
    return {
      fabricObj: null,
      drawingObject: {
        type: "",
        background: "",
        border: "",
      },
      mouseFrom: {},
      roofPoints: [],
      lines: [],
      lines1: [],
      lines2: [],
      lineCounter: 0,
      roof: null,
      firstx: "",
      firsty: "",
      flag1: null,
      flag2: false,
      flag3: false,
    };
  },
  computed: {},
  watch: {},
  methods: {
    start() {
      this.fabricObj = new fabric.Canvas("label-canvas");
      if (this.drawingObject.type == "roof") {
      } else {
        this.drawingObject.type = "roof";
      }
      this.fabricEvent();
    },
    fabricEvent() {
      this.fabricObj.on({
        "mouse:down": (e) => {
          if (this.drawingObject.type == "roof") {
            this.lines2 = [];
            this.mouseFrom.x = e.pointer.x;
            this.mouseFrom.y = e.pointer.y;
            if (!this.lineCounter) {
              this.firstx = e.pointer.x;
              this.firsty = e.pointer.y;
            }
            let a = {};
            a["x"] = this.mouseFrom.x;
            a["y"] = this.mouseFrom.y;
            this.roofPoints.push(a);
            var points = [a.x, a.y, a.x, a.y];
            var points1 = [this.firstx, this.firsty, a.x, a.y];

            this.lines.push(
              new fabric.Line(points, {
                strokeWidth: 3,
                selectable: false,
                stroke: "red",
              })
            );

            this.lines1.push(
              new fabric.Line(points1, {
                strokeWidth: 3,
                selectable: false,
                stroke: "yellow",
              })
            );
            this.lines2.push(
              new fabric.Line(points1, {
                strokeWidth: 3,
                selectable: false,
                stroke: "yellow",
              })
            );
            if (this.lineCounter > 1) {
              var num = this.lines.length - 2;
              // console.log(this.lines);
              // console.log(this.lines[num]);
              this.flag1 = this.lines.some((v) => {
                return this.isIntersect(v, this.lines[num]);
              });
              this.flag3 = this.lines.some((v) => {
                return this.isIntersect(v, this.lines2[0]);
              });
              if (this.flag1 || this.flag3) {
                // this.fabricObj.off("mouse:dblclick");
                this.lineCounter--;
                this.flag1 = false;
                this.roofPoints.splice(this.roofPoints.length - 1, 1);
                this.fabricObj.remove(this.lines[num]);

                this.lines.splice(num, 2);
                var point2 = [
                  this.lines[this.lines.length - 1].x2,
                  this.lines[this.lines.length - 1].y2,
                  this.lines[this.lines.length - 1].x2,
                  this.lines[this.lines.length - 1].y2,
                ];
                this.lines.push(
                  new fabric.Line(point2, {
                    strokeWidth: 3,
                    selectable: false,
                    stroke: "red",
                  })
                );
                this.lines1[0].set({
                  x2: this.lines[this.lines.length - 1].x2,
                  y2: this.lines[this.lines.length - 1].y2,
                });
              } else {
                // this.fabricObj.on("mouse:dblclick");
              }
              let ret = this.lines1.splice(0, 1);
              this.lines1 = [];
              this.lines1.push(...ret);

              this.fabricObj.add(this.lines[this.lineCounter]);
            } else {
              if (this.lineCounter != 1) {
                this.fabricObj.add(
                  this.lines[this.lineCounter],
                  this.lines1[this.lineCounter]
                );
              } else {
                this.fabricObj.add(this.lines[this.lineCounter]);
              }
            }

            this.lineCounter++;
          }
        },
        "mouse:move": (e) => {
          if (
            this.lines[0] !== null &&
            this.lines[0] !== undefined &&
            this.drawingObject.type == "roof"
          ) {
            this.x = e.pointer.x;
            this.y = e.pointer.y;
            this.lines[this.lineCounter - 1].set({
              x2: this.x,
              y2: this.y,
            });

            this.lines1[0].set({
              x2: this.x,
              y2: this.y,
            });
            this.fabricObj.renderAll();
          }
        },
        "mouse:dblclick": (e) => {
          this.fabricObj.off("mouse:down");

          this.lines.forEach((item) => this.fabricObj.remove(item));
          this.lines1.forEach((item) => this.fabricObj.remove(item));
          this.makeRoof();
          this.fabricObj.add(this.roof);
          this.fabricObj.renderAll();

          this.roofPoints = [];
          this.lines = [];
          this.lines1 = [];
          this.lineCounter = 0;
        },
      });
    },
    makeRoof() {
      let left = this.findLeftPaddingForRoof(this.roofPoints);
      let top = this.findTopPaddingForRoof(this.roofPoints);
      let a = {};
      a["x"] = this.roofPoints[0].x;
      a["y"] = this.roofPoints[0].y;
      this.roofPoints.push(a);
      this.roofPoints.some((v, index) => {
        if (this.lines[this.lines.length - 1].aCoords.bl.x == v.x) {
          this.roofPoints.splice(index, 1);
        }
      });
      this.roof = new fabric.Polyline(this.roofPoints, {
        fill: "rgba(0,0,0,1)",
        strokeWidth: 5,
        stroke: "red",
        left: left,
        top: top,
      });
    },
    findTopPaddingForRoof(roofPoints) {
      var result = 999999;
      for (var f = 0; f < this.lineCounter; f++) {
        if (roofPoints[f].y < result) {
          result = roofPoints[f].y;
        }
      }
      return Math.abs(result);
    },
    findLeftPaddingForRoof(roofPoints) {
      var result = 999999;
      for (var i = 0; i < this.lineCounter; i++) {
        if (roofPoints[i].x < result) {
          result = roofPoints[i].x;
        }
      }
      return Math.abs(result);
    },
    isIntersect(line1, line2) {
      // 转换成一般式: Ax+By = C
      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))
        ) {
          return true;
        }
      }

      return false;
    },
    //如果b在a和c之间,返回true
    //当a==b或者b==c时排除结果,返回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);
    },
  },
  created() {},
  mounted() {
    this.start();
  },
};
</script>
<style lang='less' scoped>
.canvas {
  // border: 1px solid black;
  // box-sizing: content-box;
  background-color: blue;
}
.canvasbox/deep/ .canvas-container {
  margin: auto;
}
</style>


1. 当鼠标按下,画一条线(实际是个点),初始和终止的点坐标一样,当鼠标移动的时候改变末位置的坐标,即可实现鼠标跟随划线的操作,每点一下存一条线在数组里。
2. 当鼠标按下,用另外一个数组存起来画封闭图形的另外一条线,就是图中的黄线,只记住初始位置,末位置随着点击位置的变动而变动。
3. 判断相交的时候是参考—js判断两直线相交
4. 自从画第三条线开始,之后每画一条线,新画的红线和黄线都要和之前的红线判断是否相交,如果相交,就返回上一步的操作

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值