JavaScript is required

树布局与连线参数调节

这个示例把固定树数据集变成 relation-graph 的实时调参工作台。用户可调整树方向与间距、批量重设现有连线样式、切换画布交互模式,并在不重建数据的情况下导出当前视图为图片。

通过实时布局与连线控制微调树形图

这个示例构建了什么

这个示例围绕一份预先准备好的层级数据,构建了一个全高度的树形布局交互调试场景。画布中展示的是一棵紧凑的单色蓝色树,带有盒状的连线标签、位于底部居中的内置工具栏,以及悬浮在图上方的白色控制窗口。

用户可以切换树的朝向、调整水平和垂直间距、重新设置所有已渲染连线的样式、移动连线标签偏移、修改滚轮和拖拽行为,并将当前画布导出为图片。这个示例的重点不是数据编辑,而是在已加载的图实例上进行实时展示调优。

数据是如何组织的

图数据以内联方式声明为一个 RGJsonData 对象,包含 rootId、扁平的 nodes 数组和扁平的 lines 数组。每个节点只使用简单的 idtext,每条连线使用 idtextfromto。这样可以让示例聚焦于布局与样式行为,而不是特定业务领域的内容。

在调用 setJsonData() 之前没有预处理步骤。initializeGraph() 会直接加载这份准备好的树数据,然后执行居中与适配。另有一个单独的 React 状态对象保存用于驱动运行时行为的控制值:layoutFromrangeHorizontalrangeVerticallineShapejunctionPointlineRadiustextOffsetXtextOffsetY

在真实项目中,相同的数据结构可以表示组织架构图、产品分类树、依赖树或导航层级。这个控制状态对象也可以来自用户偏好、预设选择器或管理后台的调优面板。

relation-graph 是如何使用的

index.tsx 使用 RGProvider 包裹整个演示,因此子组件可以通过 hooks 访问当前活动的图实例。RelationGraph 以一组图级默认配置进行渲染,使示例在视觉上保持稳定:树形布局、节点和连线共享同一种蓝色、60 x 60 的节点方框,以及位于底部居中的水平工具栏。

主示例组件使用 RGHooks.useGraphInstance() 处理所有图相关操作。它会在初始化时调用一次 setJsonData(),随后在方向或间距控制发生变化时,通过 updateOptions({ layout }) 加上 doLayout() 重新执行布局。对于边的调优,它会通过 getLines() 读取当前已渲染的边,并对每一条边调用 updateLine(),从而在不重建数据集的情况下修改连线几何形态和标签位置。

浮动工具外壳来自 DraggableWindow。在这个共享辅助组件内部,CanvasSettingsPanel 使用 RGHooks.useGraphStore() 读取当前的 dragEventActionwheelEventAction,然后通过 setOptions() 在运行时切换这些行为。这个面板还会在执行 DOM 转图片导出时,配对调用 prepareForImageGeneration()restoreAfterImageGeneration()

这个示例没有定义节点、连线、画布或视口插槽。它依赖 relation-graph 的默认渲染器,并通过 SCSS 覆盖进行定制。样式表会将节点标签设置为白色粗体,同时为连线标签添加白底和蓝色边框,使其看起来像独立的标签块。

关键交互

  • 方向选择器会将 leftrighttopbottom 传给 layout.from,然后重新运行树布局。
  • 两个范围滑块直接映射到 treeNodeGapHtreeNodeGapV,因此用户可以在不改动数据的情况下拉宽或压缩层级结构。
  • 连线形状选择器会统一修改当前所有边的形状。当选择正交形状时,还会出现一个半径滑块,用于调整拐角圆角。
  • 连接点选择器会同时更新每条连线两端的连接点,两个偏移滑块则用于重新定位连线标签。
  • 浮动窗口可以被拖动、最小化,并切换到设置叠层界面。
  • 设置叠层可以将滚轮行为切换为滚动、缩放或无操作;将画布拖拽行为切换为框选、移动或无操作;并将图导出为图片。

