默认折叠的可展开树图
这个示例展示如何在 `setJsonData()` 后立即折叠指定分支,并通过后续节点点击逐步展开。它是预加载层级上的渐进披露参考,包含自动重布局及共享画布设置与导出工具。
构建一个初始折叠并可渐进展开的树
这个示例构建了什么
这个示例构建了一个小型的从左到右树形查看器,并且初始状态下部分分支处于折叠状态。用户首先看到的是一个紧凑的橄榄黄色层级结构,包含矩形节点、正交连线以及位于右侧的展开控件;随后可以通过点击已折叠节点来显示隐藏分支。这个示例的重点并不是通用的树渲染,而是一套明确的启动流程:一次性加载完整数据集,在代码中折叠选定节点,然后让图谱以渐进披露浏览器的方式运行。
数据是如何组织的
图数据以内联 RGJsonData 的形式声明,包含 rootId: 'a'、一个扁平的 nodes 数组以及一个扁平的 lines 数组。这个示例没有使用嵌套的 children;每一条关系都通过明确的 from 和 to id 表达,因此按节点 id 执行折叠步骤会很容易定位。
在 setJsonData() 之后有一个重要的预处理步骤:代码使用 getNodeById() 查找 a、b 和 c,并在第一次稳定布局之前先折叠这些节点。这意味着查看器的初始状态并不是原始的完整可见图,而是一个经过刻意设计的信息披露状态。
在生产场景中,同样的数据结构可以表示组织分支、课程大纲、产品分类树、故障排查流程,或者任何完整图谱已经已知、但不应一次性全部展示出来的层级结构。
relation-graph 是如何使用的
该图使用 layoutName: 'tree' 并设置 from: 'left',因此层级会以从左到右的方式水平阅读。defaultLineShape 为 RGLineShape.StandardOrthogonal,defaultJunctionPoint 为 RGJunctionPoint.lr,defaultExpandHolderPosition 为 'right',从而使整体视觉语言与水平树保持一致。示例还为节点和连线设置了匹配的橄榄黄色默认样式,并通过本地 SCSS 覆盖确保节点文字在较深的填充色上仍保持白色。
RGHooks.useGraphInstance() 是主要的集成入口。示例通过图实例加载内联数据、按 id 定位特定节点、折叠这些节点、执行 doLayout()、将结果居中、缩放到适应视口,并在后续的 onNodeClick 中展开已折叠分支。由于图选项中启用了 reLayoutWhenExpandedOrCollapsed,因此在点击处理器内部不需要为分支展开编写手动布局代码。
这里没有使用自定义节点、连线、画布或视口插槽,也没有编辑工作流。相反,外围 UI 来自一个共享的 DraggableWindow 辅助组件。该辅助组件增加了一个浮动说明窗口、一个基于 RGHooks.useGraphStore() 的设置面板、对滚轮和拖拽行为的实时 setOptions() 调用,以及一个基于 prepareForImageGeneration() 和 restoreAfterImageGeneration() 的导出流程。这些工具很有用,但它们属于共享脚手架,而不是这个示例最核心的独特行为。
关键交互
- 只有当
node.expanded === false时点击节点才会展开它,因此这个示例的行为是单向展开查看器,而不是完整的点击切换树。 - 由于启用了
reLayoutWhenExpandedOrCollapsed,树在展开或折叠状态变化后会自动重新排布。 - 浮动辅助窗口可以拖动或最小化,因此说明和工具始终可用,同时不会持续占据画布空间。
- 设置面板可以在运行时切换滚轮和拖拽行为,并将当前图谱下载为图片。
关键代码片段
这段代码展示了该 demo 被配置为一个水平正交树,并在右侧使用内置展开控件。
const graphOptions: RGOptions = {
debug: false,
layout: {
layoutName: 'tree',
from: 'left',
},
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.StandardOrthogonal,
defaultJunctionPoint: RGJunctionPoint.lr,
defaultExpandHolderPosition: 'right',
reLayoutWhenExpandedOrCollapsed: true,
这段代码说明完整数据集会先被加载,然后再按节点 id 有选择地折叠节点。
await graphInstance.setJsonData(myJsonData);
['a', 'b', 'c'].forEach(nodeId => {
const node = graphInstance.getNodeById(nodeId);
if (node) {
graphInstance.collapseNode(node);
}
});
这段代码展示了显式的启动顺序,它会在交互开始之前稳定初始折叠视图。
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
这段代码表明,节点点击只会显示已折叠分支,而不会再次折叠已经展开的分支。
const onNodeClick = async (node: RGNode, $event: RGUserEvent) => {
const isExpanded = node.expanded;
if (isExpanded === false) {
graphInstance.expandNode(node);
}
};
这段代码展示了浮动设置面板如何通过实时更新选项来改变 relation-graph 行为,而不是重新构建图谱。
<SettingRow
label="Wheel Event:"
options={[
{ label: 'Scroll', value: 'scroll' },
{ label: 'Zoom', value: 'zoom' },
{ label: 'None', value: 'none' },
]}
value={wheelMode}
onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>
这个示例的独特之处
这个示例之所以独特,是因为它聚焦于一种非常具体的信息披露模式:整个树在初始数据集中就已经完整存在,选定分支会在 setJsonData() 之后立即被折叠,并且初始视图会在用户开始浏览之前先完成稳定布局。因此,它是一个关于程序化启动折叠的聚焦参考,而不是通用树渲染示例。
与 open-by-level 相比,它强调的是通过直接点击节点逐支显示分支,而不是为整棵树重新计算披露层级深度。与 expand-button 相比,它显示的是已经加载好的后代节点,而不是之后再惰性加载新的子节点。与 graph-instance-api 和 tree-data 相比,它不只是一个简单的实例方法 demo 或被动的树加载示例,而是把启动折叠加节点点击展开作为核心教学流程。
浮动辅助窗口及其设置/导出控件虽然有用,但并不是这里的核心差异点,因为这套外壳也被其他 demo 共享。这个示例最有代表性的组合是:预加载的扁平树、选择性的启动折叠、单向点击展开,以及在一个紧凑的从左到右查看器中的自动重新布局。
这种模式还适用于哪里
这种模式适用于这样一些场景:完整层级已经可用,但如果一开始就显示所有分支,会让用户不堪重负。典型例子包括入门知识地图、部门浏览器、课程模块树、产品分类导航以及故障隔离指南。
当初始界面需要只突出主要决策,同时让细节按需可发现时,这种模式也很合适。如果后续版本需要远程加载分支,这个示例可以作为 UI 基线,然后再把显示逻辑切换为基于 onNodeExpand 的数据获取流程。