树布局方向切换
这个示例构建了一个静态层级查看器,可在运行时将同一棵已加载树在左到右与上到下预设间切换。核心要点是重布局流程:重新应用节点尺寸、默认线形和竖排文字样式,使同一数据在两种方向下都保持可读。
通过实时重布局切换树布局方向
这个示例构建了什么
这个示例构建了一个用于公司 wiki 目录的只读层级查看器。同一棵树无需加载第二份数据集,就可以显示为从左到右的树,或从上到下的树。
用户可以通过一个浮动选择器切换布局方向,通过内嵌的小视图检查图形,并打开共享的画布设置窗口以使用拖拽、滚轮和图片导出工具。这个示例的重点在于:它会对已经加载的图重新布局,并在下一次布局计算前,重新应用与方向相关的节点尺寸、连线设置和文本格式。
数据是如何组织的
数据来自一个异步本地 getMockData() 函数,该函数返回一个 RGJsonData 对象,其中包含 rootId、一个扁平的 nodes 数组和一个扁平的 lines 数组。内容表示一个文档式层级结构:一个根 wiki 目录、若干一级章节,以及更深层的策略、项目和支持分支。
在执行 setJsonData(...) 之前,没有做任何预处理。唯一的运行时转换发生在数据已经挂载之后:当方向发生变化时,代码会选择一个预设,重写每个现有节点的宽度和高度,重写每条现有连线的形状,然后调用 doLayout()。
在真实应用中,同样的结构也可以表示知识库、政策手册、学习目录、内容分类体系,或任何其他需要在多种方向下都保持可读性的树。
relation-graph 是如何使用的
这个示例包裹在 RGProvider 中,MyGraph 使用 RGHooks.useGraphInstance() 作为主要的运行时 API 接口。图由两个 RGOptions 预设驱动。两者都使用 layoutName: 'tree',但水平预设使用 from: 'left'、较大的水平间距、自动尺寸的矩形节点、左右连接点,以及白底节点配灰色连线。垂直预设使用 from: 'top'、高而窄的矩形、上下连接点,以及无边框的灰色节点。
图的初始化方式是先等待 getMockData(),再将结果传给 setJsonData(...)。之后,一个 useEffect 会响应 layoutFrom 的变化并执行重布局流程。代码会调用 setOptions(...),遍历 getNodes() 并配合 updateNode(...) 规范化当前节点尺寸,遍历 getLines() 并配合 updateLine(...) 规范化连线形状,随后通过 sleep(100) 等待,再执行 doLayout()、moveToCenter() 和 zoomToFit()。
这个示例没有使用自定义节点或连线插槽,而是保留内置渲染,并通过选项和 SCSS 来定制它。RGSlotOnView 将 RGMiniView 挂载为视口覆盖层,而包装类会切换到 .my-layout-top,从而让自上而下模式可以对内置节点标签应用 writing-mode: vertical-rl。浮动的 DraggableWindow 是一个共享辅助组件;在它内部,CanvasSettingsPanel 使用 relation-graph hooks 来修改滚轮和拖拽行为,并通过 prepareForImageGeneration() 和 restoreAfterImageGeneration() 导出画布图片。
关键交互
主要交互是布局方向切换。点击 SimpleUISelect 控件会改变 layoutFrom,这会触发对已挂载图的完整重布局,而不是从头重建数据集。
浮动辅助窗口可以拖动、最小化,并展开为设置面板。该面板可以修改滚轮行为、修改画布拖拽行为,并下载生成的图像。
节点和连线的点击处理函数是存在的,但它们只会记录被点击的对象日志,不会驱动布局、导航或编辑。
关键代码片段
下面这个片段说明,数据模型是一个静态的 RGJsonData 树,包含一个根节点以及扁平的节点数组和连线数组。
export const getMockData = async() => {
return {
"rootId": "1",
"nodes": [
{
"id": "1",
"text": "Company Wiki Directory"
},
下面这个片段展示了水平预设:内置的 tree 布局、从左到右方向、较大的水平间距,以及自动尺寸的矩形节点。
const graphOptionsH: RGOptions = {
layout: {
layoutName: 'tree',
treeNodeGapH: 300,
treeNodeGapV: 10,
from: 'left'
},
defaultNodeShape: RGNodeShape.rect,
defaultNodeWidth: 0,
defaultNodeHeight: 0,
下面这个片段证明,方向变化会在下一次布局计算前,先规范化已经加载的节点和连线状态。
const targetOptions = layoutFrom === 'left' ? graphOptionsH : graphOptionsV;
graphInstance.setOptions(targetOptions);
graphInstance.getNodes().forEach((node) => {
graphInstance.updateNode(node, {
width: targetOptions.defaultNodeWidth,
height: targetOptions.defaultNodeHeight
});
});
graphInstance.getLines().forEach((line) => {
graphInstance.updateLine(line, {
lineShape: targetOptions.defaultLineShape
});
});
下面这个片段展示了重布局流程会先等待 DOM 稳定下来,然后重新计算布局并重新适配视口。
await graphInstance.sleep(100);
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
下面这个片段展示了运行时控件如何在水平和垂直预设之间切换。
<SimpleUISelect
data={[
{ value: 'left', text: 'Horizontal Tree' },
{ value: 'top', text: 'Vertical Tree' }
]}
currentValue={layoutFrom}
onChange={(newValue: string) => { setLayoutFrom(newValue); }}
/>
下面这个片段展示了自上而下样式覆写,它会把内置节点标签改为纵向排版。
.my-graph.my-layout-top {
.relation-graph {
.rg-node-peel {
.rg-node {
.rg-node-text {
padding-top: 10px;
justify-content: flex-start;
writing-mode: vertical-rl;
}
}
}
}
}
这个示例的独特之处
对比数据表明,这个示例的突出点并不只是它本身具有布局切换器,而在于它如何处理切换。它最有辨识度的模式,是在普通 tree 预设之间切换同一个已加载的层级结构时,会在 doLayout() 之前显式重写当前节点的宽度、高度和连线形状。
因此,当目标是普通树方向切换,而不是带层级感知的 IO 路由时,它比 io-tree-layout 更适合作为参考。与 bothway-tree2 相比,它关注的是单向层级的可读性,以及对已挂载图对象进行重布局规范化,而不是双向分支和连线标签控制。与 industry-chain 和 layout-folder2 相比,它在视觉上保持节点足够简洁,使核心经验集中在方向相关的尺寸设定、连接点默认值、曲线连接器和标签流向上。
稀有度与对比记录还凸显了一种不常见的视觉组合:水平模式使用自动尺寸的白色矩形,而自上而下模式使用 30 x 260 的窄长矩形并配合纵向文字。当同一层级结构必须在两种差异明显的阅读方向中都保持可读性时,这种组合让该示例成为一个很强的起点。
这种模式还适用于哪里
这种模式适用于文档树、政策手册、课程大纲、产品分类体系以及支持知识库等场景,在这些场景中,同一层级结构可能既需要宽幅的探索视图,也需要窄幅的自上而下展示。
它也可以迁移到那些需要在不重建数据模型的情况下,对已挂载图进行重新设定样式的场景。同样的重布局流程可以适配打印布局、特定语言的标签排版、信息亭显示,或需要在同一棵底层树上切换不同节点尺寸和连接器默认值的视图模式切换。