[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:disconnected-multi-group-force-relayout":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 带延迟 Force 重新布局的断开关系分组\n\n## 这个示例构建了什么\n\n这个示例构建了一个单一的全高度图谱画布，同时展示多个彼此断开的关系孤岛以及两个独立节点。第一帧会先以居中的总览方式呈现，随后在半秒后把同一份已加载图谱交给 force 布局处理，使这些孤岛在不重建数据集的情况下进一步分散开来。\n\n可见结果刻意保持朴素风格：黑色节点边框、黑色连线、沿路径显示的连线标签，以及放置在右下角的内置工具栏。没有自定义面板、按钮或编辑工具。这个示例的核心要点，是如何先安排一个可预测的初始视图，再把一个碎片化图谱逐步放松成更分离的布局。\n\n## 数据是如何组织的\n\n数据是一个定义在模块作用域中的 `RGJsonData` 对象，其中包含 `rootId`、扁平的 `nodes` 数组和扁平的 `lines` 数组。这一份载荷中包含多个断开的组件，而不是一棵连续的树。它包括一个以节点 `2` 为中心的分组、一个围绕节点 `6` 的分组、一个围绕节点 `10` 的分组，以及两个名为 `Single 1` 和 `Single 2` 的孤立节点。\n\n图谱加载前没有任何预处理。`initializeGraph()` 会把 `myJsonData` 直接传给 `setJsonData()`，因此这个示例的行为来自运行时布局变化，而不是数据转换。在真实应用中，同样的结构可以表示子系统目录、资产清单、产品模块映射，或任何虽然彼此无关但仍需要共享同一个画布的总览场景。\n\n## relation-graph 的使用方式\n\n入口组件用 `RGProvider` 包裹整个演示，而 `MyGraph` 通过 `RGHooks.useGraphInstance()` 获取实时实例。这个 hook 驱动了完整生命周期：加载数据集、把视口移到中心、按当前内容自适应缩放、更新布局选项，以及触发第二次布局。\n\n初始的 `RelationGraph` 配置使用了 `layoutName: 'center'` 和 `distanceCoefficient: 0.5`。同一个配置对象还启用了沿路径显示的连线标签，保持节点边框和连线为黑色，并将内置工具栏以横向方式放在右下角。初始加载完成后，代码调用 `updateOptions()`，只把布局部分切换为 `force`，并显式设置 repulsion、elasticity 以及 `maxLayoutTimes: Number.MAX_SAFE_INTEGER`，然后调用 `doLayout()`。\n\n该组件没有添加自定义插槽、自定义节点渲染或事件处理器。样式基本保持接近内置外观。唯一实际生效的 SCSS 覆盖，是把已选中连线的标签改为白色，其余嵌套选择器都只是空占位。图谱区域本身只是一个 `height: '100vh'` 的包裹 `div`。\n\n## 关键交互\n\n第一个交互是自动发生的，而不是由用户驱动。当组件挂载时，它会加载图谱数据、重新居中视口，并缩放以适配整个场景。\n\n第二个交互同样是自动的。在延迟 `500` 毫秒后，图谱会从初始的 `center` 布局切换到 `force` 布局，并在同一个实例上再次执行布局。\n\n唯一可见的手动控件来自 relation-graph 的内置工具栏。这个示例只配置了工具栏出现的位置，但没有添加自定义点击处理、编辑动作或独立的控制界面。\n\n## 关键代码片段\n\n下面这个片段展示了数据集是以内联方式声明的，并且在同一份载荷中已经包含了孤立节点。\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: '2',\n    nodes: [\n        { id: '2', text: 'ALTXX' },\n        { id: '3', text: 'CH2 TTN' },\n        { id: 'single-1', text: 'Single 1' },\n        { id: 'single-2', text: 'Single 2' },\n        { id: '4', text: 'CH1 AlCu' },\n        { id: '5', text: 'MainFrame' },\n```\n\n下面这个片段展示了单色默认样式、沿路径显示的标签、工具栏位置以及初始的居中布局。\n\n```tsx\nconst graphOptions: RGOptions = {\n    defaultNodeBorderColor: '#000000',\n    toolBarDirection: 'h',\n    toolBarPositionH: 'right',\n    toolBarPositionV: 'bottom',\n    defaultLineTextOnPath: true,\n    defaultLineColor: '#000000',\n    layout: {\n        layoutName: 'center',\n        distanceCoefficient: 0.5\n    }\n};\n```\n\n下面这个片段展示了在挂载阶段执行的初始化顺序：加载数据并让第一视图适配画面。\n\n```tsx\nconst initializeGraph = async () => {\n    await graphInstance.setJsonData(myJsonData);\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n    // ...delayed relayout follows...\n};\n```\n\n下面这个片段展示了在已经加载完成的图谱实例上，如何从 `center` 延迟切换到 `force`。\n\n```tsx\nsetTimeout(() => {\n    graphInstance.updateOptions({\n        layout: {\n            layoutName: 'force',\n            force_node_repulsion: 0.3,\n            force_line_elastic: 5,\n            maxLayoutTimes: Number.MAX_SAFE_INTEGER\n        }\n    });\n    graphInstance.doLayout();\n}, 500);\n```\n\n下面这个片段展示了本地样式表中唯一一个实际生效的样式覆盖。\n\n```scss\n.rg-line-peel.rg-line-checked {\n    .rg-line-label {\n        color: #fff;\n    }\n}\n```\n\n## 这个示例的独特之处\n\n对比数据表明，真正有辨识度的地方并不只是断开的数据本身。与 [`multi-group`](multi-group.md) 相比，这个示例在初始居中总览之后又增加了一个定时的后续分离步骤，而不是停在初始视图。与 `multi-group-2` 相比，它更强调实时布局切换和更自然的空间分散，而不是针对同一类碎片化数据集去强调树形可读性、曲线路由或连接点微调。\n\n它也比其他运行时布局示例更聚焦。与 `switch-layout` 和 `force-sea-anemone` 相比，这个示例只执行一次延迟切换到 force 布局，而不是循环切换不同布局，也不是持续性的 force 动画。这里少见的组合是：多孤岛数据集、真正孤立的节点、黑白技术风格、沿路径显示的标签、右下角工具栏位置，以及在同一个实时图谱实例上的延迟重新布局。当需求是“先展示所有断开的分组，再让它们进一步分开”，同时又不想增加自定义控件时，这会是一个很强的起点。\n\n## 这种模式还适用于哪里\n\n这种模式也很适合用于子系统总览、混合资产目录、作品集地图、产品家族图，以及那些数据本身是碎片化的、但仍然适合放在一个共享画布中预览的知识图谱。生产版本可以保留同样的初始化顺序，只是把内联示例替换为 API 数据，按数据集规模调整 force 系数，或者把延迟重新布局改成在筛选或数据刷新之后按条件触发的步骤。\n\n这个可复用的思路在于分阶段呈现策略：先加载一份断开的数据集，为第一视图提供一个稳定、可预测的构图，然后通过运行时切换布局再次把它铺开，而不是从头重建整张图谱。\n",false,500,1782615417607]