Skip to content

自定义图形

自定义绘制图形,有2种方式,一种是滑动绘制,一种是点击绘制的

怎么自定义

ts
import CanvasMarkBoard from "canvas-mark-board";
const { MoveMarkObject, MarkBoardUtils } = CanvasMarkBoard;
class XXXXXObject extends MoveMarkObject {}
new CanvasMarkBoard().register('xxxx',XXXXXObject)

具体可以参考查看react在线演示源码

下面是2种模式具体api和demo

点击绘制

tsx
import CanvasMarkBoard from "canvas-mark-board";
const { ClickMarkObject, MarkBoardUtils } = CanvasMarkBoard;
class XXXObject extends ClickMarkObject {
  constructor(box) {
    super(box);
    // 图形名称
    this.type = "sides_arrow"
    // 是否闭合
    this.isClosed = true
    // 最大点数
    this.maxPointCount = 3
    // 最小点数
    this.minPointCount = 2
  }
  /** 绘制的图形path */
  get pathData() {}
  /** 获取结果点 */
  get resultPoints() {}
  /** 获取index点 */
  get indexPoint() {}
  /** 渲染 */
  render(){}
  /** 判断点是否在图形内部 */
  isPointInside(point) {}
  /** 鼠标和其他操作,一般不用写,内部已经判断好 */
  boxMousemove(point){}
  boxMouseup(point){}
  boxMousedown(point){}
  destory() {}
  removeAll() {}
  async complete() {}
  static import(){}
}
tsx
/** 自定义点用到的方法 */
interface IPointData {
  x: number;
  y: number;
}
const getDistance = (point: IPointData, point2: IPointData): number => {
  const s = Math.sqrt(
    Math.pow(point.x - point2.x, 2) + Math.pow(point.y - point2.y, 2)
  );
  return s;
};
class Circle {
  config: any;
  constructor(config: any) {
    this.config = config;
  }
  public get __tag() {
    return "Circle";
  }
  public draw() {
    const { center, radius, fillColor = "black", ctx } = this.config;
    const { x, y } = center;
    ctx.save();
    ctx.beginPath();
    ctx.fillStyle = fillColor;
    ctx.arc(x, y, radius, 0, Math.PI * 2);
    ctx.fill();
    ctx.closePath();
    ctx.restore();
  }
}
/** 自定义点 */
class MarkDotObject extends ClickMarkObject {
  constructor(box: any) {
    super(box);
    this.type = "dot" as any;
    this.minPointCount = 1;
    this.maxPointCount = 1;
  }
  /** 判断点是否在多边形内部 */
  isPointInside(point: IPointData): boolean {
    let expand = this.expent / this.box.initLayout.zoom;
    // 勾股定理
    if (!this.pointList.length || this.status === "draw") return false;
    let distanceFromCenter = Math.sqrt(
      Math.pow(this.pointList[0].x - point.x, 2) +
        Math.pow(this.pointList[0].y - point.y, 2)
    );
    let distance = 4;
    return distanceFromCenter <= distance + expand && distance !== 0;
  }
  /** 渲染 */
  render() {
    this.removeAll();
    let {
      config,
      regionCtx: ctx,
      t: { a: zoom },
    } = this.box;
    if (!this.box.selectObject) {
      this.box.clearCanvas(ctx);
    }
    if (this.status === "edit") {
      this.box.clearCanvas(ctx);
      ctx.fillStyle =
        this.status === "edit" ? config.fillColor : "rgba(0,0,0,0)";
      ctx.fill(new Path2D(this.pathData));
      this.pointList.map((item, index) => {
        let circle = null;
        if (this.acctivePointIndex === index) {
          circle = new Circle({
            ctx,
            center: item,
            radius: 8 / zoom,
            fillColor: this.color!,
          });
        } else {
          circle = new Circle({
            ctx,
            center: item,
            radius: 6 / zoom,
            fillColor: this.color!,
          });
        }
        circle.draw();
      });
    }
  }
  get pathData(): string {
    let path = ``;
    if (this.pointList.length) {
      let point1 = {
        x: this.pointList[0].x - 4,
        y: this.pointList[0].y,
      };
      let point2 = {
        x: this.pointList[0].x + 4,
        y: this.pointList[0].y,
      };

      let distance = getDistance(this.pointList[0], point1);
      path += `
          M ${point1.x} ${point1.y} 
          A ${distance} ${distance} 0 0 1 ${point2.x} ${point2.y}
          A ${distance} ${distance} 0 0 1 ${point1.x} ${point1.y}
        `;
      path += `Z `;
    }
    return path;
  }
}
tsx
/** 自定义多线段箭头 */
class MarkPolylineArrowObject extends ClickMarkObject {
  constructor(box: any) {
    super(box);
    this.type = "polyline_arrow" as any;
  }
  /** 获取path  */
  get pathData() {
    let path = ``;
    if (this.pointList.length) {
      this.pointList.forEach((point, index) => {
        if (index === 0) {
          path += `M${point.x},${point.y}`;
        } else {
          path += `L${point.x},${point.y}`;
        }
      });
      if (this.pointList.length >= 2) {
        for (let i = 0; i < this.pointList.length - 1; i++) {
          const [arrow1, arrow2] = MarkBoardUtils.getArrow(
            this.pointList[i],
            this.pointList[i + 1],
            20 / this.box.t.a
          );
          path += `M${arrow1.x},${arrow1.y}`;
          path += `L${this.pointList[i + 1].x},${this.pointList[i + 1].y}`;
          path += `M${this.pointList[i + 1].x},${this.pointList[i + 1].y}`;
          path += `L${arrow2.x},${arrow2.y}`;
          path += `M${this.pointList[i + 1].x},${this.pointList[i + 1].y}`;
        }
      }
    }
    return path;
  }
}

