按节点ID聚焦视图
这个示例构建大型“仅节点”relation-graph 画布,并通过悬浮面板中的节点 ID 列表驱动视口导航。它在内存中生成 160 个节点,使用实例 API 初始化图谱,并在 `focusNodeById(...)` 外包裹临时画布动画,同时复用共享画布设置与图片导出控制。
在大型纯节点画布上按 Node ID 聚焦视口
这个示例构建了什么
这个示例构建了一个全高的 relation-graph 查看器,在灰色画布上展示 160 个彼此断开的矩形节点,并允许用户通过悬浮控制窗口选择目标节点的 node ID,将视口跳转到已知目标位置。该图并不表达业务关系,也不展示边样式。它更像是一个定位器风格的沙盒,用于在大范围节点区域中移动。
用户可以从十个预设 node ID 中选择一个,观察画布以动画方式移动到目标位置,拖拽或最小化工具窗口,打开设置浮层,修改滚轮和拖拽行为,并将图导出为图片。这里最关键的一点是极简的外部控制模式:外部 UI 通过 focusNodeById(...) 驱动定位,而不需要重建图。
数据是如何组织的
图数据在 MyGraph.tsx 中本地生成。initializeGraph() 创建了一个 JsonNode[],其中包含 160 个名称从 N0 到 N159 的节点,为每个节点赋予 id、text、x 和 y,并将这些坐标排布在一个从 (-500, -500) 开始、拥有 22 列的网格上。随后这些节点被包装进一个带有 lines: [] 的 RGJsonData 对象,因此渲染出来的图刻意不包含任何边。
在图加载前还有一个很小的预处理步骤:这个示例直接在内存中计算坐标,而不是读取预先准备好的 JSON 文件,也不会调用后端。之后,它通过 clearGraph() 和 addNodes() 只把节点数组加载到图实例中。在真实应用里,这种结构可以表示可搜索的地图标记、大画布资源、座位或货架位置、平面图对象,或任何其他需要用户通过已知标识符快速跳转的项目。
relation-graph 是如何使用的
index.tsx 用 RGProvider 包裹整个页面,MyGraph.tsx 则使用 RGHooks.useGraphInstance() 作为主要集成入口。图配置非常收敛且明确:debug: false、defaultNodeShape: 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() 读取实时的 wheelEventAction 和 dragEventAction,然后通过 graphInstance.setOptions(...) 更新它们。同一个面板还通过 prepareForImageGeneration()、getOptions()、domToImageByModernScreenshot(...) 和 restoreAfterImageGeneration() 支持导出功能。
关键交互
- 点击某个预设胶囊按钮会更新
nodeId,并触发视口以动画方式跳转到该节点。 - 聚焦流程来自图外部的 UI,而不是通过点击图中的节点触发。
- 在已审阅的源码中,
Random Position按钮只调用graphInstance.refresh(),因此这里不应把它描述为重新生成坐标。 - 悬浮窗口可以通过标题栏拖拽,并可通过最小化控件折叠。
- 设置按钮会在同一个窗口内打开一个浮层,点击半透明背景即可关闭。
- 设置浮层可在
scroll、zoom和none之间切换滚轮行为,在selection、move和none之间切换拖拽行为,并提供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-layout、center-layout-options 和 tree-distance 相比,这个示例没有把悬浮窗口用作布局调参面板。它保持单一的 force-layout 画布,并把控制面板变成一个定位器。与 line-shape-and-label 相比,它完全移除了边,因此开发者可以在没有线条几何、标签布局或关系语义干扰的情况下,单独观察基于 node ID 的视口聚焦行为。
最强的辨识组合正如对比产物中总结的那样:灰色、无边的矩形节点区域,胶囊式预设选择器,白色悬浮工具窗口,挂载期的实例 API 启动流程,以及一次短促的动画视口跳转。这种组合让整个页面更像一个导航沙盒,而不是关系查看器或布局演示页面。
这种模式还适用于哪些场景
这种模式很适合那些用户已经知道自己要查看对象的标识符,但仍需要帮助在大画布上快速抵达目标位置的图类体验。典型的迁移目标包括拓扑定位工具、仓库或座位地图、平面图巡检视图、搜索结果跳转面板,以及需要从总览跳到细节的运维仪表盘。
当选择器应该位于图外部,而不是放在节点交互内部时,这种模式同样适用。这个示例中写死的胶囊列表,可以替换为搜索框、结果表格、命令面板,或后端驱动的 ID 列表,同时保持相同的聚焦顺序和视口控制 API。