画布框选与节点创建
这个示例构建了一个全屏的 relation-graph 工作区,并预置了 14 个彼此断开的字母节点。一个可拖拽的辅助窗口始终位于画布上方,用来说明如何开始框选、显示实时的选择框尺寸信息,并提供可切换项来改变拖拽手势的行为。
用于拾取与图形创建的画布框选
这个示例构建了什么
这个示例构建了一个全屏的 relation-graph 工作区,并预置了 14 个彼此断开的字母节点。一个可拖拽的辅助窗口始终位于画布上方,用来说明如何开始框选、显示实时的选择框尺寸信息,并提供可切换项来改变拖拽手势的行为。
其核心行为是,同一次框选手势会产生两种可能结果。在普通模式下,它会选中拖拽区域内的所有节点。在创建模式下,它会把同一区域转换成一个新的圆形或矩形节点,节点尺寸来自拖出的框选区域。因此,这个示例更像是一个围绕选择驱动工具的紧凑参考,而不是某个特定领域的图表。
数据是如何组织的
初始数据集是在 initializeGraph 内部创建的一个内联 RGJsonData 对象。它包含一个扁平的 nodes 数组,节点 id 从 a 到 n,以及一个空的 lines 数组,因此起点被刻意设计得简单且没有连线关系。
这里没有外部获取数据,也没有在调用 setJsonData(...) 之前做任何预处理,除了构造这个本地对象。数据加载完成后,示例会将图谱居中并缩放到适合当前视口。在交互过程中,选择框会成为临时的运行时输入:它要么被传给 getNodesInSelectionView(...) 用于多选,要么会被转换为画布坐标,并通过 addNode(...) 用来确定新节点的尺寸。
在生产应用中,同样的数据结构可以表示规划看板上的卡片、拓扑画布上的设备、平面图中的座位,或者标注工作区中的资源,此时区域选择与基于区域的创建比线关系本身更重要。
relation-graph 是如何使用的
这个示例外层使用 RGProvider 包裹,然后 MyGraph 通过 RGHooks.useGraphInstance() 来加载数据、居中视图、清理状态、解析选择区域内的节点,以及插入新节点。它还使用 RGHooks.useSelection(),让当前选择框既能驱动浮动状态卡片,也能驱动临时图形预览。
图谱选项不多,但都经过了明确设计:showToolBar 用于启用内置工具栏,checkedItemBackgroundColor 为选中项提供半透明绿色背景,defaultLineWidth 保持最小值,而 dragEventAction 绑定到 React 状态,因此画布可以在运行时于 selection 和 move 之间切换。本地代码没有声明自定义布局;它只是加载示例数据,然后通过 moveToCenter() 和 zoomToFit() 来规范化视口。
RelationGraph 接收关键事件处理器,尤其是 onCanvasClick 和 onCanvasSelectionEnd。RGSlotOnNode 用一个带内边距的文本渲染器覆盖了节点内容。浮动的 DraggableWindow 组件提供说明面板,并打开一个共享设置面板;这个面板通过 RGHooks.useGraphStore() 读取当前选项,使用 setOptions(...) 更新滚轮和拖拽行为,并通过 prepareForImageGeneration() 配合一个 DOM-to-image 辅助方法导出图谱图片。SCSS 文件只定义了占位选择器,因此大部分可见定制都来自 JSX、类似 Tailwind 的工具类,以及 MyCreatingShapeLayer 中的 SVG 覆盖层。
关键交互
- 按住
Shift开始画布框选,或者在辅助面板中显式将画布拖拽行为切换为selection模式。 - 在普通模式下拖出选择框,查找区域内的节点并将它们标记为选中。
- 启用圆形或矩形创建后,拖出同样的选择框,即可插入一个新节点,其尺寸来自拖拽边界。
- 点击画布空白处以清除图谱中的 checked 和 selected 状态。
- 打开浮动设置面板以修改滚轮行为、修改拖拽行为,或将当前图谱下载为图片。
节点点击和连线点击处理器也存在,但在这个示例中它们只会把对象打印到控制台,不会改变图谱状态。
关键代码片段
图谱实例、实时选择状态和运行时拖拽模式在 MyGraph 顶部被组织在一起。
const graphInstance = RGHooks.useGraphInstance();
const selectionView = RGHooks.useSelection();
const [dragMode, setDragMode] = React.useState<'move'|'selection'>('selection');
const [selectionForCreateNode, setSelectionForCreateNode] = React.useState<''|'circle'|'rect'>('');
const graphOptions: RGOptions = {
debug: false,
showToolBar: true,
checkedItemBackgroundColor: 'rgba(0, 128, 0, 0.2)',
defaultLineWidth: 1,
dragEventAction: dragMode,
defaultJunctionPoint: RGJunctionPoint.border
};
初始图谱数据是本地的、轻量的,并在视图居中和适配之前直接加载到 relation-graph 实例中。
const myJsonData: RGJsonData = {
nodes: [
{ id: 'a', text: 'A' },
{ id: 'b', text: 'B' },
// ...
],
lines: []
};
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
当启用了某种创建模式时,onCanvasSelectionEnd 会把选择框转换为一个新节点。
if (selectionForCreateNode === 'circle') {
const xyOnCanvas = graphInstance.getCanvasXyByViewXy(userSelectionView);
graphInstance.addNode({
id: graphInstance.generateNewNodeId(),
nodeShape: RGNodeShape.circle,
width: userSelectionView.width,
height: userSelectionView.height,
x: xyOnCanvas.x,
y: xyOnCanvas.y
});
}
非创建分支会使用 relation-graph 内置的选择查询来解析已有节点,并将它们标记为选中。
graphInstance.clearChecked();
graphInstance.clearSelected();
const nodesInSelection = graphInstance.getNodesInSelectionView(userSelectionView);
SimpleGlobalMessage.success(`Select [${nodesInSelection.length}] Nodes`);
nodesInSelection.forEach(node => {
graphInstance.updateNode(node, { selected: true })
});
临时覆盖层会对负方向拖拽进行归一化,因此即使用户向左或向上拖动,预览仍能正确渲染。
const rectX = width > 0 ? x : x + width;
const rectY = height > 0 ? y : y + height;
const absWidth = Math.abs(width);
const absHeight = Math.abs(height);
{shape === 'rect' ? (
<rect
x={rectX}
y={rectY}
width={absWidth}
height={absHeight}
strokeDasharray="4 2"
/>
) : (
<circle
cx={rectX + absWidth / 2}
cy={rectY + absHeight / 2}
r={Math.min(absWidth, absHeight) / 2}
/>
)}
这个示例的独特之处
与 selections 相比,这个示例并不把框选本身视为最终结果。拖拽出的区域仍然可以选中已有节点,但它也可以成为新的图谱内容。这使得该示例从纯粹的拾取转向了混合式的选择与创作。
与 canvas-selection-pro 相比,这里的重点是显式教学,而不是优先追求快捷操作。辅助窗口始终展示移动或选择模式,以及圆形或矩形创建选项,因此整个工作流更容易被学习和改造。
对比数据也体现出一种不同的特性组合:手动画布模式切换、点击清空工作区、实时选择信息展示、虚线创建预览,以及由选择驱动的节点插入,全都集中在一个紧凑的界面中。其他相邻示例也覆盖了这一模式的部分内容,但如果你希望 relation-graph 原生的选择几何成为核心交互原语,这个示例尤其值得参考。
这一模式还适用于哪里
这种模式很适合轻量级白板编辑器,在这里,拖拽出的区域既可以选择现有对象,也可以创建一个新的有边界图形。它也适用于规划看板、平面图工具、拓扑草图工具和标注画布,在这些场景里,同一个手势既要支持基于区域的拾取,也要支持基于区域的插入。
另一条扩展路径是把选择框视为一种通用输入原语。它不一定只用于创建圆形和矩形节点;一旦用户选择了某种创建模式,同样的交接过程也可以用来创建分组卡片、有边界的容器、占位设备,或者特定领域的区域。