JavaScript is required

分离关系组自动重布局

这是一个全高查看器:先将多个不连通关系组和孤立节点加载到同一画布并居中总览,再在短暂延时后切换为力导布局。它是“首帧稳定展示 + 加载后分离碎片簇”的聚焦参考,样式极简且无自定义控制面板。

带延迟 Force 重新布局的断开关系分组

这个示例构建了什么

这个示例构建了一个单一的全高度图谱画布,同时展示多个彼此断开的关系孤岛以及两个独立节点。第一帧会先以居中的总览方式呈现,随后在半秒后把同一份已加载图谱交给 force 布局处理,使这些孤岛在不重建数据集的情况下进一步分散开来。

可见结果刻意保持朴素风格:黑色节点边框、黑色连线、沿路径显示的连线标签,以及放置在右下角的内置工具栏。没有自定义面板、按钮或编辑工具。这个示例的核心要点,是如何先安排一个可预测的初始视图,再把一个碎片化图谱逐步放松成更分离的布局。

数据是如何组织的

数据是一个定义在模块作用域中的 RGJsonData 对象,其中包含 rootId、扁平的 nodes 数组和扁平的 lines 数组。这一份载荷中包含多个断开的组件,而不是一棵连续的树。它包括一个以节点 2 为中心的分组、一个围绕节点 6 的分组、一个围绕节点 10 的分组,以及两个名为 Single 1Single 2 的孤立节点。

图谱加载前没有任何预处理。initializeGraph() 会把 myJsonData 直接传给 setJsonData(),因此这个示例的行为来自运行时布局变化,而不是数据转换。在真实应用中,同样的结构可以表示子系统目录、资产清单、产品模块映射,或任何虽然彼此无关但仍需要共享同一个画布的总览场景。

relation-graph 的使用方式

入口组件用 RGProvider 包裹整个演示,而 MyGraph 通过 RGHooks.useGraphInstance() 获取实时实例。这个 hook 驱动了完整生命周期:加载数据集、把视口移到中心、按当前内容自适应缩放、更新布局选项,以及触发第二次布局。

初始的 RelationGraph 配置使用了 layoutName: 'center'distanceCoefficient: 0.5。同一个配置对象还启用了沿路径显示的连线标签,保持节点边框和连线为黑色,并将内置工具栏以横向方式放在右下角。初始加载完成后,代码调用 updateOptions(),只把布局部分切换为 force,并显式设置 repulsion、elasticity 以及 maxLayoutTimes: Number.MAX_SAFE_INTEGER,然后调用 doLayout()

该组件没有添加自定义插槽、自定义节点渲染或事件处理器。样式基本保持接近内置外观。唯一实际生效的 SCSS 覆盖,是把已选中连线的标签改为白色,其余嵌套选择器都只是空占位。图谱区域本身只是一个 height: '100vh' 的包裹 div

关键交互

第一个交互是自动发生的,而不是由用户驱动。当组件挂载时,它会加载图谱数据、重新居中视口,并缩放以适配整个场景。

第二个交互同样是自动的。在延迟 500 毫秒后,图谱会从初始的 center 布局切换到 force 布局,并在同一个实例上再次执行布局。

唯一可见的手动控件来自 relation-graph 的内置工具栏。这个示例只配置了工具栏出现的位置,但没有添加自定义点击处理、编辑动作或独立的控制界面。

关键代码片段

下面这个片段展示了数据集是以内联方式声明的,并且在同一份载荷中已经包含了孤立节点。

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '2', text: 'ALTXX' },
        { id: '3', text: 'CH2 TTN' },
        { id: 'single-1', text: 'Single 1' },
        { id: 'single-2', text: 'Single 2' },
        { id: '4', text: 'CH1 AlCu' },
        { id: '5', text: 'MainFrame' },

下面这个片段展示了单色默认样式、沿路径显示的标签、工具栏位置以及初始的居中布局。

const graphOptions: RGOptions = {
    defaultNodeBorderColor: '#000000',
    toolBarDirection: 'h',
    toolBarPositionH: 'right',
    toolBarPositionV: 'bottom',
    defaultLineTextOnPath: true,
    defaultLineColor: '#000000',
    layout: {
        layoutName: 'center',
        distanceCoefficient: 0.5
    }
};

下面这个片段展示了在挂载阶段执行的初始化顺序:加载数据并让第一视图适配画面。

const initializeGraph = async () => {
    await graphInstance.setJsonData(myJsonData);
    graphInstance.moveToCenter();
    graphInstance.zoomToFit();
    // ...delayed relayout follows...
};

下面这个片段展示了在已经加载完成的图谱实例上,如何从 center 延迟切换到 force

setTimeout(() => {
    graphInstance.updateOptions({
        layout: {
            layoutName: 'force',
            force_node_repulsion: 0.3,
            force_line_elastic: 5,
            maxLayoutTimes: Number.MAX_SAFE_INTEGER
        }
    });
    graphInstance.doLayout();
}, 500);

下面这个片段展示了本地样式表中唯一一个实际生效的样式覆盖。

.rg-line-peel.rg-line-checked {
    .rg-line-label {
        color: #fff;
    }
}

这个示例的独特之处

对比数据表明,真正有辨识度的地方并不只是断开的数据本身。与 multi-group 相比,这个示例在初始居中总览之后又增加了一个定时的后续分离步骤,而不是停在初始视图。与 multi-group-2 相比,它更强调实时布局切换和更自然的空间分散,而不是针对同一类碎片化数据集去强调树形可读性、曲线路由或连接点微调。

它也比其他运行时布局示例更聚焦。与 switch-layoutforce-sea-anemone 相比,这个示例只执行一次延迟切换到 force 布局,而不是循环切换不同布局,也不是持续性的 force 动画。这里少见的组合是:多孤岛数据集、真正孤立的节点、黑白技术风格、沿路径显示的标签、右下角工具栏位置,以及在同一个实时图谱实例上的延迟重新布局。当需求是“先展示所有断开的分组,再让它们进一步分开”,同时又不想增加自定义控件时,这会是一个很强的起点。

这种模式还适用于哪里

这种模式也很适合用于子系统总览、混合资产目录、作品集地图、产品家族图,以及那些数据本身是碎片化的、但仍然适合放在一个共享画布中预览的知识图谱。生产版本可以保留同样的初始化顺序,只是把内联示例替换为 API 数据,按数据集规模调整 force 系数,或者把延迟重新布局改成在筛选或数据刷新之后按条件触发的步骤。

这个可复用的思路在于分阶段呈现策略:先加载一份断开的数据集,为第一视图提供一个稳定、可预测的构图,然后通过运行时切换布局再次把它铺开,而不是从头重建整张图谱。