JavaScript is required

世界地图关系图叠加与快照

这是一个固定布局 relation-graph 示例,在内置世界 SVG 上叠加手工节点坐标和动画连线。它还加入可拖拽工具窗,用于快照保存与回放,并提供共享画布设置与图片导出。

世界地图图谱叠加与快照回放

这个示例构建了什么

这个示例在内置的世界 SVG 之上构建了一个固定位置的 relation-graph。画面组合了一个浅色的 2000 x 857 世界地图、5 个小型橙色圆形节点、4 条橙色连线、内置图谱工具栏,以及一个浮动的白色工具窗口。用户可以平移或缩放画布、将当前图谱保存为带时间戳的快照、点击某条已保存记录重新打开快照、将浮动窗口切换到画布设置模式,并导出图片。这个示例的重点并不是地理分析,而是一种组合模式:把 relation-graph 内容放置到地图形背景上的指定坐标位置。

数据是如何组织的

主数据集以内联 RGJsonData 的形式定义在 MyGraph.tsx 中。它包含一个 rootId、5 个带显式 xy 坐标的节点,以及 4 条带有 id、动画值和可选连线形状覆写的连线。在调用 setJsonData() 之前没有任何布局预处理。世界地图也不是由图谱数据生成的,它来自一个单独的 MapSvg4World 组件;该组件渲染一个大型静态 SVG,并被插入到图谱内容的后方。

在运行时,这个示例还会执行一次额外的数据转换:Save 操作会通过读取 getNodes()getLinks()getOptions(),把当前图谱重新序列化为一个快照对象。这种快照格式可以复用于物流枢纽、区域办公室、海缆登陆点、线路地图,或任何其他适合使用静态地理背景、但节点位置由作者直接指定的数据集。

relation-graph 是如何使用的

RGProvider 包裹了整个 demo,以便 hooks 可以解析到当前激活的图谱实例。图谱本身使用 layoutName: 'fixed',这意味着 relation-graph 会尊重数据中预先写入的坐标,而不是重新计算一个布局。图谱选项启用了内置工具栏,使用边框连接点,并定义了橙色节点与连线的默认样式,使叠加层在视觉上保持一致。

RGHooks.useGraphInstance() 驱动了几乎所有图谱操作:初始 setJsonData()moveToCenter()zoomToFit()、快照序列化、快照回放,以及浮动窗口中的共享设置与导出流程。该 demo 没有定义自定义节点或连线插槽,而是把 MapSvg4World 作为 RelationGraph 的子内容传入,因此这个 SVG 会成为图谱场景的一部分。样式保持得较轻,SCSS 基本不干预图谱内部,主要关注快照列表的外观。

关键交互

核心交互是快照捕获与回放。点击 Save 按钮会读取当前图谱状态,存储一个带时间戳的快照;如果最新快照已经与当前图谱一致,则阻止重复保存。点击某一条已保存记录会清空图谱,并重新绘制已保存的节点与连线。

浮动辅助窗口也是交互模型的一部分。用户可以拖动它、最小化它,并把它切换成设置叠层。这个叠层可以修改滚轮行为、修改画布拖拽行为,并通过在图谱实例上执行图片生成前后的准备与恢复生命周期来下载图片。节点和连线点击处理器也存在,但它们只记录检查数据,并不驱动主要功能流程。

关键代码片段

下面这段选项配置说明该图谱被配置为一个固定位置的橙色叠加层,并启用了内置工具栏。

const graphOptions: RGOptions = {
  debug: false,
  showToolBar: true,
  defaultJunctionPoint: RGJunctionPoint.border,
  defaultNodeColor: '#f39930',
  defaultNodeBorderWidth: 1,
  defaultNodeBorderColor: '#f39930',
  defaultNodeWidth: 20,
  defaultNodeHeight: 20,
  defaultLineColor: '#f39930',
  defaultLineWidth: 2,
  layout: {
    layoutName: 'fixed'
  }
};

下面这段初始化代码表明,节点位置是直接写在数据里的,而不是通过外部布局引擎计算出来的。

const myJsonData: RGJsonData = {
  rootId: 'R',
  nodes: [
    { id: "R", text: "R", opacity: 1, x: 1070, y: 250, nodeShape: RGNodeShape.circle },
    { id: "A", text: "A", opacity: 1, x: 460, y: 160, nodeShape: RGNodeShape.circle },
    // ...
    { id: "C", text: "C", opacity: 1, x: 1680, y: 550, nodeShape: RGNodeShape.circle },
    { id: "D", text: "D", opacity: 1, x: 1230, y: 450, nodeShape: RGNodeShape.circle }
  ],
  lines: [
    { id: 'l1', from: 'R', to: 'A', animation: 1, lineShape: RGLineShape.Curve3 },
    // ...
    { id: 'l4', from: 'R', to: 'D', animation: 4, lineShape: RGLineShape.StandardStraight },
  ]
};

下面这段保存逻辑展示了该 demo 如何把当前图谱实例序列化为可回放的快照。

const allNodesWithPosition = graphInstance.getNodes().map(node => ({
    id: node.id,
    text: node.text,
    x: node.x,
    y: node.y,
    opacity: node.opacity,
    nodeShape: node.nodeShape
}));

const allLines = graphInstance.getLinks().map(link => ({
    id: link.line.id,
    from: link.fromNode.id,
    to: link.toNode.id,
    lineShape: link.line.lineShape,
    animation: link.line.animation
}));

下面这段重复保存保护与回放路径说明,快照是轻量级的图谱状态检查点,而不是完整的视口恢复。

if (graphSnapshots.length > 0 && JSON.stringify(graphSnapshotData) === JSON.stringify(graphSnapshots[0].data)) {
  alert('The data has not changed');
  return;
}

const redrawSnapshot = (theSnapshot: {data: RGJsonData}) => {
  graphInstance.clearGraph();
  graphInstance.addNodes(theSnapshot.data.nodes);
  graphInstance.addLines(theSnapshot.data.lines);
};

下面这段设置代码证明,浮动窗口可以从快照历史切换到运行时画布控制和图片导出。

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();

这个示例的独特之处

对比数据表明,map-world 并不是唯一一个带地图背景的示例,map-us 也使用了类似的快照加背景图方案。这个示例的独特之处在于,它把完整的世界 SVG、显式的 xy 定位、始终可见的内置工具栏、带动画的橙色连线,以及一个可拖动窗口中的快照保存与回放控制组合在了一起。

它与 map-us 的差异主要体现在覆盖范围和动态效果上。map-world 把这一模式扩展到更大的全球背景上,并且让每一条预设连线都带有动画值,同时混合使用曲线和直线形状。相比 use-sigma-layoutuse-dagre-layout,这个示例的教学价值不同:这里的坐标是为了背景叠加而预先写定的,而不是通过外部布局引擎计算并在测量后回写。因此,与自动图谱布局工作流相比,这个示例更适合作为固定坐标叠加方案的起点。

这种模式还适用于哪里

这种模式非常适合用于全球供应链地图、路线看板、国际基础设施视图、办公区或园区叠加图,以及楼层平面图或设备地图等静态示意背景。当产品需要轻量级的保存与恢复行为、图片导出或辅助窗口,而又不想演变成完整的图谱编辑器时,它也同样适用。在这些场景下,世界 SVG 可以替换为任何手工绘制的背景,而 relation-graph 继续负责图谱层、视口以及序列化 hooks。