JavaScript is required

中心布局层级间距设置

这个示例构建了一个用于 `center` 布局的全视口 relation graph 演示,并在画布上方叠加了一个悬浮控制窗口。图本身是一个固定的分支层级结构,以小型青绿色圆点和青绿色连线渲染,因此布局变化会始终保持清晰可见。

在运行时调整 Center 布局间距

这个示例构建了什么

这个示例构建了一个用于 center 布局的全视口 relation graph 演示,并在画布上方叠加了一个悬浮控制窗口。图本身是一个固定的分支层级结构,以小型青绿色圆点和青绿色连线渲染,因此布局变化会始终保持清晰可见。

用户可以在两种间距策略之间切换。一种模式通过滑块调整单个 distanceCoefficient 值,另一种模式则通过逗号分隔的文本输入框和预设快捷项来编辑各层的明确间距。这个悬浮窗口还支持拖拽、最小化、打开画布设置浮层,以及将当前图视图导出为图片。

重点不在示例数据本身。重点在于,同一套层级结构可以通过全局间距倍数或按层级的间距数组,在运行时实时重新布局。

数据是如何组织的

图数据以内联方式声明为一个 RGJsonData 对象,其中包含 rootId: 'a'、一个 nodes 数组和一个 lines 数组。在执行 setJsonData() 之前,并没有对图数据负载做任何预处理。初始化流程只是加载这份静态数据集,将视口居中,并让图适配可用空间。

这个示例中唯一的运行时预处理针对的是布局参数,而不是图数据。在层级距离模式下,levelDistance 字符串会按逗号拆分,使用 parseInt 转换后传给 layout.levelGaps

在真实项目中,这种相同的数据形态可以表示组织层级、产品分类树、能力地图、依赖树,或任何不同深度层级需要不同视觉间距的层级结构。

relation-graph 是如何使用的

页面包裹在 RGProvider 中,示例组件和共享设置面板都通过 RGHooks.useGraphInstance() 读取当前激活的图实例。图通过 RelationGraph 渲染,使用的是一个较小的初始 options 对象,而不是自定义节点或连线插槽。

初始图配置将 layout.layoutName 设为 center,关闭调试输出,并在代码中定义主要视觉默认值:50x50 节点、圆形节点形状、无节点边框,以及青绿色节点和连线颜色。组件挂载后,会通过 setJsonData() 加载内联层级数据,然后调用 moveToCenter()zoomToFit()

运行时的布局调整通过仅重建 options 中的 layout 部分来完成。当 configType === 1 时,代码写入 distanceCoefficient;否则会解析逗号分隔的输入并写入 levelGaps。随后它通过 setOptions({ layout }) 应用布局,并显式调用 doLayout()

悬浮工具窗口来自共享的 DraggableWindow 组件。该组件还会使用 RGHooks.useGraphStore()setOptions() 来切换 wheelEventActiondragEventAction,并在截图导出期间使用 prepareForImageGeneration()getOptions()restoreAfterImageGeneration()

这个示例没有使用 relation-graph 插槽、自定义节点模板或编辑 API。它引入了一个本地样式表,并在 .my-graph 作用域下定义了选中状态覆盖样式,但这个演示中的可见定制主要仍然来自图配置和共享悬浮窗口。

关键交互

最主要的交互是在两种间距策略之间切换模式。选择器只会显示 distanceCoefficient 的范围滑块,或显示基于文本的 levelGaps 编辑器,两者不会同时出现。

在 coefficient 模式下,拖动滑块会更新 React state,并立即触发一次新的布局计算。在 level-gap 模式下,用户可以输入类似 100,150,200,250,300 这样的逗号分隔列表,或点击其中一个预设字符串,把现成的间距模式写入同一个 state 变量。

图数据保持只读,但整个工作区仍然是可交互的。悬浮控制窗口可以拖拽和最小化,其设置浮层可以切换画布的滚轮和拖拽行为,同一个浮层还可以导出当前图像。

关键代码片段

