JavaScript is required

IO Tree 布局方向与连线路由

这个示例构建了一个静态 IO Tree 查看器,可在运行时切换横向与纵向预设。核心是布局后的路由修正步骤:对比计算得到的节点层级,重写每条连线的连接锚点、偏移和几何参数,以保持输入/输出分支可读。

IO Tree 布局方向与连线路由

这个示例构建了什么

这个示例围绕一个根节点以及一组有方向的入向和出向分支,构建了一个只读的 IO tree 查看器。同一张图可以在自上而下和从左到右的 io-tree 预设之间切换,同时保持紧凑的矩形节点和正交连接线。

用户可以改变布局方向、切换布局过程中节点尺寸是否允许变化、通过嵌入式 mini view 检查图形,并打开共享的画布设置浮层。这个示例最重要的教学点,是在 relation-graph 已经计算出节点层级之后,再通过布局后的路由处理阶段重写连线拐点锚点。

数据是如何组织的

数据以内联方式声明为一个静态 RGJsonData 对象,其中包含 rootId: 'root'、扁平的 nodes 数组和扁平的 lines 数组。连线方向很关键:有些分支指向根节点一侧的路径,有些分支则从该路径向外延伸,这让 io-tree 布局具备了足够的结构,能够在同一画布上表示入向和出向关系。

setJsonData 之前没有任何预处理。主要转换发生在初始加载之后以及每次重新布局之后,此时组件会遍历 getLinks(),并比较 link.fromNode.lot?.levellink.toNode.lot?.level,以决定每条连线应如何附着到它的两端节点。

在真实应用中,同样的数据结构可以表示服务输入和输出、上下游依赖、供应链路由、审批流入与流出,或者任何一种边方向会改变分支阅读方式的有向关系视图。

relation-graph 是如何使用的

这个示例挂载在 RGProvider 内部,然后 MyGraph 使用 RGHooks.useGraphInstance() 作为主要的运行时控制入口。初始 options 只负责建立视觉基线:矩形节点、无节点边框,以及节点和连线共用的琥珀色。真正的布局行为是在运行时通过两个 RGOptions 预设构建的,它们都使用 layoutName: 'io-tree',但会切换 fromdefaultJunctionPointchangeNodeSizeDuringLayout 策略。

当选择器或复选框发生变化时,组件会先调用 updateOptions(),然后用 getNodes()updateNode()getLines()updateLine() 对已经挂载的节点与连线状态做归一化。短暂 sleep(100) 之后,它会运行 doLayout(),接着再次通过 getLinks() 处理每一条链接,这样代码就能在一次遍历中同时访问两个端点节点以及底层连线记录。第二轮处理就是清空连线文本、设置连接器圆角和宽度,并根据计算得到的布局层级重新分配拐点或偏移量的地方。

画布层基本保持 relation-graph 的内置能力。这里没有自定义节点或连线模板;相反,示例使用 RGSlotOnView 挂载 RGMiniView,并通过 SCSS 把内置节点文字颜色改成白色。本地的 DraggableWindow 子组件提供浮动控制窗口外壳,而其中嵌入的 CanvasSettingsPanel 使用 relation-graph hooks 来切换滚轮和拖拽行为,并将画布导出为图片。

关键交互

布局选择器可以在横向和纵向 io-tree 预设之间切换。这个变化不只是翻转方向:它还会切换默认拐点约定、重新运行布局、将图形重新居中,并缩放到适合当前视口。

Change Node Size During Layout 复选框也会触发一次完整的重新布局。在这组数据里,它的可见效果比较细微,但它会实质性改变路由逻辑,因为当允许节点尺寸在布局过程中变化时,代码会在基于 offset 的锚点和 horizontalLineverticalLine 拐点目标之间切换。

浮动工具窗口可以被拖动、最小化,并打开为一个设置浮层。该浮层可以改变滚轮模式、改变画布拖拽行为,并下载通过 prepareForImageGeneration()restoreAfterImageGeneration() 配合导出的图片。

节点和连线点击处理器确实存在,但它们只是在控制台打印对象,因此并不是这个 demo 的功能性组成部分。

关键代码片段

