[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:simulated-data-flow-path":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 关系图上的最短路径数据流回放\n\n## 这个示例构建了什么\n\n这个示例构建了一个只读的技术网络查看器，把最短路径查询转化为可见的数据流回放。画布一开始会加载一张准备好的图，弱化不在所选路径上的节点和连线，然后按段依次播放所选路径上的动画，让流向更容易被看清。\n\n在视觉上，这个示例使用了深色遥测风格背景、发光的连线处理，以及圆角图标式节点胶囊，而不是默认的节点标签。用户可以通过选择两个不同节点来触发一条路径，在树形布局和中心布局之间切换，修改回放速度，拖动或最小化悬浮控制窗口，打开共享画布设置，并把当前图导出为图片。首次渲染时，示例还会自动播放一条从 `H-7-21` 到 `2` 的默认路径。\n\n## 数据是如何组织的\n\n图数据在 `initializeGraph()` 内联组装为一个静态 `RGJsonData` 对象，其中包含 `rootId`、扁平的 `nodes` 数组以及扁平的 `lines` 数组。每条连线都带有 `from`、`to` 和 `text`，有些连线指向根节点，有些则背离根节点，这使得回放逻辑确实需要处理混合的可视方向。\n\n在运行 `setJsonData(...)` 之前，示例会先预处理每条连线：挂上自定义 marker ID、开启起始箭头，并关闭终点箭头。路径搜索并不是针对另一份独立数据副本完成的。相反，`calcShortestPath(...)` 会根据当前 `relation-graph` 的节点和连线重建一份辅助图，找出节点路径，再把该节点路径映射回连线 ID，并弱化路径之外的所有内容。\n\n在真实应用里，这样的结构可以表示服务依赖、ETL 阶段、设备跳转、队列传输、审批链，或任何需要用户查看某个对象如何在两个已知端点之间流动的管道。\n\n## relation-graph 是如何使用的\n\n这个示例包裹在 `RGProvider` 中，而 `RGHooks.useGraphInstance()` 是核心运行时 API。初始 `RGOptions` 设置了 `RGJunctionPoint.ltrb`、圆形节点、`RGLineShape.StandardCurve`，以及从左到右的树形布局。数据加载完成后，代码通过 `setJsonData(...)`、`moveToCenter()`、`zoomToFit()` 和 `setCanvasOffset(...)` 来构建并定位图。后续布局选择器会更新 `layout.layoutName` 并重新执行初始化，从而让同一份数据集可以在 `tree` 或 `center` 模式下重新回放。\n\n最重要的定制点是 `RGSlotOnLine`。`MyLineContent` 替换了默认连线表面，请求图实例生成路径几何信息，计算 SVG 路径长度，写入 `--rg-line-path` 和 `--my-step-time` 之类的 CSS 变量，并在活动段上按条件渲染一个小的移动矩形。这个 slot 还会把点击事件继续转交回 `relation-graph`，因此自定义渲染不会破坏正常的连线点击事件链路。\n\n`RGSlotOnNode` 的使用相对轻一些。它把每个节点渲染成一个带 `LandPlotIcon` 和文字的圆角胶囊，而 SCSS 覆盖样式则提供深色背景、青色边框、选中状态下标签显示、已播放路径的描边动画，以及反向播放样式。`MySvgDefs` 提供了加载后连线所使用的自定义起止 marker 定义。\n\n悬浮的 `DraggableWindow` 是共享的示例脚手架，但它在这里依然重要，因为它承载了布局选择器、速度选择器、画布行为设置以及截图导出操作。在这个共享面板内部，`RGHooks.useGraphStore()` 暴露了当前的滚轮和拖拽模式，`RGHooks.useGraphInstance()` 则为图片导出提供 `prepareForImageGeneration()` 和 `restoreAfterImageGeneration()`。这里仍然是一个查看器，而不是编辑器：用户可以检查、回放、重新布局和导出这张图，但不会创建节点和连线，也不会重新连接它们。\n\n## 关键交互\n\n主要交互是两步式节点选择。第一次点击节点会保存源节点 ID，第二次点击另一个不同节点时会计算最短路径并重新开始回放。连续点击同一个节点两次不会发生任何事。尽管悬浮面板中的辅助文字比代码表达得更宽泛，但这才是实际实现的行为。\n\n回放本身由定时器驱动。每一步都会把当前连线标记为活动状态，把之前访问过的连线标记为已播放，突出显示当前接收节点，然后在选定的延迟后安排下一步。当所选路径沿着某条连线时，其方向与该连线记录的 `from -> to` 方向相反，示例就会设置 `reverseLine`，让绘制动画和移动 marker 在该段上反向运行。\n\n还有一些会明显影响体验的辅助交互。点击连线会清除任何待完成的首次节点选择。修改回放速度会重新启动当前路径，使新的时序立即生效。切换布局会用同一份数据集重建图，然后在新的排布上重新执行同类数据流交互。悬浮窗口可以拖动、最小化、打开为设置覆盖层，并用于修改滚轮或拖拽行为，或者下载图的图片。\n\n## 关键代码片段\n\n这个片段说明，示例从普通的 `relation-graph` 配置开始，然后构建本地数据集，而不是从别处获取数据。\n\n```tsx\nconst graphOptions: RGOptions = {\n    defaultJunctionPoint: RGJunctionPoint.ltrb,\n    defaultNodeShape: RGNodeShape.circle,\n    defaultLineShape: RGLineShape.StandardCurve,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 100,\n        treeNodeGapV: 20\n    }\n};\n```\n\n这个片段证明，连线 marker 会在加载前预处理，而且首次渲染后会立即开始默认回放。\n\n```tsx\nmyJsonData.lines.forEach(line => {\n    line.endMarkerId = 'my-arrow-001';\n    line.startMarkerId = 'my-arrow-001-start';\n    line.showStartArrow = true;\n});\nmyJsonData.lines.forEach((line) => {\n    line.showEndArrow = false;\n});\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\ngraphInstance.setCanvasOffset(canvasOffset.x, canvasOffset.y + 100);\nshowDataFlow('H-7-21', '2');\n```\n\n这个片段展示了，路径回放由两次不同的节点点击触发，并且会基于实时图实例重新计算。\n\n```tsx\nconst onNodeClick = (node: RGNode) => {\n    if (checkedNodeIdRef.current) {\n        if (checkedNodeIdRef.current === node.id) {\n            return;\n        }\n        showDataFlow(checkedNodeIdRef.current, node.id);\n        checkedNodeIdRef.current = '';\n    } else {\n        checkedNodeIdRef.current = node.id;\n    }\n};\nconst showDataFlow = (fromNodeId: string, toNodeId: string) => {\n    const { lineIdsOnPath, nodeIdsOnPath } = calcShortestPath(fromNodeId, toNodeId, graphInstance, '');\n    allLineIdsOnPathRef.current = lineIdsOnPath;\n    allNodeIdsOnPathRef.current = nodeIdsOnPath;\n    restartTask();\n};\n```\n\n这个片段展示了，最短路径辅助逻辑会从当前 `relation-graph` 状态重建其工作图，而不是依赖一份独立索引。\n\n```ts\nconst graphDb = new ShortestPathGraph();\ngraphDb.loadDataFromRelationGraph(graphInstance);\nconst nodeIdsOnPath = graphDb.findPath(fromNodeId, toNodeId);\nif (!nodeIdsOnPath) {\n    throw new Error('Cannot find the association between the start node and the end node!');\n}\nconst lineIdsOnPath = getAllLineIdsOnPath(nodeIdsOnPath, graphInstance);\ndrawMinPath(nodeIdsOnPath, lineIdsOnPath, graphInstance, itemsOnPathClassName);\n```\n\n这个片段展示了，自定义连线 slot 如何把生成出的路径几何信息转为基于 CSS 的运动渲染。\n\n```tsx\nconst linePathInfo = useMemo\u003CRGLinePathInfo>(() => graphInstance.generateLinePath(lineConfig), [lineConfig]);\nconst pathEl = document.createElementNS('http://www.w3.org/2000/svg', 'path');\npathEl.setAttribute('d', linePathInfo.pathData);\nconst pathLength = pathEl.getTotalLength();\nreturn (\u003Cg\n    className={`${lineConfig.line.data?.played ? (lineConfig.line.data?.reverseLine ? 'my-draw-path-animation reverse-line' : 'my-draw-path-animation') : ''}`}\n    style={{\n        '--rg-line-path': `path('${linePathInfo.pathData}')`,\n        '--my-line-path-length': pathLength + 'px',\n        '--my-step-time': `${lineConfig.line.data?.myStepTime || 1000}ms`\n    }}>\n```\n\n## 这个示例的独特之处\n\n根据对比数据，它功能上最接近的邻近示例是 `line-style-pro`，但侧重点不同。`line-style-pro` 展示的是在内置连线表面上复用路径效果主题，而这个示例更深入地推进了 slot 级别的运动渲染：它会计算一条路径，用自定义连线 slot 替换当前活动路径，并通过移动 marker 和描边绘制时序为每一段添加动画。\n\n对比文件还把这个示例和 `use-dagre-layout-2`、`layout-tree` 这类以布局为重点的查看器区分开来。那些示例把重新布局本身作为主要教学点。而在这里，运行时切换布局是次要能力。它存在的目的，是为了让同一种最短路径回放交互能在不同排布下重新演示，而不是单独讲解布局调优。\n\n相较于 `table-relationship` 这类 heavily slot 化的技术查看器，以及 `line-shape-and-label` 这类以连线为中心的示例，这个示例把连线定制用于路径特定的运行时行为，而不是固定的端点连线方式，或全图范围内的连线几何控制。最突出的稀有组合，正如准备好的对比数据所指出的那样，是挂载时自动播放、基于实时图状态推导最短路径、弱化非路径上下文、自定义箭头 marker、深色遥测风格、速度控制，以及在布局或速度变化后可重新启动的方向感知分段回放。\n\n## 这种模式还适用于哪里\n\n这种模式可以自然迁移到依赖追踪、数据血缘讲解和服务跳转分析中。如果团队需要解释某个事件如何跨系统传播，或者为什么某个目标会经由一串特定的中间对象被到达，那么最短路径查找、路径弱化以及定时回放的组合，会比单纯的静态高亮路径更容易理解。\n\n它也适合面向操作人员的工具和教学型视图。比如回放工作流审批、展示事故在基础设施中的影响传播路径、解释队列到消费者的投递链路，或者演示流程图中的推荐路径。在这些场景里，可复用的核心思路并不是占位用的 `H-*` 数据集，而是 `relation-graph` 如何被用来从实时图状态重建一条路径，再把这条路径转化为有节奏的可视化追踪。\n",false,500,1782615379706]