JavaScript is required

画布框选多个节点

这个示例构建了一个全高度图查看器,用于通过拖拽矩形框选择多个现有节点。用户会看到一个居中的图标式网络、一个悬浮辅助窗口、一个可在框选与移动之间切换画布拖拽行为的模式开关,以及一个用于实时显示当前已选节点数量的计数器。这个示例的重点不是图编辑,而是把 relation-graph 的框选能力转化为一个明确的多节点选择工作流,并为其配套提供周边 UI 反馈。

带实时状态反馈的画布多节点选择

这个示例构建了什么

这个示例构建了一个全高度图查看器,用于通过拖拽矩形框选择多个现有节点。用户会看到一个居中的图标式网络、一个悬浮辅助窗口、一个可在框选与移动之间切换画布拖拽行为的模式开关,以及一个用于实时显示当前已选节点数量的计数器。这个示例的重点不是图编辑,而是把 relation-graph 的框选能力转化为一个明确的多节点选择工作流,并为其配套提供周边 UI 反馈。

数据是如何组织的

图数据被定义为一个内联的 RGJsonData 对象,其中包含固定的 rootId、扁平的 nodes 数组,以及一个通过父子 id 建立连接关系的 lines 数组。加载前没有进行任何特定领域的数据转换;数据会直接传给 setJsonData,然后将图居中并适配到视口中。在真实产品中,同样的结构可以表示服务依赖、资源清单、拓扑图,或者任何需要让操作人员批量选择现有项目的预制图数据。

relation-graph 是如何使用的

入口文件通过 RGProvider 包裹整个示例,然后 MyGraph.tsx 使用 RGHooks.useGraphInstance()RGHooks.useSelection() 将 React 状态连接到 relation-graph 的运行时状态。图配置被刻意控制得比较收敛:layout.layoutName = 'center'defaultJunctionPoint = RGJunctionPoint.border,并且 dragEventAction 由 React 状态驱动,这样画布就可以在不重建图实例的前提下,在框选与平移之间切换。

示例特有的行为通过 onCanvasSelectionEndonCanvasClick 挂接。当框选结束时,代码会通过 getNodesInSelectionView(...) 解析出选择矩形内的节点,然后用 updateNode(...) 回写每个节点的 selected 标记,并在 React 侧生成一个已选节点列表。当用户点击空白画布区域时,它会清除所有节点的选中状态,并重置计数。

图主体通过 RGSlotOnNode 做了简化,用一个居中的云图标替换了默认节点内容。这样视觉重点就落在选择工作流上,而不是标签或节点模板上。悬浮窗口来自共享的 DraggableWindow 辅助组件。在这个示例中,它承载了说明文本、选择模式按钮、已选节点计数以及实时框选几何信息。同一个共享辅助组件还提供了一个设置面板,可用于在运行时调整滚轮或拖拽行为,以及导出图片。本地 SCSS 主要用于设置辅助文本和切换按钮的样式;其中虽然包含与选中节点有关的选择器,但它们是空的,因此本次审查的本地文件并没有定义自定义的选中节点外观。

关键交互

  • 在选择模式下于画布上拖拽会创建一个框选区域,完成该手势后,只会选中 getNodesInSelectionView(...) 返回的节点。
  • 点击空白画布区域会清除所有节点的选中状态,并重置可见的已选节点计数。
  • 悬浮窗口中的按钮会在 selectionmove 两种画布拖拽行为之间切换。
  • 当框选区域处于激活状态时,辅助窗口会显示当前是否正在进行选择,并通过 RGHooks.useSelection() 展示该矩形的起始位置与尺寸。
  • 共享设置浮层可以修改滚轮和拖拽行为,并下载当前图视图的图片,但这些控件来自复用的辅助代码,而不是核心的选择逻辑。

关键代码片段

