JavaScript is required

可调图谱缩略图(鹰眼)

这个示例构建了一个全屏力导布局图查看器,并始终显示一个缩略图覆盖层。画布被有意设计得相当繁忙:一百多个节点散布在深色的点状背景上,节点的大小和形状各不相同,而缩略图始终覆盖在图之上,因此用户在导航时也能持续掌握整体结构。

大型力导布局图的可调缩略图覆盖层

这个示例构建了什么

这个示例构建了一个全屏力导布局图查看器,并始终显示一个缩略图覆盖层。画布被有意设计得相当繁忙:一百多个节点散布在深色的点状背景上,节点的大小和形状各不相同,而缩略图始终覆盖在图之上,因此用户在导航时也能持续掌握整体结构。

用户可以把缩略图移动到任意角落,使用独立的宽度和高度滑块调整它的尺寸,拖动或最小化这个浮动辅助窗口,打开一个共享设置面板来配置画布滚轮和拖拽行为,并将当前图下载为图片。这个示例最核心的教学点在于,RGMiniView 并没有被当作被动的脚手架。它是作为一个覆盖层组件挂载的,并直接由 React state 控制。

数据是如何组织的

数据在 initializeGraph() 内联声明为一个 RGJsonData 对象,包含 rootId: 'a'、103 个节点和 102 条连线。从结构上看,它是一个大型分支层级,但节点标签只是像 b4-7e2-9 这样的通用标识符,因此这份数据集承担的是导航载体的作用,而不是领域内容本身。

在调用 setJsonData(...) 之前还有一段重要的预处理。每个节点都会被修改,赋予一个随机颜色、随机宽度、随机高度,以及 RGNodeShape.circleRGNodeShape.rect 之一。这个预处理让缩略图更有信息量,因为总览中呈现的是不同的轮廓,而不是完全一致的方框。完成初始加载后,这个示例不会重新构建数据集。后续的 UI 控件只会改变组件状态和画布行为。在真实场景里,同样的结构可以替代拓扑图、组织树、依赖图或知识图谱,此时在大画布上保持方位感比编辑本身更重要。

relation-graph 是如何使用的

index.tsx 使用 RGProvider 包裹整个页面,而 MyGraph.tsx 则把 RGHooks.useGraphInstance() 作为主要控制入口。在挂载时,示例会调用一次 setJsonData(...)moveToCenter()zoomToFit()。图配置被刻意收窄:showToolBar: falsedefaultLineColor: '#666',以及一个设置了 maxLayoutTimes: Number.MAX_SAFE_INTEGER 的力导布局。

这个示例中 relation-graph 最关键的扩展点是 RGSlotOnView。它没有去自定义节点主体或连线渲染,而是把 RGMiniView 挂载到图的覆盖层中,并将它的 positionwidthheight 属性绑定到本地 React state。这样一来,缩略图就成为查看器外层界面的一部分,而不是节点内容的一部分。这个示例没有实现图编辑、节点创作或自定义布局器。它让图本身尽量接近默认渲染,把定制预算集中投入到导航工具上。

共享的 DraggableWindow 组件提供了浮动的说明区和控制界面。在它的设置覆盖层内部,CanvasSettingsPanel 使用 RGHooks.useGraphStore() 读取当前的 dragEventActionwheelEventAction,然后调用 graphInstance.setOptions(...) 在运行时更新这些值。同一个面板还通过 prepareForImageGeneration()getOptions()domToImageByModernScreenshot(...)restoreAfterImageGeneration() 支持导出功能。

样式表则补全了查看器的整体呈现。它使用 --rg-canvas-scale 以及画布偏移等 relation-graph 画布 CSS 变量,让点状背景始终跟随平移和缩放对齐;把节点标签放大为白色文字;并把被选中的连线标签改成实心色块。这些都只是局部样式覆盖,并不是通用指南层面的预设。

关键交互

  • 点击角落选择器,可以在不重新加载图数据的情况下,把缩略图移动到左上、右上、左下和右下。
  • 调整宽度滑块会立即改变缩略图宽度。
  • 调整高度滑块会立即改变缩略图高度。
  • 拖动浮动辅助窗口可以重新放置控制界面,这样它就不必始终遮住图中的固定区域。
  • 最小化辅助窗口会收起说明面板,同时仍然保持图可见。
  • 打开设置覆盖层后,会显示滚轮行为、画布拖拽行为和图片导出的共享运行时控制项。
  • 本地说明文字告诉用户,他们可以拖动缩略图中的可视区域指示框来移动视口;从已检查的源码来看,这个行为由 RGMiniView 自身提供,而不是通过自定义拖拽处理器实现。

关键代码片段

这个片段展示了,示例把缩略图状态与图实例一起,当作一等 React state 来处理:

const graphInstance = RGHooks.useGraphInstance();
const [miniViewPosition, setMiniViewPosition] = React.useState('br');
const [miniViewSize, setMiniViewSize] = React.useState<{width: number, height: number}>({width: 300, height: 150});
useEffect(() => {
    initializeGraph();
}, []);

