前端使用 Konva 实现可视化设计器(12)- 连接线 - 直线

这一章实现的连接线,目前仅支持直线连接,为了能够不影响原有的其它功能,尝试了2、3个实现思路,最终实测这个实现方式目前来说最为合适了。

请大家动动小手,给我一个免费的 Star 吧~

大家如果发现了 Bug,欢迎来提 Issue 哟~

github源码

gitee源码

示例地址

在这里插入图片描述

相关定义

  • 连接点
    在这里插入图片描述

记录了连接点相关信息,并不作为素材而存在,仅记录信息,即导出导入的时候,并不会出现所谓的连接点节点。

它存放在节点身上,因此导出、导入自然而然就可以持久化了。

src/Render/draws/LinkDraw.ts

// 连接点
export interface LinkDrawPoint {
   
  id: string
  groupId: string
  visible: boolean
  pairs: LinkDrawPair[]
  x: number
  y: number
}
  • 连接对
    在这里插入图片描述

一个连接点,记录从该点出发的多条连接线信息,作为连接对信息存在。

src/Render/draws/LinkDraw.ts

// 连接对
export interface LinkDrawPair {
   
  id: string
  from: {
   
    groupId: string
    pointId: string
  }
  to: {
   
    groupId: string
    pointId: string
  }
}
  • 连接点(锚点)
    在这里插入图片描述

它是拖入素材的时候生成的真实节点,归属于所在的节点中,存在却不可见,关键作用是同步连接点真实坐标,尤其是节点发生 transform 时候,必须依赖它获得 transform 后连接点变化。

src/Render/handlers/DragOutsideHandlers.ts

// 略
		drop: (e: GlobalEventHandlersEventMap['drop']) => {
   
// 略

              const points = [
                // 左
                {
    x: 0, y: group.height() / 2 },
                // 右
                {
   
                  x: group.width(),
                  y: group.height() / 2
                },
                // 上
                {
    x: group.width() / 2, y: 0 },
                // 下
                {
   
                  x: group.width() / 2,
                  y: group.height()
                }
              ]

              // 连接点信息
              group.setAttrs({
   
                points: points.map(
                  (o) =>
                    ({
   
                      ...o,
                      id: nanoid(),
                      groupId: group.id(),
                      visible: true,
                      pairs: []
                    }) as LinkDrawPoint
                )
              })

              // 连接点(锚点)
              for (const point of group.getAttr('points') ?? []) {
   
                group.add(
                  new Konva.Circle({
   
                    name: 'link-anchor',
                    id: point.id,
                    x: point.x,
                    y: point.y,
                    radius: this.render.toStageValue(1),
                    stroke: 'rgba(0,0,255,1)',
                    strokeWidth: this.render.toStageValue(2),
                    visible: false
                  })
                )
              }

              group.on('mouseenter', () => {
   
                // 显示 连接点
                this.render.linkTool.pointsVisible(true, group)
              })

              // hover 框(多选时才显示)
              group.add(
                new Konva.Rect({
   
                  id: 'hoverRect',
                  width: image.width(),
                  height: image.height(),
                  fill: 'rgba(0,255,0,0.3)',
                  visible: false
                })
              )

              group.on('mouseleave', () => {
   
                // 隐藏 连接点
                this.render.linkTool.pointsVisible(false, group)

                // 隐藏 hover 框
                group.findOne('#hoverRect')?.visible(false)
              })
              
// 略
}
// 略
  • 连接线
    在这里插入图片描述

根据连接点信息,绘制的线条,也不作为素材而存在,导出导入的时候,也不会出现所谓的连接点节点。不过,在导出图片、SVG和用于预览框的时候,会直接利用线条节点导出、显示。

src/Render/tools/ImportExportTool.ts

// 略
  /**
   * 获得显示内容
   * @param withLink 是否包含线条
   * @returns 
   */
  getView(withLink: boolean = false) {
   
  	// 复制画布
    const copy = this.render.stage.clone()
    // 提取 main layer 备用
    const main = copy.find('#main')[0] as Konva.Layer
    const cover = copy.find('#cover')[0] as Konva.Layer
    // 暂时清空所有 layer
    copy.removeChildren()

    // 提取节点
    let nodes = main.getChildren((node) => {
   
      return !this.render.ignore(node)
    })

    if (withLink) {
   
      nodes = nodes.concat(
        cover.getChildren((node) => {
   
          return node.name() === Draws.LinkDraw.name
        })
      )
    }
    
  	// 略
  }
// 略

src/Render/draws/PreviewDraw.ts

  override draw() {
   
      // 略
      
      const main = this.render.stage.find('#main')[0] as Konva.Layer
      const cover = this.render.stage.find('#cover')[0] as Konva.Layer
      // 提取节点
      const nodes = [
        ...main.getChildren((node) => {
   
          return !this.render.ignore(node)
        }),
        // 补充连线
        ...cover.getChildren((node) => {
   
          return node.name() === Draws.LinkDraw.name
        })
      ]
      
      // 略
  }
  • 连接线(临时)
    在这里插入图片描述

起点鼠标按下 -> 拖动显示线条 -> 终点鼠标释放 -> 产生连接信息 LinkDrawPoint. LinkDrawPair

// 连接线(临时)
export interface LinkDrawState {
   
  linkingLine: {
   
    group: Konva.Group
    circle: Konva.Circle
    line: Konva.Line
  } | null
}

代码文件

新增几个关键的代码文件:

src/Render/draws/LinkDraw.ts

根据 连接点.链接对 绘制 连接点、连接线,及其相关的事件处理

它的绘制顺序,应该放在绘制 比例尺、预览框之前。

src/Render/handlers/LinkHandlers.ts

根据 连接线(临时)信息,绘制/移除 连接线(临时)

src/Render/tools/LinkTool.ts

移除连接线,控制 连接点 的显示/隐藏

移除连接线,实际上就是移除其 连接对 信息

// 略

export class LinkTool {
   
  // 略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值