节点尺寸调整与控件样式
这个示例把小型关系图变成聚焦“选中节点缩放”的工作区。它演示 `setEditingNodes([node])`、`RGEditingNodeController` 与 `RGEditingResize` 的协同方式,并展示内置缩放覆盖层如何在默认外观与两套自定义 SCSS 皮肤之间切换。
点击节点调整大小,并可切换调整手柄样式
这个示例构建了什么
这个示例在一个 relation graph 之上构建了一个小型的节点尺寸调整工作区。界面上展示了一个全高的点状画布、一组包含不同节点形状和尺寸的混合示例图,以及一个浮动的辅助窗口,用来说明交互方式并提供样式切换器。
主要的用户流程是直接编辑尺寸。点击某个节点后,它会成为当前的编辑目标,此时 relation-graph 内置的尺寸调整覆盖层会出现在该节点周围。随后,用户可以拖动调整手柄,并在默认外观和两套自定义皮肤之间切换这个覆盖层。
这个示例最有价值的地方在于它的范围足够聚焦。它并不试图成为一个完整的图编辑器,而是单独抽离出“调整已选节点尺寸”所需的最小配置,并展示如何通过 CSS 重新定制内置的尺寸调整控件样式。
数据是如何组织的
图数据在 initializeGraph() 内以内联方式声明为一个 RGJsonData 对象,其中包含 rootId、一个扁平的 nodes 数组,以及一个 lines 数组。节点被刻意设计成多种样式:有的修改边框宽度或颜色,有的修改填充色或字体颜色,其中一个是圆形,两个是矩形,一个显式设置了 width 和 height,还有一个使用固定坐标。
在调用 setJsonData() 之前,只有一个预处理步骤:对每条连线做映射,补充一个生成出来的 id 值。之后,这份数据集只会被加载一次,并执行居中和缩放适配。这个示例自身的代码并没有实现服务端同步、尺寸持久化或增量式图组装。
在真实应用中,同样的数据结构可以表示仪表盘卡片、设备模块、组织单元、拓扑设备,或工作流步骤等场景。在这些场景里,操作人员需要在上下文中调整节点尺寸,但不必构建一个更大型的创作工具。
如何使用 relation-graph
入口文件使用 RGProvider 包裹这个演示,MyGraph 则通过 RGHooks.useGraphInstance() 控制正在运行的图实例。在挂载时,它会使用 setJsonData() 加载内联数据集,然后调用 moveToCenter() 和 zoomToFit(),使示例图在初始化后即可直接使用。
图配置保持得很简洁。唯一显式设置的选项是 dragEventAction: 'move',它会把画布拖拽转换为视口平移。编辑流程由两个图事件驱动:onNodeClick 调用 setEditingNodes([nodeObject]),而 onCanvasClick 调用 setEditingNodes([])。这意味着,是否显示调整手柄完全由内置的 editing-node 状态作为唯一事实来源来控制。
尺寸调整 UI 是通过 RGSlotOnView 挂载的,而不是通过自定义节点渲染来实现。在这个 slot 内部,RGEditingNodeController 承载 RGEditingResize,让 relation-graph 为当前处于编辑状态的节点绘制并管理尺寸调整覆盖层。这个 demo 保持默认的节点主体不变,只改动编辑覆盖层。
样式分成包裹层 class 和 SCSS 覆盖两部分。根 <div> 在 class 列表中使用 myGraphStyle,SimpleUISelect 会在运行时修改这个值,而 SCSS 文件则通过 .my-graph-style-01 和 .my-graph-style-02 来重设 .rg-editing-ctrl 与 .rg-resize-ctl 的样式。同一个 SCSS 文件还为画布提供了随缩放适配的点状背景。
浮动的 DraggableWindow 属于共享的辅助基础设施,而不是这个示例的核心教学内容,但它仍然会影响运行时行为。它的设置面板通过 RGHooks.useGraphStore() 读取图配置,使用 graphInstance.setOptions(...) 修改滚轮和拖拽行为,并通过 prepareForImageGeneration() 与 restoreAfterImageGeneration() 导出图像。
关键交互
点击节点会进入尺寸调整编辑模式。代码会把该节点放入 editing-node 集合中,然后由 RGEditingResize 为这个被选中的目标渲染调整手柄。
拖动内置手柄会直接在画布上修改当前节点的尺寸。这个示例没有额外添加自定义的尺寸调整计算逻辑,而是依赖 relation-graph 的内置控制器。
点击画布空白区域会清空 editing-node 集合。这会立即移除尺寸调整覆盖层,使画布回到非编辑状态。
浮动选择器可以在不改变尺寸调整行为的前提下切换不同皮肤。它只修改外层包裹 class,因此同一个内置控制器会以不同的手柄形状、尺寸、偏移量和强调色显示出来。
辅助窗口本身也可以交互。它可以被拖动、最小化、展开成设置面板,用来切换滚轮和拖拽行为,也可以将当前图导出为图片,但这些控件都只是尺寸调整工作流的次要部分。
关键代码片段
下面这段代码说明,图数据以内联方式初始化,并且在加载数据集之前会先为连线生成 id。
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'Border color', borderColor: '#333333' },
{ id: 'a1-4', text: 'XXX', nodeShape: RGNodeShape.circle },
{ id: 'd', text: 'Node Size', width: 150, height: 150, color: '#ff8c00', borderWidth: 5, borderColor: '#ffd700', fontColor: '#ffffff' },
],
lines: [
{ from: 'a', to: 'b' },
{ from: 'a', to: 'a1' }
].map((line, index) => ({ ...line, id: `line_${index}` }))
};
下面这段代码展示了组件挂载时的图初始化路径。
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
下面这段代码表明,尺寸调整目标完全由 relation-graph 的 editing-node 状态控制。
const onNodeClick = (nodeObject: RGNode, $event: RGUserEvent) => {
console.log('onNodeClick:', nodeObject);
graphInstance.setEditingNodes([nodeObject]);
};
const onCanvasClick = () => {
graphInstance.setEditingNodes([]);
};
下面这段代码展示了 UI 只通过修改包裹层 class 来切换尺寸调整控制器的不同皮肤。
<div className={`my-graph ${myGraphStyle}`} style={{ height: '100vh' }}>
<DraggableWindow>
<SimpleUISelect
data={[
{value: '', text: 'Default'},
{value: 'my-graph-style-01', text: 'Customize Style 1'},
{value: 'my-graph-style-02', text: 'Customize Style 2'},
]}
currentValue={myGraphStyle}
onChange={setMyGraphStyle}
/>
</DraggableWindow>
下面这段代码展示了内置的尺寸调整覆盖层是在视图层的什么位置挂载的。
<RelationGraph
options={graphOptions}
onCanvasClick={onCanvasClick}
onNodeClick={onNodeClick}
>
<RGSlotOnView>
<RGEditingNodeController>
<RGEditingResize />
</RGEditingNodeController>
</RGSlotOnView>
</RelationGraph>
下面这段代码展示了第二套自定义皮肤如何仅通过 SCSS 修改控制器颜色和手柄几何参数。
.my-graph-style-02 {
.relation-graph {
.rg-editing-ctrl {
--editor-main-color: #f616df;
box-shadow: 0 0 0 2px var(--editor-main-color);
--my-size-big: 20px;
--my-size-small: 8px;
.rg-resize-ctl {
--resize-handler-size: 12px;
--resize-handler-offset: -5px;
}
}
}
}
这个示例的独特之处
对比数据表明,这个示例的独特性并不只是因为它使用了 RGEditingNodeController、RGEditingResize、editing-node 状态,或共享的浮动辅助窗口。附近的示例,如 batch-operations-on-nodes、edit-node-text、gee-node-alignment-guides 和 freely-draw-lines-on-canvas,也复用了这套编辑脚手架中的部分内容。
真正突出的地方在于它的目标足够聚焦。这个示例专注于一个已选节点工作流:点击节点、显示内置的尺寸调整覆盖层,并可选择在默认外观与两套自定义 SCSS 皮肤之间切换。与 batch-operations-on-nodes 和 gee-node-alignment-guides 相比,它把交互预算投入在尺寸调整控制器的主题化上,而不是多选工具、吸附或拖拽时的对齐辅助。
与 edit-node-text 或 freely-draw-lines-on-canvas 相比,它也是一个更直接的几何编辑参考。这些示例更依赖自定义节点 slot、内联文本输入、绘图图层或节点创建流程。而这个示例保留了原生节点渲染,并演示了如何在尺寸调整行为保持不变的情况下,仅通过 CSS 重设内置尺寸调整控件的样式。
准备好的稀有性说明还表明,实时皮肤切换和混合节点几何采样也很重要。这个示例刻意混合了不同尺寸的圆形与矩形节点,从而可以在同一块紧凑画布中,针对不同形状测试尺寸调整覆盖层的效果。
这种模式还可应用于哪里
这种模式非常适合那些用户需要调整现有图元素尺寸、但又不需要完整创作环境的工具。例如仪表盘布局编辑器、组织架构维护工具、设备或平面图标注工具、拓扑图清理工具,以及带有可调整尺寸卡片的白板模板。
在生产版本中,可以保留相同的 editing-node 工作流,同时把内联示例数据替换为 API 数据,在尺寸调整后持久化 width 和 height,加入权限校验,或为不同编辑模式提供更多控制器皮肤。这里可复用的核心思想是:把尺寸调整行为保留在 relation-graph 的内置控制器中,并通过外层包裹级别的 CSS 自定义它的外观。