这个片段证明了,示例数据在加载前会先做预处理,因此总览中会呈现不同的颜色、尺寸和形状:

const randomColorRange = ['#5b05f1', '#FD8B37', '#9b9903', '#247c02'];
myJsonData.nodes.forEach(node => {
    node.color = randomColorRange[Math.floor(Math.random() * randomColorRange.length)];
    node.width = 30 + Math.floor(Math.random() * 200);
    node.height = 30 + Math.floor(Math.random() * 200);
    node.nodeShape = Math.random() < 0.3 ? RGNodeShape.circle : RGNodeShape.rect;
});
await graphInstance.setJsonData(myJsonData);

这个片段展示了图配置如何隐藏内置工具栏,并让布局配置聚焦于一个大型的力驱动查看器:

const graphOptions: RGOptions = {
    showToolBar: false,
    defaultLineColor: '#666',
    layout: {
        layoutName: 'force',
        maxLayoutTimes: Number.MAX_SAFE_INTEGER
    }
};

这个片段展示了角落选择器如何把缩略图位置变成运行时控制项,而不是一个硬编码的选择:

<SimpleUISelect
    data={[
        { value: 'tl', text: 'Top-Left' },
        { value: 'tr', text: 'Top-Right' },
        { value: 'bl', text: 'Bottom-Left' },
        { value: 'br', text: 'Bottom-Right' }
    ]}
    currentValue={miniViewPosition}
    onChange={(newValue: string) => { setMiniViewPosition(newValue); }}
/>

这个片段展示了状态驱动的组件模式:滑块的值会直接传入覆盖层 RGMiniView 的属性中:

<SimpleUISlider currentValue={miniViewSize.width} min={50} max={500} step={10} onChange={(newValue) => {setMiniViewSize({...miniViewSize, width: newValue});}} />
<SimpleUISlider currentValue={miniViewSize.height} min={50} max={500} step={10} onChange={(newValue) => {setMiniViewSize({...miniViewSize, height: newValue});}} />
<RelationGraph options={graphOptions}>
    <RGSlotOnView>
        <RGMiniView width={`${miniViewSize.width}px`} height={`${miniViewSize.height}px`} position={miniViewPosition as RGWidgetPosition} />
    </RGSlotOnView>
</RelationGraph>

这个片段展示了,浮动设置面板如何与当前图配置保持同步,并通过 setOptions(...) 更新画布行为:

const { options } = RGHooks.useGraphStore();
const dragMode = options.dragEventAction;
const wheelMode = options.wheelEventAction;

<SettingRow
    label="Wheel Event:"
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

这个片段展示了导出流程:先为截图准备图画布,把画布 DOM 渲染成图片 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();

这个片段展示了样式表如何让背景跟随图视口移动,而不是表现为静态页面纹理:

.relation-graph {
    --rg-canvas-scale: 1;
    --rg-canvas-offset-x: 0px;
    --rg-canvas-offset-y: 0px;
    background-position: var(--rg-canvas-offset-x) var(--rg-canvas-offset-y);
    background-size: calc(var(--rg-canvas-scale) * 15px) calc(var(--rg-canvas-scale) * 15px);
    background-image: radial-gradient(circle, rgb(197, 197, 197) calc(var(--rg-canvas-scale) * 1px), transparent 0);
}

这个示例的独特之处

准备好的对比数据已经清楚说明了这种区别。最接近的、同样以缩略图为导向的相邻示例是 node,但在那里,mini view 只是更大范围节点样式展示中的次要部分。而在 gee-thumbnail-diagram 中,总览组件才是主要教学内容:角落位置、宽度和高度都被暴露为实时控制项,而图的其他部分则整体上更接近默认渲染。

对比文件也把这个示例与 drag-and-wheel-eventnode-style3 区分开来。这些示例共享了浮动辅助壳层,以及部分相同的运行时画布工具,但它们的主要教学点并不相同。在这里,重要的局部模式是在一个大型、视觉上信息密集的力导图之上,通过 RGSlotOnView 挂载 RGMiniView。隐藏工具栏、随机化节点轮廓的预处理、深色点状画布以及可移动的辅助面板,共同让它比起力导调优、节点皮肤定制或通用交互测试,更适合作为导航型图查看器的起点。

这种模式还适用于哪里

这种模式很适合那些用户需要在大画布上保持方位感、但又不进入完整编辑器工作流的图形界面。示例包括服务依赖图、基础设施拓扑视图、大型组织层级、知识地图,以及调查分析工作台等,在这些场景里,核心问题是在缩放查看细节时仍然知道自己处于整体的哪个位置。

它也适用于团队希望让辅助查看工具可以在运行时配置的场景。相同的结构可以支持面向不同操作人员的缩略图预设、适配不同屏幕尺寸的可调总览组件、可导出的审计视图,以及在不改变底层图数据模型的前提下暴露平移或缩放行为的浮动工具面板。