动画最短路径高亮
这个示例构建了一个只读的关系查看器,用于在一张紧凑图中持续强调两个节点之间的最短路径。画布使用基于图标的圆形节点、带自定义箭头标记的曲线连线,以及暗色霓虹视觉主题。用户可以点击两个节点手动计算一条路径,让示例自动回放随机路径,在 `tree` 和 `center` 布局之间切换,并修改当前活动路径使用的视觉皮肤。
带可选线条效果的动画最短路径高亮
这个示例构建了什么
这个示例构建了一个只读的关系查看器,用于在一张紧凑图中持续强调两个节点之间的最短路径。画布使用基于图标的圆形节点、带自定义箭头标记的曲线连线,以及暗色霓虹视觉主题。用户可以点击两个节点手动计算一条路径,让示例自动回放随机路径,在 tree 和 center 布局之间切换,并修改当前活动路径使用的视觉皮肤。
这里的重点不是通用的线条主题化。选中的效果类专门保留给当前最短路径,而无关的节点和连线都会被调暗,以便将注意力引导到正在检查的路径上。
数据是如何组织的
图数据以内联方式声明为一个 RGJsonData 对象,其中包含 rootId、扁平的 nodes 数组和扁平的 lines 数组。该示例在同一个枢纽节点周围混合了出向和入向连线,因此高亮路径可以跨越拓扑中的不同区域。
在调用 setJsonData 之前,代码会重写每一条连线,使其使用自定义 SVG marker id,启用起始箭头,并关闭默认的结束箭头。路径查找数据并没有作为单独的静态结构保存。相反,示例会基于 graphInstance.getNodes() 和 graphInstance.getLinks() 重建一个辅助图,然后沿这些连接的任意方向还原出一条最短路径。在生产系统中,这种结构同样可以表示服务依赖、流程交接、仓储路径、基础设施追踪或升级路径。
relation-graph 是如何使用的
RGProvider 提供图实例,而 RGHooks.useGraphInstance() 驱动了几乎所有运行时行为。图的初始配置为 layout.layoutName = 'tree'、from = 'left'、treeNodeGapH = 100、treeNodeGapV = 20、defaultJunctionPoint = RGJunctionPoint.ltrb、defaultNodeShape = RGNodeShape.circle,以及 defaultLineShape = RGLineShape.StandardCurve。
该示例保留 relation-graph 的内置渲染器,并通过 slots、defs 和 CSS 对其进行定制。RGSlotOnNode 用图标加文本的节点替换默认节点主体。MySvgDefs 注入自定义箭头标记,以及供电流路线样式使用的 glitch 过滤器。SCSS 文件会针对 .rg-node-peel、.rg-line-peel、.rg-line-bg 和 .rg-line 编写样式,因此示例无需提供自定义线条 slot,也能重设内置节点和连线的样式。
在运行时,setJsonData 负责加载准备好的图,moveToCenter() 和 zoomToFit() 负责归一化视口,updateOptions() 会在另一种布局下重新启动同一份数据集,而 getNodes()、getLinks()、getLines()、updateNode() 和 updateLine() 则用于计算并渲染当前活动路径。共享浮动窗口还会使用 prepareForImageGeneration()、getOptions()、setOptions() 和 restoreAfterImageGeneration(),围绕图形暴露画布设置和图片导出能力。
关键交互
- 点击一个节点会记录一个起始端点。随后点击另一个不同的节点时,会计算最短路径并高亮匹配的节点与连线。
- 连续点击同一个节点两次会被忽略。点击一条连线会清除任何待定的首个节点选择。
- 一个
800ms定时器会把随机节点送入同一套节点点击流程,因此自动播放和手动选路共用同一套路径高亮机制。 - 浮动面板可以在
tree和center布局之间切换图形、重新加载数据集,并让用户在Electric Current与Data Flow两种路径效果之间切换。 - 共享窗口还可以被拖动、最小化、作为画布设置覆盖层打开,并用于将当前图导出为图片。
关键代码片段
这段初始化过程说明,线条标记是在数据准备阶段分配的,也就是图被加载之前。
myJsonData.lines.forEach(line => {
line.endMarkerId = 'my-arrow-001';
line.startMarkerId = 'my-arrow-001-start';
line.showStartArrow = true;
});
myJsonData.lines.forEach((line) => {
line.showEndArrow = false;
});
await graphInstance.setJsonData(myJsonData);
这个定时器会持续回放同一套路径选择流程,而不是另外创建一套自动播放渲染器。
const restartRandomPathTask = () => {
clearInterval(playTimerRef.current);
playTimerRef.current = setInterval(() => {
const nodes = graphInstance.getNodes();
const randomNode = nodes[Math.floor(Math.random() * nodes.length)];
onNodeClick(randomNode);
}, 800);
};
这个点击处理器会把两个端点选择转成一次最短路径查询,并在之后清除待定的起始节点。
const onNodeClick = (node: RGNode, $event?: RGUserEvent) => {
if (checkedNodeIdRef.current) {
if (checkedNodeIdRef.current === node.id) {
return;
}
calcShortestPath(checkedNodeIdRef.current, node.id, graphInstance, itemsOnPathClassName);
checkedNodeIdRef.current = '';
} else {
checkedNodeIdRef.current = node.id;
}
};
这个辅助方法表明,路径查找层是基于运行中的 relation-graph 实例重建的,而不是来自第二份硬编码邻接表。
loadDataFromRelationGraph(graphInstance: RelationGraphInstance) {
this.nodes = graphInstance.getNodes().map(n => {
return { id: n.id, childs: [], indexed: false, parentNode: null };
});
this.edges = graphInstance.getLinks().map(link => {
return { from: link.fromNode.id, to: link.toNode.id };
});
}
这段变更过程只会把所选效果类应用到路径连线上,同时淡化图中的其余上下文。
graphInstance.getLines().forEach((line: RGLine) => {
if (lineIdsOnPath.includes(line.id)) {
graphInstance.updateLine(line, { className: flagClassName });
} else {
graphInstance.updateLine(line, { opacity: 0.2 });
}
});
这个 SCSS 预设表明,动画路径皮肤只是 relation-graph 内置线条层上的普通类名。
.rg-line-peel.my-line-class-12 {
.rg-line-bg {
stroke: #334455;
stroke-width: 6px;
stroke-dasharray: 9, 9, 9;
animation: traffic-slow 4s linear infinite;
}
.rg-line {
stroke: #00d2ff;
stroke-dasharray: 20, 80;
animation: traffic-fast 1.5s linear infinite;
}
}
这个示例的独特之处
对比数据将这个示例放在了线条样式示例与路径分析示例之间。与 custom-line-animation 和 custom-line-style 相比,它不会重新主题化每一条已渲染的连线。相反,样式选择器只会改变计算后路径的呈现方式,而非路径元素会被调暗。与 find-min-path 相比,它采用了更轻量的交互模型:两次节点点击加上一个定时器驱动的回放循环,会复用同一套最短路径例程,而不是依赖表单式查询流程。
正是这种组合让该示例脱颖而出。稀缺之处不在于某一个单独成分本身,而在于把实时最短路径计算、仅作用于路径的动画皮肤、自定义 SVG 箭头标记、运行时布局切换以及自动播放驱动的路径回放整合进同一个查看器。因此,与通用的全图线条样式展示相比,它更适合作为路径检查类界面的起点。
这种模式还适用于哪里
这种模式非常适合基础设施与运维图示场景,因为团队需要一次只检查一条路径,而不必编辑图形。例如服务依赖追踪、物流交接地图、告警升级链路、数据血缘查看器以及网络路径演示。
它也适合那些既需要分析能力又需要展示氛围的演示型工具。团队可以保留同一套底层拓扑,为了可读性切换布局,并为演示、调查或导出快照应用不同的路径皮肤,而无需替换 relation-graph 的内置线条渲染器。