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