JavaScript is required

动画最短路径高亮

这个示例构建了一个只读的关系查看器,用于在一张紧凑图中持续强调两个节点之间的最短路径。画布使用基于图标的圆形节点、带自定义箭头标记的曲线连线,以及暗色霓虹视觉主题。用户可以点击两个节点手动计算一条路径,让示例自动回放随机路径,在 `tree` 和 `center` 布局之间切换,并修改当前活动路径使用的视觉皮肤。

带可选线条效果的动画最短路径高亮

这个示例构建了什么

这个示例构建了一个只读的关系查看器,用于在一张紧凑图中持续强调两个节点之间的最短路径。画布使用基于图标的圆形节点、带自定义箭头标记的曲线连线,以及暗色霓虹视觉主题。用户可以点击两个节点手动计算一条路径,让示例自动回放随机路径,在 treecenter 布局之间切换,并修改当前活动路径使用的视觉皮肤。

这里的重点不是通用的线条主题化。选中的效果类专门保留给当前最短路径,而无关的节点和连线都会被调暗,以便将注意力引导到正在检查的路径上。

数据是如何组织的

图数据以内联方式声明为一个 RGJsonData 对象,其中包含 rootId、扁平的 nodes 数组和扁平的 lines 数组。该示例在同一个枢纽节点周围混合了出向和入向连线,因此高亮路径可以跨越拓扑中的不同区域。

在调用 setJsonData 之前,代码会重写每一条连线,使其使用自定义 SVG marker id,启用起始箭头,并关闭默认的结束箭头。路径查找数据并没有作为单独的静态结构保存。相反,示例会基于 graphInstance.getNodes()graphInstance.getLinks() 重建一个辅助图,然后沿这些连接的任意方向还原出一条最短路径。在生产系统中,这种结构同样可以表示服务依赖、流程交接、仓储路径、基础设施追踪或升级路径。

relation-graph 是如何使用的

RGProvider 提供图实例,而 RGHooks.useGraphInstance() 驱动了几乎所有运行时行为。图的初始配置为 layout.layoutName = 'tree'from = 'left'treeNodeGapH = 100treeNodeGapV = 20defaultJunctionPoint = RGJunctionPoint.ltrbdefaultNodeShape = 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 定时器会把随机节点送入同一套节点点击流程,因此自动播放和手动选路共用同一套路径高亮机制。
  • 浮动面板可以在 treecenter 布局之间切换图形、重新加载数据集,并让用户在 Electric CurrentData 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-animationcustom-line-style 相比,它不会重新主题化每一条已渲染的连线。相反,样式选择器只会改变计算后路径的呈现方式,而非路径元素会被调暗。与 find-min-path 相比,它采用了更轻量的交互模型:两次节点点击加上一个定时器驱动的回放循环,会复用同一套最短路径例程,而不是依赖表单式查询流程。

正是这种组合让该示例脱颖而出。稀缺之处不在于某一个单独成分本身,而在于把实时最短路径计算、仅作用于路径的动画皮肤、自定义 SVG 箭头标记、运行时布局切换以及自动播放驱动的路径回放整合进同一个查看器。因此,与通用的全图线条样式展示相比,它更适合作为路径检查类界面的起点。

这种模式还适用于哪里

这种模式非常适合基础设施与运维图示场景,因为团队需要一次只检查一条路径,而不必编辑图形。例如服务依赖追踪、物流交接地图、告警升级链路、数据血缘查看器以及网络路径演示。

它也适合那些既需要分析能力又需要展示氛围的演示型工具。团队可以保留同一套底层拓扑,为了可读性切换布局,并为演示、调查或导出快照应用不同的路径皮肤,而无需替换 relation-graph 的内置线条渲染器。