这段配置代码表明,该示例将 relation-graph 的拖拽行为直接绑定到了 React 状态上。

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.border,
    dragEventAction: enableSelectionMode ? 'selection' : 'move',
    layout: {
        layoutName: 'center'
    }
};

这段初始化代码证明,图数据是以内联方式定义的,并且在加载后只做了视口适配处理。

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '1', text: 'Node-1' },
        { id: '2', text: 'Node-2' },
        // ... more prepared nodes
    ],
    lines: [
        { id: 'l1', from: '7', to: '71' },
        // ... more prepared lines
    ]
};
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

这个处理函数就是选择工作流的核心:解析框选区域内的节点,将 selected 回写到每个节点,然后重新计算已选列表。

const onCanvasSelectionEnd = (selectionView: RGSelectionView, $event: RGUserEvent) => {
    const nodesInSelection = graphInstance.getNodesInSelectionView(selectionView);

    graphInstance.getNodes().forEach((node: RGNode) => {
        const isSelected = nodesInSelection.some(n => n.id === node.id);
        graphInstance.updateNode(node.id, { selected: isSelected });
    });

    updateSelectedNodes();
};

这段 JSX 将模式切换按钮和实时框选状态信息绑定到了悬浮辅助窗口中。

<button
    className={`c-my-button ${enableSelectionMode ? 'c-my-button-checked' : ''}`}
    style={{ width: '200px' }}
    onClick={() => { setEnableSelectionMode(!enableSelectionMode); }}
>
    {enableSelectionMode ? 'Disable Selection Mode' : 'Enable Selection Mode'}
</button>
<div className="c-option-name">Selected Nodes Count: {selectedNodes.length}</div>
{selectionView.show && <div className="rounded p-3 bg-green-100">
    Start: {selectionView.x}, {selectionView.y}
    Size: {selectionView.width}, {selectionView.height}
</div>}

这段共享辅助代码片段展示了悬浮窗口如何打开运行时画布设置和图片导出功能。

<SettingRow
    label="Canvas Drag Event:"
    options={[
        { label: 'Selection', value: 'selection' },
        { label: 'Move', value: 'move' },
        { label: 'none', value: 'none' },
    ]}
    value={dragMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ dragEventAction: newValue }); }}
/>
<SimpleUIButton onClick={downloadImage}>
    Download Image
</SimpleUIButton>

这个示例的独特之处

与附近的其他选择类示例相比,这个示例让拖拽框聚焦于检查而不是创建。对比数据特别将它与 canvas-selection 区分开来,因为后者中的拖拽区域可以进一步用于创建节点。而这里,手势的结果是针对现有节点的选择状态维护:解析被包围的节点、设置每个节点的 selected 标记,并显示已选数量。

它也比通用的画布交互演示更具体。与 drag-and-wheel-event 相比,运行时拖拽模式切换本身并不是最终目标;它的存在是为了支撑一个明确的工作流,而这个工作流又通过 RGHooks.useSelection()onCanvasSelectionEnd,以及实时计数与几何信息读数得到强化。与 canvas-event 相比,它对拖拽生命周期反馈的关注更少,而更关注手势完成后的结果:哪些节点最终落在了框内。

对比数据和稀有度数据共同支持一个较窄的核心特征组合作为它的主要区别点:纯拖拽框多节点选择、点击清空重置行为、运行时选择/移动切换、仅图标节点,以及一个同时展示实时框选几何信息和最终选择数量的悬浮辅助窗口,这些共同构成了同一个查看器风格的示例。

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

这种模式很适合迁移到以检查为导向的工具中,在这些工具里,用户需要在不编辑结构的前提下收集现有图项目。例如网络故障分诊、依赖分析、批量操作前的资产或设备选择,以及知识图谱审查工具中操作人员标记临时工作集的场景。

它也适用于图只是更大工作流中一个面板的混合式界面。实时选择矩形和已选节点计数可以驱动侧边面板、对比抽屉、导出操作或批量标记流程,同时保持底层图本身为只读状态。