JavaScript is required

自定义连线文字位置

这个示例构建了一棵小型树图,可对比分离标签与路径贴合文字两种边标签模式。悬浮控制窗可调节标签 x/y 偏移、树方向重排以及默认线形切换,是一个用于连线文字可读性调优的紧凑参考。

在树状关系图中调整连线文本位置

这个示例构建了什么

这个示例构建了一个小型树状图,用来对比边标签在不同渲染规则下的表现。画布展示了一个固定的子系统层级结构,同时一个悬浮控制窗口允许用户在分离标签和沿连线路径渲染的文本之间切换,通过 x 和 y 偏移移动标签,旋转树的方向,并切换连接线形状。

视觉上的主要关注点是连线文本本身。蓝色标签样式、克制的节点样式以及紧凑的数据集,让注意力集中在可读性变化上,而不是领域特定内容上。

数据是如何组织的

图数据是在 initializeGraph() 内部创建的一个静态内联 RGJsonData 对象。它使用 rootId: '2',包含九个节点和八条连线,并且每条连线都带有相同的 text: 'Subsystem' 标签,这样该示例就能把关注点放在标签位置行为上,而不是内容差异上。

这里没有外部获取步骤,也没有在 setJsonData() 之前对每条连线进行预处理。关键准备工作发生在 options 对象中:defaultLineShapedefaultJunctionPointdefaultLineTextOnPathdefaultLineTextOffsetXdefaultLineTextOffsetY 都是在图渲染之前从 React state 派生出来的。在真实产品中,这种结构同样可以表示设备层级、组织树、模块依赖,或任何需要在多种方向下保持边文本可读性的树结构。

relation-graph 是如何使用的

这个示例将 RelationGraph 挂载在 RGProvider 内部,然后通过 RGHooks.useGraphInstance() 获取实时实例。首次渲染时,它会调用 setJsonData(),随后应用布局更新,并运行 doLayout()moveToCenter()zoomToFit(),这样这个小型树图会立即以合适的方式呈现在视口中。

布局始终是树布局,但方向是动态的。当 layoutFrom 发生变化时,代码会调用 graphInstance.updateOptions() 来重写 layout.from,并根据树是水平还是垂直方向来交换 treeNodeGapHtreeNodeGapV。默认连接点同样由当前方向和连线形状推导而来,因此标签对比是与连接线几何形态绑定的,而不是绑定在某一种固定的布线样式上。

标签行为主要通过作为 props 传给 RelationGraph 的图选项来驱动。defaultLineTextOnPathdefaultLineTextOffsetXdefaultLineTextOffsetY 直接来自组件 state。defaultLineShape 也来自 state,但当这个 state 改变时,示例会重新加载图,因为该示例专门演示了在添加连线数据时默认连线形状是如何生效的。

这个演示中没有自定义节点、连线、画布或视口 slot,也没有图事件处理器。定制是通过共享辅助组件和样式表覆写来完成的:DraggableWindow 承载悬浮控制区,SimpleUISelect 渲染分段选择器,CanvasSettingsPanel 暴露共享的画布设置和图片导出能力,而 my-relation-graph.scss 则重新着色节点文本,以及 SVG 和 HTML 两种连线标签样式。

关键交互

  • x 偏移滑块将 textOffsetX-100 调整到 100,从而在水平方向移动默认标签位置。
  • y 偏移滑块将 textOffsetY-40 调整到 40,从而在垂直方向移动默认标签位置。
  • 文本模式选择器在 Normal TextText On Path 之间切换,便于对比分离标签与直接挂载在线条上的标签。
  • 树方向选择器会在 leftrighttopbottom 之间切换布局,然后重新执行布局并将图重新居中。
  • 连线形状选择器会在直线、曲线和正交线三种默认形状之间切换,然后重新初始化图,使重新加载的连线继承所选的默认形状。
  • 悬浮窗口可以拖动、最小化,并展开为一个共享设置面板,在其中可以使用画布滚轮模式、拖拽模式和图片导出功能。

关键代码片段

这个片段表明,该示例使用的是固定的内联数据集,而不是从外部数据源加载数据。

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '2', text: 'ALTXX' },
        { id: '3', text: 'CH2 TTN' },
        // ...
    ],
    lines: [
        { from: '2', to: '5', text: 'Subsystem' },
        // ...
    ]
};
await graphInstance.setJsonData(myJsonData);

这个片段是该演示的核心:连线标签模式、偏移、形状和连接点行为都由组件 state 推导出来,并传入 RelationGraph

const graphOptions: RGOptions = {
    defaultLineShape: lineShape,
    defaultJunctionPoint:
        lineShape === RGLineShape.StandardStraight
            ? RGJunctionPoint.border
            : ((layoutFrom === 'left' || layoutFrom === 'right') ? RGJunctionPoint.lr : RGJunctionPoint.tb),
    defaultLineTextOnPath: textOnPath,
    defaultLineTextOffsetX: textOffsetX,
    defaultLineTextOffsetY: textOffsetY,
    layout: { layoutName: 'tree', from: 'left', treeNodeGapV: 30, treeNodeGapH: 150 }
};

这个片段展示了方向变化如何更新实时树布局,以及如何为水平对比和垂直对比交换 gap 值。

if (layoutFrom === 'left' || layoutFrom === 'right') {
    graphInstance.updateOptions({
        layout: { ...layoutOptions, from: layoutFrom, treeNodeGapH: 150, treeNodeGapV: 30 }
    });
} else {
    graphInstance.updateOptions({
        layout: { ...layoutOptions, from: layoutFrom, treeNodeGapH: 30, treeNodeGapV: 150 }
    });
}

这个片段证明,连线形状变化是通过重新构建图数据来处理的,而不只是调用 updateOptions()

useEffect(() => {
    // When lineShape changes, data needs to be re-set because the expected effect
    // of lineShape changes on defaultLineShape takes effect when adding line data
    initializeGraph();
}, [lineShape]);

这个片段展示了悬浮窗口中用于画布设置和截图导出的共享辅助路径。

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

这个示例的独特之处

与附近那些同样聚焦标签的示例,比如 text-on-curvetext-on-orthogonal 相比,这个演示更关注图级默认值,而不是逐条连线编辑。它不会深入到每条边的锚点或折点控制。相反,它专注于观察同一棵固定树在整张图切换为分离标签或文本沿路径模式、同时改变方向和默认连线形状时的表现。

这让它成为更合适的起点,尤其当问题是“在我旋转树或切换布线样式之后,默认边标签是否仍然清晰可读?”时更是如此。对比数据还表明,这个示例在一个紧凑的试验场中组合了多个不常见特性:x 和 y 标签偏移滑块、普通文本与文本沿路径切换、跨连线形状对比,以及带有方向感知连接点变化的四向树重排。

它也不同于 center-layout-optionsgraph-angle-offset 这类布局调优示例。那些示例更接近通用布局调整,而这个示例始终将边标签可读性作为主要主题。

这种模式还适用于哪些地方

这种模式很适合用于内部工具场景,在这些场景中,一套边标签规则必须在多种布局下都能工作,而不需要对每条连接进行手工调优。示例包括子系统图、流程交接树、依赖关系图、审批链,以及在图旋转或重新设定样式后仍需保持相同标签文本可读的组织结构图。

它同样适合作为 QA 沙盒。团队可以把自己的示例数据接入同样的控制模式,在投入更高级的逐线定制之前,先验证默认边标签行为。