这段代码表明,该演示以 center 布局启动,并刻意保持视觉默认值简单,以便让间距变化成为最主要的视觉信号。

const graphOptions: RGOptions = {
  debug: false,
  defaultNodeWidth: 50,
  defaultNodeHeight: 50,
  defaultLineColor: 'rgba(0, 186, 189, 1)',
  defaultNodeColor: 'rgba(0, 206, 209, 1)',
  defaultNodeShape: RGNodeShape.circle,
  defaultNodeBorderWidth: 0,
  layout: {
    layoutName: 'center',
    distanceCoefficient: 1
  }
};

这段代码说明,图数据负载是一个固定的内联层级结构,只会加载一次,之后便将其居中并适配到视口中。

const myJsonData: RGJsonData = {
  rootId: 'a',
  nodes: [
    { id: 'a', text: 'a' },
    { id: 'b', text: 'b' },
    { id: 'b1', text: 'b1' },
    // ...
  ],
  lines: [
    { id: 'l-1', from: 'a', to: 'b' },
    { id: 'l-2', from: 'b', to: 'b1' },
    // ...
  ]
};

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

这段代码证明,这个示例通过只重建布局选项并重新执行 doLayout(),来对比两种布局间距模式。

const layoutOptions: RGLayoutOptions = {
  layoutName: 'center'
};

if (configType === 1) {
  layoutOptions.distanceCoefficient = distanceCoefficient;
} else {
  const _levelDistance = levelDistance.split(',').map(i => parseInt(i, 10));
  layoutOptions.levelGaps = _levelDistance;
}

graphInstance.setOptions({
  layout: layoutOptions
});
await graphInstance.doLayout();

这段代码展示了 UI 如何让两种间距控制保持互斥,并将它们都连接回 React state。

<SimpleUISelect
  data={[
    { value: '1', text: 'Set by Distance Coefficient' },
    { value: '2', text: 'Set by Level Distance' }
  ]}
  currentValue={configType}
  onChange={(newValue: string) => { setConfigType(parseInt(newValue)); }}
/>
<div style={{ display: configType === 1 ? 'block' : 'none', paddingTop: '10px' }}>
  <div className="py-1">Distance Coefficient: {distanceCoefficient}</div>
</div>

这段代码说明,共享浮层不只是装饰;它会直接驱动 relation-graph 画布行为和图片导出流程。

const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
  graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
  backgroundColor: graphBackgroundColor
});
if (imageBlob) {
  downloadBlob(imageBlob, 'my-image-name');
}
await graphInstance.restoreAfterImageGeneration();

这个示例的独特之处

与附近那些布局实验示例相比,这个示例紧紧聚焦于一个问题:当你在 distanceCoefficientlevelGaps 之间切换时,center 布局内部的间距会如何变化。因此,相比 graph-angle-offsetswitch-layout,它更具体,因为后者比较的是多种布局类型或朝向设置,而不是深入研究单一布局的间距规则。

layout-center 相比,这个演示避免了对节点和连线进行批量重设样式,并让图的外观几乎保持不变。这个居中的青绿色层级结构充当了视觉探针,因此可见差异完全来自间距本身。

tree-distance 相比,它将相同的运行时调节模式应用在 center 布局上,而不是方向性树布局上。这里最有辨识度的组合是:一个小型只读居中图、两种互斥的间距模式、原始和预设的 levelGaps 字符串,以及一个可复用的悬浮工具窗口,后者带有画布模式切换和图片导出功能。

这一模式还能用在哪里

这一模式非常适合用于内部工具中,让团队在最终确定默认值前先校准层级间距。示例包括组织架构图间距实验、技能树调优、产品分类查看器、依赖关系浏览器,以及深度不均的能力地图。

它也适合作为一种可复用的小型图工作台模式:保持一份有代表性的数据集不变,只暴露少量布局参数,并针对实时图实例重新执行布局。当目标是参数验证或设计评审,而不是完整的图编辑时,这种方式尤其有用。