[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:center-layout-expand-relayout-toggle":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 居中层级中的实时展开/收起重布局\n\n## 这个示例构建了什么\n\n这个示例构建了一个全高度的居中层级查看器，用来比较用户通过 relation-graph 内置的 expand holder 展开或收起分支后会发生什么。画布展示的是一个以根节点为中心的子系统风格图谱，包含较大的青色圆形节点、蓝绿色连线、沿连线路径重复渲染的 `Subsystem` 标签，以及叠加在图谱上方的白色悬浮辅助窗口。\n\n用户可以直接在图上展开或收起分支，在 `Re-layout` 和 `Do not re-layout` 之间切换后续行为，拖动辅助窗口，将其最小化，并打开一个次级设置浮层。这个浮层来自本地共享辅助组件，增加了滚轮模式、拖动画布模式和图片下载控制，但该示例的重点并不是任何自定义动画调优逻辑，而是在 center 布局下实时切换 `reLayoutWhenExpandedOrCollapsed`。\n\n## 数据是如何组织的\n\n数据在 `initializeGraph()` 内以内联方式声明为一个 `RGJsonData` 对象。它使用 `rootId: '2'`，定义了 39 个硬编码节点，并通过 38 条显式连线将它们连接起来。标签混合了子系统名称、制造步骤和组件名称，但结构本身保持简单：节点只需要 `id` 和 `text`，连线只需要 `from`、`to` 以及重复使用的边标签文本。\n\n在调用 `setJsonData(...)` 之前没有任何预处理步骤。组件不会获取数据、标准化记录，也不会派生第二套布局结构。在真实应用中，同样的模式可以表示产品架构图、设备分解图、制造能力拆解图、技术子系统树，或任何其他以根节点为中心的层级结构，在这些场景里，团队需要决定展开操作是否应该触发一次新的布局计算。\n\n## relation-graph 是如何使用的\n\n`index.tsx` 在最外层用 `RGProvider` 包装示例，`MyGraph.tsx` 则通过 `RGHooks.useGraphInstance()` 使用实例 API 控制图谱。图谱选项配置了 `layoutName: 'center'` 和 `distanceCoefficient: 1.5`，节点为 `100x100` 的圆形节点，填充色为青色，连线颜色为蓝绿色，expand holder 位于底部，线条文本直接渲染在连接线路径上。最终得到的是一个以根节点为中心的图示；由于分支可以围绕根节点向多个方向重新分布，因此展开后的重布局视觉影响会更容易比较。\n\n组件先通过 `setJsonData(...)` 加载内联层级数据，然后立即调用 `moveToCenter()` 和 `zoomToFit()`，让完整图谱在初始状态下处于视图中心并完整显示。第二个 effect 监听 React 的 `relayout` 状态，并通过 `updateOptions({ reLayoutWhenExpandedOrCollapsed })` 将其推送到当前图谱实例。这个运行时同步机制是本示例里 relation-graph 的核心用法：无需重建数据集，也无需重新挂载组件，就能改变图谱行为。\n\n这个示例没有使用自定义节点、连线、画布或视口 slot，也没有示例专属的图谱事件处理器。展开和收起行为来自 relation-graph 内置的 holder，而不是来自自定义 slot 或手动控制分支可见性的代码。该示例同样没有实现编辑或创作类功能。\n\n悬浮控制面板来自本地共享的 `DraggableWindow` 辅助组件。这个辅助组件并不是该示例独有的，但它很重要，因为它让重布局选择器可以被拖动，并为演示提供了一个次级设置浮层。在这个浮层内部，`CanvasSettingsPanel` 使用 `RGHooks.useGraphStore()` 读取 `wheelEventAction` 和 `dragEventAction`，调用 `graphInstance.setOptions(...)` 在实时图谱上更新这些值，并通过 `prepareForImageGeneration()` 和 `restoreAfterImageGeneration()` 支持图片导出。本地 SCSS 文件只包含空的包装选择器，因此几乎所有可见样式都来自图谱选项和共享悬浮窗口，而不是来自自定义 CSS 覆盖。\n\n## 关键交互\n\n- 点击内置的 expand holder，可以在居中层级中展开或收起分支。\n- 点击 `Re-layout` 或 `Do not re-layout`，会更新已挂载图谱上的 `reLayoutWhenExpandedOrCollapsed`，因此下一次展开或收起时，要么重新排布当前可见层级，要么保留当前分支位置。\n- 辅助窗口可以通过标题栏拖动，因此控制面板不是固定在某一个屏幕位置。\n- 辅助窗口可以最小化，这使得示例可以在带可见控制的教学模式和更简洁的查看模式之间切换。\n- 设置按钮会打开一个共享浮层，可用于修改滚轮行为、修改画布拖动行为，以及将图谱下载为图片。这些控件是次要的共享工具，而不是该示例的主要教学内容。\n\n## 关键代码片段\n\n下面这个片段说明，该示例被明确配置为一个具有更大间距、大型圆形节点以及运行时重布局选项的居中层级图：\n\n```tsx\nconst graphOptions: RGOptions = {\n    layout: {\n        layoutName: 'center',\n        distanceCoefficient: 1.5\n    },\n    defaultNodeBorderWidth: 1,\n    defaultNodeShape: RGNodeShape.circle,\n    defaultNodeWidth: 100,\n    defaultNodeHeight: 100,\n    defaultLineColor: 'rgba(0, 186, 189, 1)',\n    defaultNodeColor: 'rgba(0, 206, 209, 1)',\n    reLayoutWhenExpandedOrCollapsed: relayout,\n    defaultExpandHolderPosition: 'bottom',\n    defaultLineTextOnPath: true\n};\n```\n\n下面这个片段说明，该层级数据以内联方式组装，并在没有任何转换步骤的情况下直接传给 relation-graph：\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: '2',\n    nodes: [\n        { id: '2', text: 'ALTXX' },\n        { id: '3', text: 'CH2 TTN' },\n        { id: '4', text: 'CH1 AlCu' },\n        // ... additional nodes omitted\n    ],\n    lines: [\n        { from: '2', to: '5', text: 'Subsystem' },\n        { from: '2', to: '6', text: 'Subsystem' },\n        // ... additional lines omitted\n    ]\n};\n```\n\n下面这个片段说明了挂载时的数据加载顺序：先加载一次层级数据，再将其居中并适配到视口中：\n\n```tsx\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\n下面这个片段说明了运行时选项同步模式，这也是该示例真正有用的地方：\n\n```tsx\nconst syncOptionsToGraph = () => {\n    graphInstance.updateOptions({\n        reLayoutWhenExpandedOrCollapsed: relayout\n    });\n};\n\nuseEffect(() => {\n    syncOptionsToGraph();\n}, [relayout]);\n```\n\n下面这个片段说明，面向用户的控件是一个直接的布尔选择器，而不是 reset、重建或脚本驱动的动作：\n\n```tsx\n\u003CSimpleUISelect\n    data={[\n        { value: true, text: 'Re-layout' },\n        { value: false, text: 'Do not re-layout' }\n    ]}\n    currentValue={relayout}\n    onChange={(newValue: boolean) => {\n        setRelayout(newValue);\n    }}\n/>\n```\n\n下面这个片段说明，共享浮层仍然可以通过图谱实例重新配置实时画布行为：\n\n```tsx\nconst { options } = RGHooks.useGraphStore();\nconst dragMode = options.dragEventAction;\nconst wheelMode = options.wheelEventAction;\n\n\u003CSettingRow\n    label=\"Wheel Event:\"\n    value={wheelMode}\n    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}\n/>\n```\n\n## 这个示例的独特之处\n\n对比数据表明，`expand-animation` 是最接近的直接邻近示例，因为这两个示例都使用了相同的悬浮选择器模式，以及相同的 `updateOptions({ reLayoutWhenExpandedOrCollapsed })` 技术。区别在于布局语境不同。这个示例使用带有 `distanceCoefficient: 1.5` 的居中层级、大型青色圆形节点、底部 expand holder，以及沿路径显示标签的蓝绿色连接线，因此分支重分布发生在同一个根节点周围，而不是沿单一的垂直方向展开。\n\n与 `open-all-close-all` 相比，这个示例不会脚本化地执行整个图谱的递归播放、定时展开或动画编排。它只聚焦于一个更简单的决策：普通的用户驱动展开或收起操作是否应该触发重布局。与 `multiple-expand-buttons` 相比，它依赖内置 expand holder，而不是自定义节点 slot 和手动的左右分支可见性逻辑。\n\n它特别有价值的地方在于：一个包含 39 个节点的居中子系统层级、`reLayoutWhenExpandedOrCollapsed` 的实时同步、一个悬浮辅助窗口，以及同屏显示的基于路径的连线标签被组合在一起。这使它在团队需要评估展开后应发生多大空间位移时，成为比自上而下树形版本更适合作为根居中层级产品起点的示例。对比分析结果还明确了一个边界：这并不是一个真正的动画参数演示，因为已审查的源码并没有配置自定义时长、缓动或过渡代码。\n\n## 这种模式还能应用到哪里\n\n这种模式很适合迁移到子系统图、产品结构查看器、设备树、能力地图和技术知识层级中，尤其适用于根节点位于画布中部、用户需要反复展开和收起分支，同时还希望保持方向感的场景。当产品团队需要决定展开行为应该保留局部位置，还是应该触发一次完整的视觉再平衡时，它尤其相关。\n\n同样的方法也可以复用于设置项较多的图谱工具、QA 测试工具或管理后台界面，这些场景会把布局行为作为运行时偏好暴露给用户。与其硬编码一种展开策略，不如让用户在同一份数据集上对比两种模式，再决定默认值。\n",false,500,1782615434205]