滑动绘制

ts
import CanvasMarkBoard from "canvas-mark-board";
const { MoveMarkObject, MarkBoardUtils } = CanvasMarkBoard;
class XXXXXObject extends MoveMarkObject {
  constructor(box: any) {
    super(box);
    // 类型
    this.type = "triangle" as any;
    // 低于多少尺寸不绘制
    this.completeOffset = 30 
  }
  /** move到操作点时设置鼠标样式 */
  setCursor(){}
  /** 编辑move时设置形状操作 */
  setMoveEdit(_offset?: any) {}
  /** 获取绘制的path */
  get pathData(){}
  /** 获取坐标点 */
  get vertexList(){}
  /** 获取导出结果点 */
  get resultPoints(){}
  /** 获取index点 */
  get indexPoint(){}
  /** 渲染 */
  render(){}
  /** 判断点是否在多边形内部 */
  isPointInside(){}
   /** 鼠标和其他操作,一般不用写,内部已经判断好 */
  boxMousemove(point){}
  boxMouseup(point){}
  boxMousedown(point){}
  destory() {}
  removeAll() {}
  async complete() {}
  static import(){}

}
ts
/** 自定义三角形 */
class MarkTriangleObject extends MoveMarkObject {
  constructor(box: any) {
    super(box);
    this.type = "triangle" as any;
  }
  setMoveEdit(offset: any): void {
    if (this.acctivePointIndex == 0) {
      this.pointList[0] = {
        x: this.pointList[0].x + offset.x,
        y: this.pointList[0].y + offset.y,
      };
    } else if (this.acctivePointIndex === 1) {
      this.pointList[1] = {
        x: this.pointList[1].x + offset.x,
        y: this.pointList[1].y + offset.y,
      };
    } else if (this.acctivePointIndex === 2) {
      this.pointList[0] = {
        x: this.pointList[0].x + offset.x,
        y: this.pointList[0].y,
      };
      this.pointList[1] = {
        x: this.pointList[1].x,
        y: this.pointList[1].y + offset.y,
      };
    }
  }
  /** 获取path  */
  get pathData() {
    let path = ``;
    if (this.vertexList.length) {
      this.vertexList.forEach((point, index) => {
        // 绘制线段
        if (index === 0) {
          path += `M${point.x},${point.y}`;
        } else {
          path += `L${point.x},${point.y}`;
        }
      });
      path += `Z `;
    }
    return path;
  }
  /** 获取三角形顶点 */
  get vertexList() {
    if (this.pointList.length === 2) {
      return [
        {
          x: (this.pointList[0].x + this.pointList[1].x) / 2,
          y: this.pointList[0].y,
        },
        this.pointList[1],
        { x: this.pointList[0].x, y: this.pointList[1].y },
      ];
    } else {
      return [];
    }
  }
  get indexPoint() {
    return this.vertexList[0];
  }
  /** 获取三角形结果点 */
  get resultPoints() {
    return this.vertexList;
  }
}
ts
/** 自定义带方向的线 */
class MarkSidesArrowObject extends MoveMarkObject {
  constructor(box: any) {
    super(box);
    this.type = "sides_arrow" as any;
  }
  setMoveEdit(): void {
    this.pointList[this.acctivePointIndex] = {
      x: this.lastMousePoint!.x,
      y: this.lastMousePoint!.y,
    };
  }
  isPointInside(point: { x: number; y: number }): boolean {
    let expand = this.expent / this.box.t.a;
    let offset = MarkBoardUtils.isPointInPolygon(point, this.pointList);
    return offset < expand;
  }
  get pathData() {
    let path = ``;
    if (
      this.pointList.length === 2 &&
      this.pointList[0].x != this.pointList[1].x &&
      this.pointList[0].y != this.pointList[1].y
    ) {
      path += `M${this.pointList[0].x},${this.pointList[1].y}`;
      path += `L${this.pointList[0].x},${this.pointList[1].y}`;
      const [side1, side2] = MarkBoardUtils.getSides(
        this.pointList[0],
        this.pointList[1]
      );
      const [arrow1, arrow2] = MarkBoardUtils.getArrow(
        side2,
        side1,
        20 / this.box.t.a
      );
      path += `Z`;
      path += `M${side2.x},${side2.y}`;
      path += `L${side1.x},${side1.y}`;
      // 绘制三角形
      path += `Z`;
      path += `M${arrow1.x},${arrow1.y}`;
      path += `L${side1.x},${side1.y}`;
      path += `M${side1.x},${side1.y}`;
      path += `L${arrow2.x},${arrow2.y}`;
      path += `Z`;
      // 连接到原来位置
      path += `M${this.pointList[0].x},${this.pointList[0].y}`;
      path += `L${this.pointList[1].x},${this.pointList[1].y}`;

      path += `L${this.pointList[0].x},${this.pointList[0].y} `;
      path += `L${this.pointList[this.pointList.length - 1].x},${
        this.pointList[this.pointList.length - 1].y
      } `;
      path += `Z `;
    }

    return path;
  }
}