这段代码表明,水平预设是围绕内置 io-tree 布局、紧凑间距、正交连线以及左右拐点默认值明确构建的。

// inside graphOptionsH
layout: {
    layoutName: 'io-tree',
    treeNodeGapH: 10,
    treeNodeGapV: 10,
    from: 'left',
    changeNodeSizeDuringLayout
},
defaultNodeWidth: 120,
defaultNodeHeight: 30,
defaultLineShape: RGLineShape.StandardOrthogonal,
defaultJunctionPoint: RGJunctionPoint.lr

这段代码证明,重新布局会先更新选项,并在再次运行 doLayout() 之前归一化现有节点和连线状态。

const targetOptions = layoutFrom === 'left' ? graphOptionsH : graphOptionsV;
graphInstance.updateOptions(targetOptions);
graphInstance.getNodes().forEach((node) => {
    graphInstance.updateNode(node, {
        width: targetOptions.defaultNodeWidth,
        height: targetOptions.defaultNodeHeight
    });
});
graphInstance.getLines().forEach((line) => {
    graphInstance.updateLine(line, {
        lineShape: targetOptions.defaultLineShape
    });
});

这段代码展示了,路由处理阶段如何利用计算得到的节点层级,以及 changeNodeSizeDuringLayout 标记,为反向分支选择不同的拐点行为。

if (link.fromNode.lot?.level > link.toNode.lot?.level) {
    if (treeFrom === 'top') {
        lineProps.fromJunctionPoint = RGJunctionPoint.right;
        let toJunctionPoint = RGJunctionPoint.bottom;
        if (changeNodeSizeDuringLayout) {
            toJunctionPoint = 'horizontalLine';
        } else {
            lineProps.toJunctionPointOffsetX = -5;
        }
        lineProps.toJunctionPoint = toJunctionPoint;
    }
}

这段代码表明,UI 在浮动辅助窗口中同时暴露了方向切换和节点尺寸策略这两个实时控件。

<SimpleUISelect
    data={[
        { value: 'left', text: 'Horizontal Tree' },
        { value: 'top', text: 'Vertical Tree' }
    ]}
    currentValue={layoutFrom}
    onChange={(newValue: string) => { setLayoutFrom(newValue); }}
/>
<SimpleUIBoolean currentValue={changeNodeSizeDuringLayout} onChange={setChangeNodeSizeDuringLayout} label="Change Node Size During Layout" />

这段代码展示了该示例中唯一的本地样式覆盖:内置节点标签被强制设为白色,以便在琥珀色卡片上保持清晰可读。

.rg-node-peel {
    .rg-node {
        .rg-node-text {
            color: #ffffff;
        }
    }
}

这个示例的独特之处

对比数据把 layout-treebothway-tree2bothway-treelayout-folder2 视为与这个 demo 最接近的示例,但 io-tree-layout 强调的重点与它们都不同。与 layout-tree 相比,它更关注内置 io-tree 布局,以及在布局之后重新计算的连接器附着决策,而不是通用的树形预设切换。

bothway-tree2 相比,重点从可见的线标签转移到了按连线逐条重写拐点。与 bothway-treelayout-folder2 相比,它的主要价值也不在于分支着色或名片式展示,而在于通过比较计算得到的起点和终点节点层级,再相应调整每条连线的锚点或偏移量,从而让静态 IO 风格关系图保持可读性。

这种组合在多个示例共用的全屏查看器外壳中也很少见。对比数据和稀有度数据都指向同一组区分性特征:可在运行时切换方向的 io-tree、对节点尺寸敏感的重新布局控制、琥珀色矩形节点、正交折线连接器,以及在布局引擎完成后通过 getLinks() 重写路由几何的处理过程。

这种模式还适用于哪里

这种模式很适合迁移到系统依赖图、上下游数据管道、网络入口与出口视图,以及审批流或事件流等场景,在这些场景中,一个单一焦点节点同时具有入向和出向关系。

当团队希望继续使用 relation-graph 的内置树形布局,但又需要在布局之后应用自定义连接器附着规则时,这种方法也很有用。同样的思路还可以扩展为根据边方向、分支类型、状态或拥塞规则选择锚点,而不必放弃内置渲染。