自定义线条箭头预设
这个示例展示如何定义 4 组自定义 SVG 箭头 marker,并通过 relation-graph 的 `defaultLineMarker` 选项应用。悬浮控制窗可切换箭头预设,并在树布局与中心布局间对比同一层级数据,同时提供共享的画布设置与图片导出。
在树形布局与中心布局中比较自定义箭头预设
这个示例构建了什么
这个示例构建了一个小型子系统层级查看器,并把它变成了一个箭头样式对比实验台。图谱初始为一个从左到右的树形结构,使用矩形节点、曲线连接线、圆角折线拐点,以及沿连线路径渲染的标签。一个浮动面板允许用户在四种自定义 SVG 箭头预设之间切换,并使用 tree 或 center 布局重新渲染同一份层级数据;共享的面板外壳还额外提供了拖拽、最小化、设置和图片导出控制。
这个示例的重点并不是自定义连线渲染本身,而是集中展示通过 defaultLineMarker 传入 marker 对象,并在已经加载的图实例上更新这些选项后,内置的 relation-graph 连线渲染器可以被推进到什么程度。
数据是如何组织的
图数据在 initializeGraph() 内部以内联方式组装为一个 RGJsonData 对象。它包含一个固定的 rootId、十五个具名节点,以及十四条全部标记为 Subsystem 的连线。这里没有 fetch 步骤,没有外部归一化步骤,也没有按连线单独提供的样式载荷。
唯一的预处理是模块级的 myArrows 数组。每一项都是一个原始 SVG marker 定义,带有各自的 viewBox、marker 尺寸、参考点和路径数据。React state 负责选中其中一个定义,而这个 marker 会成为整个图谱的默认箭头。
在真实系统中,同样的数据结构可以表示设备层级、服务依赖、工作流阶段,或任何需要在整张图中统一评估单一连线末端约定的小型有根结构。
relation-graph 是如何使用的
index.tsx 使用 RGProvider 包裹这个示例,MyGraph.tsx 则渲染一个带有单个 graphOptions 对象的 RelationGraph 实例。这些选项建立了初始的从左到右树形布局,设置了明确的节点间距,把内置工具栏放在底部居中位置,启用了折线圆角,并保持连线标签附着在连线路径上。
这个示例依赖 RGHooks.useGraphInstance(),而不是重建图组件。挂载时它会调用 setJsonData(...),然后执行 moveToCenter() 和 zoomToFit()。当布局模式变化时,它会再次调用 updateOptions(...)、doLayout()、moveToCenter() 和 zoomToFit()。当选中的箭头预设变化时,它只更新 defaultLineMarker,从而保持数据集不变,只在原地改变呈现效果。
这个演示没有使用节点插槽、连线插槽、画布插槽或编辑 API。它停留在 relation-graph 的内置渲染器上,并通过选项和本地样式表覆写来进行定制。SCSS 将图谱限制在 .my-graph 下,并强制 .rg-node 使用白色背景渲染,这使得在浅色画布上更容易辨认不同的 marker 形状。
浮动面板也是实现模式的一部分。DraggableWindow 提供可移动的外壳,SimpleUISelect 提供芯片式选择器,而共享的 CanvasSettingsPanel 通过图实例 API 增加了滚轮模式切换、拖拽模式切换和图片导出能力。这些工具在页面中都是真实功能,但它们属于继承而来的脚手架,而不是本示例最独特的教学重点。
关键交互
- 选择
My Arrow 1到My Arrow 4会更新myArrowIndex,并立即替换活动图谱上的defaultLineMarker。 - 在
Tree和Center之间切换会改变layoutName,同时在调用doLayout()之前重新映射节点形状、连线形状和连接点行为。 - 浮动控制窗口可以通过标题栏拖动,也可以通过最小化按钮折叠。
- 齿轮按钮会打开一个共享设置浮层,其中滚轮行为可以在
scroll、zoom和none之间切换,画布拖拽行为可以在selection、move和none之间切换。 - 共享设置浮层可以通过准备图谱 DOM、使用
modern-screenshot将其渲染为 blob,并下载结果,来导出当前图谱图片。
关键代码片段
这个 marker 数组是核心的自定义输入:示例定义了原始 SVG 箭头,而不是替换 relation-graph 的连线渲染器。
const myArrows = [
{
viewBox: '0 0 12 12',
markerWidth: 30,
markerHeight: 30,
refX: 3,
refY: 3,
data: 'M 0 0, V 6, L 4 3, Z'
},
// ...
];
这个选项块表明,初始树形布局和自定义 marker 是一起配置在主图实例上的。
const graphOptions: RGOptions = {
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.StandardCurve,
defaultJunctionPoint: RGJunctionPoint.lr,
toolBarDirection: 'h',
toolBarPositionH: 'center',
toolBarPositionV: 'bottom',
defaultPolyLineRadius: 10,
defaultLineTextOnPath: true,
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 200,
treeNodeGapV: 10
},
defaultLineMarker: myArrows[myArrowIndex]
};
这个分支证明,布局切换不仅仅是改一个布局名称,它还会在重新执行布局前切换节点几何形态和连接器行为。
if (layoutName === 'center') {
graphInstance.updateOptions({
defaultNodeShape: RGNodeShape.circle,
defaultLineShape: RGLineShape.StandardStraight,
defaultJunctionPoint: RGJunctionPoint.border,
layout: {
layoutName: layoutName
}
});
}
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
这个 effect 展示了实时切换 marker 的模式:只有选中的箭头选项会变化,而已加载的数据保持不动。
useEffect(() => {
graphInstance.updateOptions({
defaultLineMarker: myArrows[myArrowIndex]
});
}, [myArrowIndex]);
这个示例的独特之处
对比数据表明,箭头预设选择和布局选择在这组示例中都属于很少见的特性,而这两者的组合正是这个页面突出的主要原因。它并不只是展示一次自定义连线样式,而是在本地定义了四个原始 SVG marker 对象,通过 defaultLineMarker 应用它们,并让同一份数据集同时在曲线的从左到右树形布局和直线的中心布局中接受比较。
与 custom-line-animation 和 custom-line-style 相比,这个示例更强调端点 marker 的几何形态,而不是 CSS 类切换、动画预设或描边与标签的皮肤化。与 div-on-line 相比,它是一个面向整图的箭头对比演示,而不是针对选中边的几何或 DOM 覆盖层演示。与 node-style2 相比,它从类似的树形基线出发,但在不改变底层层级结构的前提下,把这条基线变成了一个双模式展示对比。
最终,它为那些希望在投入更深层自定义连线渲染之前,先测试箭头可读性和方向语义的团队,提供了一个更聚焦的起点。
这种模式还适用于哪里
当团队需要先在一份稳定的数据集上验证连线末端语义,然后再构建更专业的渲染方案时,这种模式可以复用。示例包括需要用不同箭头表示审批、驳回或交接状态的工作流图;必须同时在层级布局和中心枢纽布局中评审的设备与网络拓扑;以及无需重写图数据、只比较连接器预设的设计系统演示页面。
它也适合内部 QA 或评审工具,在这些工具中,分析人员需要一个小型选项面板、导出能力,以及在多种连接器样式之间快速进行视觉比较。可迁移的经验是:保持数据模型简单,保留内置渲染器,并使用运行时选项更新在同一张图上比较不同的展示选择。