关键代码片段

这段代码在任何运行时调优开始之前,先建立固定的树形布局基线和一致的视觉配色。

const graphOptions: RGOptions = {
    defaultLineColor: '#2E74B5',
    defaultLineWidth: 3,
    defaultNodeColor: '#2E74B5',
    defaultNodeWidth: 60,
    defaultNodeHeight: 60,
    toolBarDirection: 'h',
    toolBarPositionH: 'center',
    toolBarPositionV: 'bottom',
    layout: {
        layoutName: 'tree'
    }
};

这条重新布局路径展示了如何将 React 状态转换为实时树布局选项,并应用到当前图实例。

graphInstance.updateOptions({
    layout: {
        layoutName: 'tree',
        from: myTreeGraphOptions.layoutFrom,
        treeNodeGapH: myTreeGraphOptions.rangeHorizontal,
        treeNodeGapV: myTreeGraphOptions.rangeVertical
    }
});
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();

这个循环是批量重设样式的核心模式:它读取已渲染的边,并原地逐条修改。

graphInstance.getLines().forEach((line) => {
    graphInstance.updateLine(line, {
        lineShape: myTreeGraphOptions.lineShape,
        lineRadius: myTreeGraphOptions.lineRadius,
        fromJunctionPoint: myTreeGraphOptions.junctionPoint,
        toJunctionPoint: myTreeGraphOptions.junctionPoint,
        textOffsetX: myTreeGraphOptions.textOffsetX,
        textOffsetY: myTreeGraphOptions.textOffsetY,
        placeText: (myTreeGraphOptions.lineShape === RGLineShape.StandardOrthogonal) ? 'end' : undefined
    });
});

这个选择器让示例成为一个支持四个方向的树朝向调试场,而不是一个单方向查看器。

<SimpleUISelect
    data={[
        { value: 'left', text: 'Left to Right' },
        { value: 'right', text: 'Right to Left' },
        { value: 'top', text: 'Top to Bottom' },
        { value: 'bottom', text: 'Bottom to Top' }
    ]}
    currentValue={myOptions.layoutFrom}
    onChange={(newValue: string) => { myOptionsUpdater({ ...myOptions, layoutFrom: newValue }); }}
/>

这个共享辅助函数表明,图片导出是通过 relation-graph 的准备与恢复 API 实现的,而不是仅仅依赖一次原始截图调用。

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();

这个示例的独特之处

对比数据表明,这个示例与 tree-distancelayout-treecustom-line-style 较为接近,但它强调的是另一种控制组合。它最突出的区别在于:同一个浮动面板会在同一份静态数据集上,同时控制树的重新布局参数和全图范围的连线几何属性。

  • tree-distance 相比,这个示例不仅控制间距,还增加了四方向朝向切换和批量边几何更新。
  • layout-tree 相比,它更像一个连续参数工作台,而不是一个精心设计的预设切换器。
  • custom-line-style 相比,它关注的是 relation-graph 的连线属性,例如形状、连接点、圆角半径和标签偏移,而不是 CSS 皮肤样式族。
  • layout-folder2 相比,它去除了特定业务领域的节点卡片和文件夹布局语义,从而让可复用的重点保持在树与连线 API 上。

共享的可拖拽窗口、滚轮模式切换、拖拽模式切换和图片导出都是实用的辅助功能,但对比记录并不支持把这些工具视为该示例的唯一标识。这个示例真正独特的价值,在于运行时同时控制树方向、间距和边几何属性。

这种模式还适用于哪里

  • 一个组织架构图查看器,允许用户在导出演示截图之前调节层级密度和连接线样式。
  • 一个分类体系或目录浏览器,不同团队可以在同一份数据上使用不同的树方向和间距预设。
  • 一个依赖树或工作流树检查器,在调试过程中需要实时重新定位边标签以减少重叠。
  • 一个内部调试场,用于先在代表性样本数据上测试 relation-graph 布局参数,再将产品默认值固化到代码中。