流程图渲染器多点折线实践

背景:G6的折线无法自定义controlPoints,折线的方案由G6官方自定义的算法生成,会导致画线时线段穿过图形。

解决方案:在绘制折线时,让用户能自定义画转折点,从而达到线段自由弯折,不和图形重叠。

实现效果:

具体方案:

  • 利用G6内置直线进行画线,在需要转折的地方按一下ctrl键,利用G6的内置圆形画一个转折点。
  • 线段的终点指向转折点,然后又从转折点开启一条新的连线,连线的终点跟着鼠标。
  • 记录下画线起点、终点,转折点集合。
  • 当线段画线终止时,利用G6内置的折线(polyLine)画一条折线。然后把刚刚记录的转折点赋值给controlPoints。
  • 转折点是基于G6的内置图形,本身也是支持拖拽的。在画线结束后,鼠标放上去折线时,隐藏原本的polyLine画线,显示此线段的转折点,并和起点、终点相连。

关键代码:

    // 绘制转折小圆点
    handleDrawControlPointRect() {
      if (!this.edge) return;

      const { edge, currentEdgeGroupId, graph } = this;
      const { x, y } = this.mouseCoord;
      const sourceNodeId = guid();
      const newNodeModel = {
        id: sourceNodeId,
        x,
        y,
        group: currentEdgeGroupId,
      };
      const newNode = graph.addItem('node', new VirtulaNode(newNodeModel).getModel());
      const lastEdgeModel = {
        target: newNodeModel.id,
        targetAnchor: 0,
      };
      let newEdgeModel = {};

      // 更新上一次划线的样式
      graph.updateItem(edge, new VirtualEdge(lastEdgeModel).getModel());

      const sourceEdgeId = guid();
      newEdgeModel = {
        id: sourceEdgeId,
        source: sourceNodeId,
        sourceAnchor: 3,
        target: {
          x,
          y,
        },
        group: currentEdgeGroupId,
      };

      // 从结束节点重新开始划线
      const newEdge = graph.addItem('edge', new VirtualEdge(newEdgeModel).getModel());

      // 将小圆点和线条加入到组中
      this.edgeGroup[currentEdgeGroupId].data = [...this.edgeGroup[currentEdgeGroupId].data, newNode, newEdge];
      this.edgeGroupCopy[currentEdgeGroupId].data = [
        ...this.edgeGroupCopy[currentEdgeGroupId].data,
        sourceNodeId,
        sourceEdgeId,
      ];
      this.edgeGroup[currentEdgeGroupId].points = {
        ...this.edgeGroup[currentEdgeGroupId].points,
        [sourceNodeId]: { x, y },
      };
      this.edgeGroupCopy[currentEdgeGroupId].points = {
        ...this.edgeGroupCopy[currentEdgeGroupId].points,
        [sourceNodeId]: { x, y },
      };
      this.edge = newEdge;
    },
    // 删除一个分组 并且删除真实线段
    handleRemoveGroup(currentGroupId) {
      const { graph } = this;

      // 如果是开始节点和起草节点的连线,不进行删除
      if (this.edgeGroup[currentGroupId]?.data?.length === 1 || true) {
        const tmpEdge = this.edgeGroup[currentGroupId].data[0];
        const tmpEdgeModel = tmpEdge?.getModel() ?? {};
        // const sourceNode = graph.findById(tmpEdgeModel.source)?.getModel() ?? {};

        // if (sourceNode.bizFormType === 'BEGIN_NODE') {
        //   graph.emit(EventEmit.DeleteBuildInEdge, {
        //     type: 'warn',
        //     desc: '内置线条不能删除',
        //   });
        //   return;
        // }
      }

      (this.edgeGroup[currentGroupId]?.data || []).forEach((item) => {
        !item.destroyed && graph.removeItem(item);
      });

      // 删除线段
      !this.edgeGroup[currentGroupId]?.realEdge?.destroyed && graph.removeItem(this.edgeGroup[currentGroupId]?.realEdge);
      // 删除转折点和线条
      if (this.edgeGroup[currentGroupId]?.points) {
        (Object.keys(this.edgeGroup[currentGroupId].points) || []).forEach((id) => {
          const ele = graph.findById(id);
          ele && graph.remove(ele);
        });
      }
      globalCurrentEle.setCurrent({});

      delete this.edgeGroup[currentGroupId];
      delete this.edgeGroupCopy[currentGroupId];
      this.edge = null;
      graph.emit('test', this.edgeGroupCopy);
    },

如何使用:

作者:聂维  创建时间:2022-11-25 15:27
最后编辑:聂维  更新时间:2024-12-11 10:09