JavaScript is required

按节点ID聚焦视图

这个示例构建大型“仅节点”relation-graph 画布,并通过悬浮面板中的节点 ID 列表驱动视口导航。它在内存中生成 160 个节点,使用实例 API 初始化图谱,并在 `focusNodeById(...)` 外包裹临时画布动画,同时复用共享画布设置与图片导出控制。

在大型纯节点画布上按 Node ID 聚焦视口

这个示例构建了什么

这个示例构建了一个全高的 relation-graph 查看器,在灰色画布上展示 160 个彼此断开的矩形节点,并允许用户通过悬浮控制窗口选择目标节点的 node ID,将视口跳转到已知目标位置。该图并不表达业务关系,也不展示边样式。它更像是一个定位器风格的沙盒,用于在大范围节点区域中移动。

用户可以从十个预设 node ID 中选择一个,观察画布以动画方式移动到目标位置,拖拽或最小化工具窗口,打开设置浮层,修改滚轮和拖拽行为,并将图导出为图片。这里最关键的一点是极简的外部控制模式:外部 UI 通过 focusNodeById(...) 驱动定位,而不需要重建图。

数据是如何组织的

图数据在 MyGraph.tsx 中本地生成。initializeGraph() 创建了一个 JsonNode[],其中包含 160 个名称从 N0N159 的节点,为每个节点赋予 idtextxy,并将这些坐标排布在一个从 (-500, -500) 开始、拥有 22 列的网格上。随后这些节点被包装进一个带有 lines: []RGJsonData 对象,因此渲染出来的图刻意不包含任何边。

在图加载前还有一个很小的预处理步骤:这个示例直接在内存中计算坐标,而不是读取预先准备好的 JSON 文件,也不会调用后端。之后,它通过 clearGraph()addNodes() 只把节点数组加载到图实例中。在真实应用里,这种结构可以表示可搜索的地图标记、大画布资源、座位或货架位置、平面图对象,或任何其他需要用户通过已知标识符快速跳转的项目。

relation-graph 是如何使用的

index.tsxRGProvider 包裹整个页面,MyGraph.tsx 则使用 RGHooks.useGraphInstance() 作为主要集成入口。图配置非常收敛且明确:debug: falsedefaultNodeShape: RGNodeShape.rect,以及 layout.layoutName = 'force'。该示例没有使用 setJsonData(...);相反,它在挂载后以命令式方式清空图、添加生成出的节点、将画布居中,并让视口适配内容。

relation-graph 的核心行为是视口导航。一个 useEffect 监听 nodeId,临时启用画布动画,调用 focusNodeById(nodeId),并在 350 毫秒后再次关闭动画。这个示例中没有 node、line、canvas 或 viewport 插槽,也没有编辑工作流。本地 SCSS 仅覆盖了 relation-graph 的背景色为 #aaa,因此大多数可见行为都来自图实例 API 和共享控制窗口。

悬浮工具外壳由共享组件 DraggableWindow 实现。在这个共享组件内部,CanvasSettingsPanel 使用 RGHooks.useGraphStore() 读取实时的 wheelEventActiondragEventAction,然后通过 graphInstance.setOptions(...) 更新它们。同一个面板还通过 prepareForImageGeneration()getOptions()domToImageByModernScreenshot(...)restoreAfterImageGeneration() 支持导出功能。

关键交互

  • 点击某个预设胶囊按钮会更新 nodeId,并触发视口以动画方式跳转到该节点。
  • 聚焦流程来自图外部的 UI,而不是通过点击图中的节点触发。
  • 在已审阅的源码中,Random Position 按钮只调用 graphInstance.refresh(),因此这里不应把它描述为重新生成坐标。
  • 悬浮窗口可以通过标题栏拖拽,并可通过最小化控件折叠。
  • 设置按钮会在同一个窗口内打开一个浮层,点击半透明背景即可关闭。
  • 设置浮层可在 scrollzoomnone 之间切换滚轮行为,在 selectionmovenone 之间切换拖拽行为,并提供 Download Image 操作。

关键代码片段

