整图展开/折叠回放
这是一个 relation-graph 示例,用于在固定居中层级图上回放整图展开/收缩序列。它加载一份内联 39 节点数据,自动适配视图并在挂载后自动播放展开过程,在可拖拽辅助窗中提供全局 `Expand All` 与 `Collapse All`,并继承共享画布设置与图片导出能力。
居中层级中的整图展开/折叠回放
这个示例构建了什么
这个示例构建了一个全高度的居中层级查看器,它会对整张图的分支可见性进行回放,而不是把展开操作留给单个节点点击。画布展示的是一个以根节点为中心、类似子系统结构图的关系图,包含橙色圆形节点、灰色直线标签连线,以及悬浮在图上方的白色辅助窗口。
用户可以回放 Expand All 和 Collapse All,拖动辅助窗口、将其最小化,并打开一个次级设置浮层。该浮层来自共享的本地辅助组件,提供滚轮模式、拖动画布模式和图片下载控制,但这个示例的重点是脚本化的整图回放:它会重置层级结构、按自上而下的顺序执行展开动画,并且还能基于同一份数据执行从叶子节点开始的折叠。
数据是如何组织的
数据在 initializeGraph() 内以内联方式声明为一个 RGJsonData 对象。它使用 rootId: '2',定义了 39 个硬编码节点,并通过 38 条显式连线将它们连接起来。节点记录只需要 id 和 text,而连线记录只需要 id、from、to 以及重复出现的标签 Subsystem。
在调用 setJsonData(...) 之前没有任何预处理步骤。组件不会拉取远程数据、不会对源记录做标准化,也不会为了回放再派生出第二套结构。在真实应用里,这种同样的数据形状可以表示子系统树、产品架构图、设备分解结构、制造能力层级,或任何其他固定根节点居中的拆解结构,只要团队希望通过一个控制项按顺序展示或收起整张图。
relation-graph 是如何使用的
index.tsx 用 RGProvider 包裹整个示例,MyGraph.tsx 则通过 RGHooks.useGraphInstance() 使用实例 API 控制运行中的图。图配置中设置了 layoutName: 'center',将展开控制点放在右侧,在分支变化时保持重新布局启用,使用紧凑的 50x50 圆形节点,并通过 RGLineShape.StandardStraight 渲染边。随后,SCSS 文件覆盖了节点和连线样式,以生成白色画布、橙色节点、选中态发光效果、灰色描边,以及小型灰色连线标签。
组件加载内联层级数据时会调用 setJsonData(...),随后立即用 moveToCenter() 和 zoomToFit() 将图重新居中并适配显示,然后在挂载时调用 openAll()。这条启动路径很关键,因为这个示例并不只是一个手动演示。它会在数据准备好后主动把图重置到折叠状态,并立即回放整套展开序列。
这个示例特有的逻辑是围绕 relation-graph 的实例 API 构建的,而不是依赖自定义插槽或事件处理器。openAll() 会启用画布动画、折叠所有分支节点、获取根节点,并执行一次深度优先的 deepExpandNode(...) 遍历。在这个遍历过程中,每个子节点会先通过 updateNode(...) 被移动到父节点坐标位置,然后通过 expandNode(...) 展开父分支,接着固定暂停 sleep(400),再递归进入布局计算出来的子节点。closeAll() 则执行相反的流程:它先将分支节点标记为展开状态,获取根节点,然后运行 deepCloseNode(...),该函数会先向下递归,再以相同的节奏暂停并自底向上调用 collapseNode(...),同时反复执行 zoomToFit()。
这个示例没有自定义节点、连线、画布或视口插槽,也没有编辑工作流。悬浮控制界面来自共享的 DraggableWindow 辅助组件。这个辅助组件并不是该示例独有的,但它很重要,因为它提供了可拖动、可最小化的外壳以及设置浮层。在这个浮层内部,CanvasSettingsPanel 使用 RGHooks.useGraphStore() 读取当前的 wheelEventAction 和 dragEventAction,通过 graphInstance.setOptions(...) 更新它们,并借助 prepareForImageGeneration()、getOptions() 和 restoreAfterImageGeneration() 支持图片导出。
关键交互
- 点击
Expand All会从根节点开始,以整图自上而下的方式回放显示过程,而不是只展开单个节点。 - 点击
Collapse All会对整个层级结构回放一套从叶子节点开始的折叠序列。 playing状态会在序列运行期间禁用两个回放按钮,从而防止展开和折叠命令相互重叠。- 辅助窗口可以通过标题栏拖动,因此操作面板不会占用固定的页面区域。
- 辅助窗口可以最小化,这让用户能够在更少界面装饰的情况下查看图。
- 设置按钮会打开一个共享浮层,可切换滚轮行为、切换画布拖动行为,并将当前图下载为图片。这些控制项是继承来的辅助能力,而不是该示例的核心要点。
关键代码片段
这段代码说明,该图被有意配置为一个居中层级结构,使用紧凑的圆形节点、右侧展开控制点,并在可见性变化期间启用重新布局:
const graphOptions: RGOptions = {
defaultExpandHolderPosition: "right",
reLayoutWhenExpandedOrCollapsed: true,
defaultNodeWidth: 50,
defaultNodeHeight: 50,
defaultNodeShape: RGNodeShape.circle,
debug: false,
layout: {
layoutName: 'center',
},
defaultLineShape: RGLineShape.StandardStraight
};
这段代码说明,层级数据以内联方式组装,并在没有任何预处理步骤的情况下直接传给 relation-graph:
const myJsonData: RGJsonData = {
"rootId": "2",
"nodes": [
{ "id": "2", "text": "ALTXX" }, { "id": "3", "text": "CH2 TTN" },
{ "id": "4", "text": "CH1 AlCu" }, { "id": "5", "text": "MainFrame" },
// ... more nodes omitted
],
"lines": [
{ "id": "l1", "from": "2", "to": "5", "text": "Subsystem" },
{ "id": "l2", "from": "2", "to": "6", "text": "Subsystem" }
// ... more lines omitted
]
};
这段代码说明了挂载时的数据加载顺序,包括图完成居中和适配显示后自动触发回放:
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
await openAll();
这段代码展示了展开侧的回放技巧:先把子节点预放置到父节点位置,展开分支,等待,然后再递归处理计算出的子节点:
const deepExpandNode = async (node: RGNode) => {
if (node.rgChildrenSize === 0) return;
for (const cnode of node.lot.childs) {
graphInstance.updateNode(cnode, { x: node.x, y: node.y});
}
graphInstance.expandNode(node);
await graphInstance.sleep(400);
for (const cnode of node.lot.childs) {
await deepExpandNode(cnode);
}
graphInstance.zoomToFit();
};
这段代码说明,openAll() 会在回放前先重置分支可见性,然后在启用画布动画的情况下,从根节点驱动整套展开序列:
const openAll = async () => {
setPlaying(true);
graphInstance.enableCanvasAnimation();
graphInstance.getNodes().forEach(n => {
if (n.rgChildrenSize > 0) {
graphInstance.collapseNode(n);
}
});
const rootNode = graphInstance.getRootNode();
if (rootNode) await deepExpandNode(rootNode);
graphInstance.disableCanvasAnimation();
setPlaying(false);
};
这段代码展示了折叠回放时相反的遍历顺序:先向下递归,再在回溯过程中执行折叠:
const deepCloseNode = async (node: RGNode) => {
if (node.rgChildrenSize === 0) return;
for (const cnode of node.lot.childs) {
await deepCloseNode(cnode);
}
await graphInstance.sleep(400);
graphInstance.collapseNode(node);
graphInstance.zoomToFit();
};
这段代码说明,面向用户的控制项是全局回放按钮,并且在序列运行期间会被锁定:
<DraggableWindow>
<div className="py-2 text-sm">Actions:</div>
<div className="flex gap-3">
<SimpleUIButton disabled={playing} onClick={openAll}>Expand All</SimpleUIButton>
<SimpleUIButton disabled={playing} onClick={closeAll}>Collapse All</SimpleUIButton>
</div>
</DraggableWindow>
这段代码说明,共享浮层仍然可以通过当前的图实例修改画布交互模式,并导出当前图像:
const { options } = RGHooks.useGraphStore();
const dragMode = options.dragEventAction;
const wheelMode = options.wheelEventAction;
<SettingRow
label="Wheel Event:"
value={wheelMode}
onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>
这个示例的独特之处
对比数据清楚地表明了它最主要的区别:这并不主要是一个像 expand-animation2 或 expand-animation 那样的重新布局切换示例。它更适合作为按时间编排整图回放的参考。它不寻常的地方不只是图可以展开或折叠,而是这两个方向都被设计成可以从同一个控制界面反复回放的动作。
与附近的其他示例相比,这个示例在同一个居中层级中组合了多项少见特性:启动时自动重置为折叠状态、挂载后立即自动播放、专门的 Expand All 和 Collapse All 按钮、自顶向下的递归展开、从叶子节点开始的递归折叠、通过 updateNode(...) 预放置子节点位置、固定的 sleep(400) 节奏,以及包裹整套流程的画布动画。对比产物也明确划出了一条重要边界:悬浮辅助窗口、滚轮模式切换、拖动模式切换以及图片导出都只是共享脚手架,而不是这个示例真正独特的点。
正因为有这种组合,当需求是重置并回放整棵层级的可见性状态,而不是只控制根节点某一侧时,这个示例比 multiple-expand-buttons 更适合作为起点。当目标是异步分支编排,而不是画布交互设置时,它也比 drag-and-wheel-event 更适合作为起点。
这种模式还适用于哪些场景
这种模式很适合迁移到引导式产品演示、架构讲解、设备分解查看器、子系统图、依赖关系浏览器,以及面向展示的知识图谱场景。在这些场景中,用户需要通过一个命令按可控顺序展开或收起整棵层级结构。尤其是在瞬间切换状态显得过于突兀,而产品需要一个更易读的展示过程时,这种模式会很有用。
同样的方法也可以扩展到重置流程、引导序列、演示 kiosks、QA 回放工具,或在“完全展开”和“完全折叠基线”之间切换的分析工作台中。需要强调的是,这些只是可迁移的应用场景,并不是该示例中已经实现的功能。