连通子关系网布局切换
这个示例构建了一个单一的 relation-graph 画布,其中包含两个彼此断开的图岛,并允许用户只对当前点击节点周围的图岛重新布局。界面组合了主图、小地图、一个浮动辅助窗口,以及一个显示在当前活动编辑分组上方的小型预设选择器。
为选中的连通子网络切换布局
这个示例构建了什么
这个示例构建了一个单一的 relation-graph 画布,其中包含两个彼此断开的图岛,并允许用户只对当前点击节点周围的图岛重新布局。界面组合了主图、小地图、一个浮动辅助窗口,以及一个显示在当前活动编辑分组上方的小型预设选择器。
核心行为是“以选中范围为作用域的重新布局”。这个示例不是切换整个画布的布局,而是检测被点击节点所属的连通子网络,并仅对这一组应用四种预设之一:center、从左到右的 tree、circle,或经过调优的 force 布局。
数据是如何组织的
图数据由两个内联的 RGJsonData 对象组装而成。每个数据集都声明了一个 rootId、一个 nodes 数组,以及一个带有显式连线 id 的 lines 数组。这个示例不会在布局前先把数据预处理成单独的分组模型,而是通过 addNodes() 和 addLines() 将两个数据集都追加到同一个已挂载的图实例中,执行 doLayout(),然后在运行时依赖图的连通性来发现当前活动的子网络。
这种结构很适合真实应用中多个独立关系分组共享同一个工作区的场景。相同的数据形态可以表示彼此独立的业务单元、多个事件簇、互不连通的知识图谱社区,或者加载到同一个分析画布中的多个依赖关系孤岛。
relation-graph 是如何使用的
页面由 RGProvider 包裹,MyGraph 则使用 RGHooks.useGraphInstance() 直接操作当前活动的图实例。初始图配置关闭了调试模式,使用矩形节点,并以 exampleLayouts 中的第一个预设作为初始布局,也就是降低了 distanceCoefficient 的 center 布局。
这个示例把 relation-graph 实例 API 作为主要控制面。它通过 addNodes() 和 addLines() 追加两个数据集,调用 doLayout(),然后使用 moveToCenter() 和 zoomToFit() 对齐视口。节点被点击后,它会通过 getNetworkNodesByNode() 解析出对应的连通分量,使用 setEditingNodes() 保存这组节点,并在后续通过创建新的布局实例并调用 placeNodes(groupNodes, checkedNode),只对这些节点重新布局。
视图层 UI 通过 RGSlotOnView 注入。在这个插槽内部,RGMiniView 提供小地图,RGEditingNodeController 则把布局选择器锚定到当前正在编辑的选区上。这样一来,这个选择器给人的感觉就是局部作用于所选子网络,而不是全局作用于整个页面。
浮动辅助窗口来自共享的 DraggableWindow 组件。在这个示例中,它提供了使用说明、拖拽和最小化行为,以及访问共享设置面板的入口。该设置面板通过 RGHooks.useGraphInstance() 和 RGHooks.useGraphStore() 在运行时切换 wheelEventAction 和 dragEventAction,并通过调用 prepareForImageGeneration()、getOptions() 和 restoreAfterImageGeneration() 来导出画布图像。
样式刻意保持得比较克制。示例专用的 SCSS 基本保留了图的默认样式,只对 .simple-ui-select 重新着色,让这些布局选项以洋红色强调更醒目。
关键交互
- 点击某个节点会设置
checkedNodeId,解析该节点周围的连通分量,并将这个分量设为当前活动编辑分组。 - 从覆盖层选择器中选择一个预设,会创建对应的布局实例,并仅围绕所选节点重新定位当前活动分组。
- 点击画布会清除选中状态、移除编辑分组,并退出当前的重新布局上下文。
- 浮动窗口可以拖拽和最小化,它的设置模式还可以切换滚轮与拖拽行为,或导出当前画布图像。
关键代码片段
这个预设表定义了四种可应用于所选分组的布局家族。
const exampleLayouts: { label: string, layoutOptions: RGLayoutOptions }[] = [
{
label: 'Center',
layoutOptions: {
layoutName: 'center',
distanceCoefficient: 0.5
}
},
{
label: 'Tree',
layoutOptions: {
layoutName: 'tree',
from: 'left'
}
},
// ...
];
这个初始化步骤会把两个相互独立的数据集合并到同一个图实例中,并让它们适配共享画布。
graphInstance.addNodes(network1JsonData.nodes);
graphInstance.addLines(network1JsonData.lines);
graphInstance.addNodes(network2JsonData.nodes);
graphInstance.addLines(network2JsonData.lines);
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
这段选择流程会把一个被点击的节点转换成一个连通的编辑分组。
const getGroupNodes = (node: RGNode) => {
return graphInstance.getNetworkNodesByNode(node);
}
const selectGroupNodes = () => {
if (!checkedNodeId) {
return;
}
const checkedNode = graphInstance.getNodeById(checkedNodeId);
if (checkedNode) {
const groupNodes = getGroupNodes(checkedNode);
graphInstance.setEditingNodes(groupNodes);
}
}
这个重新布局处理器只会把所选预设应用到当前活动的连通分量上。
const onChangeNextworkLayout = (layoutLabel: string) => {
const layoutInfo = exampleLayouts.find(n => n.label === layoutLabel);
if (!layoutInfo) return;
const layout = graphInstance.createLayout(layoutInfo?.layoutOptions);
const checkedNode = graphInstance.getNodeById(checkedNodeId);
if (!checkedNode) return;
const groupNodes = getGroupNodes(checkedNode);
layout.placeNodes(groupNodes, checkedNode);
graphInstance.setEditingNodes(groupNodes);
};
这个视图层插槽会持续显示小地图,并在当前活动编辑选区旁渲染选择器。
<RGSlotOnView>
<RGMiniView />
<RGEditingNodeController>
<div className="absolute transform translate-y-[-40px] pointer-events-auto">
<div className="flex gap-2 flex-nowrap w-[220px]">
<SimpleUISelect
data={exampleLayouts.map(n => ({ text: n.label, value: n.label }))}
currentValue={''}
onChange={(clickedLabel: string) => {
onChangeNextworkLayout(clickedLabel);
}}
/>
</div>
</div>
</RGEditingNodeController>
</RGSlotOnView>
这个示例的独特之处
与附近的布局演示(例如 switch-layout 和 layout-tree)相比,这个示例不会把布局切换视为作用于整个画布的操作。它的独特模式在于组合使用 getNetworkNodesByNode()、setEditingNodes() 和 createLayout(...).placeNodes(...),从而把重新布局的作用域限制在被点击节点周围的连通分组内。
它与这些相邻示例的另一个差异在于:它会把两个彼此断开的数据集加载到同一个图实例中。因此,对于包含多个图岛的画布来说,这个示例更具参考价值,因为用户可能希望只重新排列其中一个图岛,而不影响其他部分。
与 change-line-vertices 和 freely-draw-lines-on-canvas 这类更偏编辑器的示例相比,这里的覆盖层控制器用于布局调整,而不是拓扑结构创作。最终效果落在了偏查看器的布局演示与完整图编辑器之间的中间地带。
这种模式还适用于哪些地方
这种模式很适合迁移到分析工具中,尤其是在用户需要整理或比较某个局部簇、又不想重建整张图的时候。例如,可以在更大的组织架构图中重组某一个部门,在依赖关系图中重新排布某一个服务簇,在运维分析中隔离某一条事件链,或在包含多个断开主题的知识图谱中调整某一个社区。
它也适合分阶段编辑工作流。产品可以保持图结构为只读,仅暴露以选区为作用域的布局控制,同时仍然为用户提供一种在拥挤的共享画布中提升可读性的实用方式。