下面这段代码表明,该图被配置为一个简单的力导向查看器,使用矩形节点且没有额外的展示选项:

const graphOptions: RGOptions = {
    debug: false,
    defaultNodeShape: RGNodeShape.rect,
    layout: {
        layoutName: 'force'
    }
};

下面这段代码证明,该示例的数据集是在本地生成的,而不是从外部 JSON 文件读取:

const newNodes: JsonNode[] = [];
const startPoint = { x: -500, y: -500 };
for (let i = 0; i < 160; i++) {
    newNodes.push({
        id: 'N' + i,
        text: 'N' + i,
        x: startPoint.x + (i % 22) * 200,
        y: startPoint.y + Math.floor(i / 22) * 200
    });
}

下面这段代码展示了组件挂载时的实例 API 调用顺序:加载一个仅包含节点的图,并让它适配到当前视图中:

const myJsonData: RGJsonData = {
    nodes: newNodes,
    lines: [] // no edges in this example
};
graphInstance.clearGraph();
graphInstance.addNodes(myJsonData.nodes);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

下面这段代码展示了核心导航模式:开启动画,聚焦选中的 node ID,然后再关闭动画:

const tabChange = async () => {
    graphInstance.enableCanvasAnimation();
    graphInstance.focusNodeById(nodeId);
    setTimeout(() => {
        graphInstance.disableCanvasAnimation();
    }, 350);
};

下面这段代码表明,聚焦目标来自固定的预设列表,而不是根据图内容实时生成的搜索结果:

<SimpleUISelect
    data={[
        { value: 'N1', text: 'N1' },
        { value: 'N20', text: 'N20' },
        // ... other preset ids ...
        { value: 'N159', text: 'N159' }
    ]}
    currentValue={nodeId}
    onChange={(newValue: string) => {
        setNodeId(newValue);
    }}
/>

下面这段代码展示了共享设置面板如何在运行时更新 relation-graph 的交互模式:

<SettingRow
    label="Wheel Event:"
    options={[
        { label: 'Scroll', value: 'scroll' },
        { label: 'Zoom', value: 'zoom' },
        { label: 'None', value: 'none' },
    ]}
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

下面这段代码展示了导出流程:先让图画布进入可截图状态,将其渲染为图片 blob,再在之后恢复图状态:

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

这个示例的独特之处

对比数据表明,这个示例与 search-and-focus 最接近,但强调点不同。search-and-focus 处理的是带标签的人物关系图,并从实时图数据中得出聚焦选项;而这个示例生成了 160 个彼此断开的节点,并提供一个写死的十项 ID 列表。因此,当需求只是“通过外部 UI 跳转到某个已知 node ID 对应的位置”时,focus-node-by-id 是一个更精简的参考。

switch-layoutcenter-layout-optionstree-distance 相比,这个示例没有把悬浮窗口用作布局调参面板。它保持单一的 force-layout 画布,并把控制面板变成一个定位器。与 line-shape-and-label 相比,它完全移除了边,因此开发者可以在没有线条几何、标签布局或关系语义干扰的情况下,单独观察基于 node ID 的视口聚焦行为。

最强的辨识组合正如对比产物中总结的那样:灰色、无边的矩形节点区域,胶囊式预设选择器,白色悬浮工具窗口,挂载期的实例 API 启动流程,以及一次短促的动画视口跳转。这种组合让整个页面更像一个导航沙盒,而不是关系查看器或布局演示页面。

这种模式还适用于哪些场景

这种模式很适合那些用户已经知道自己要查看对象的标识符,但仍需要帮助在大画布上快速抵达目标位置的图类体验。典型的迁移目标包括拓扑定位工具、仓库或座位地图、平面图巡检视图、搜索结果跳转面板,以及需要从总览跳到细节的运维仪表盘。

当选择器应该位于图外部,而不是放在节点交互内部时,这种模式同样适用。这个示例中写死的胶囊列表,可以替换为搜索框、结果表格、命令面板,或后端驱动的 ID 列表,同时保持相同的聚焦顺序和视口控制 API。