Skip to content

feat(#2246): 给多选框增加整体rotate 和 resize#5042

Open
zhangxilong-43 wants to merge 2 commits intoantvis:masterfrom
zhangxilong-43:bugfix/#2246
Open

feat(#2246): 给多选框增加整体rotate 和 resize#5042
zhangxilong-43 wants to merge 2 commits intoantvis:masterfrom
zhangxilong-43:bugfix/#2246

Conversation

@zhangxilong-43
Copy link

Title: feat(selection): support group Resize & Rotate for multi-selected nodes

📝 Description

为 Selection 插件新增框选后统一 Resize 和 Rotate 的能力。选中 2 个及以上节点时,选框上出现 8 个缩放手柄和 1 个旋转手柄,拖拽手柄可对所有选中节点进行等比缩放或绕选框中心旋转。

核心改动:

  • src/plugin/selection/selection.ts(+430 行):手柄渲染、resize/rotate 事件处理和算法逻辑
  • src/plugin/selection/style/raw.ts(+67 行):手柄 CSS 样式
  • src/model/model.ts(+2 行):BatchName 联合类型新增 'group-rotate' | 'group-resize'

设计要点:

  1. 最小侵入性:对核心代码仅改动 model.ts 的 2 行类型扩展;所有功能逻辑封装在 Selection 插件内部,新增方法均为 protected,不改变任何已有公共 API
  2. 默认关闭:不配置 resizable/rotatable 时行为与原来 100% 一致
  3. 旋转算法采用快照 + 绝对定位:避免增量旋转的浮点累积误差和 theta 角跨越 0°/360° 边界跳变的问题,保证旋转 360° 后精确回到原位
  4. 缩放算法基于归一化坐标:记录每个节点在选框内的相对位置和相对尺寸(relX/relY/relW/relH),缩放时按比例还原
  5. 复用已有机制:事件绑定复用 delegateEvents/delegateDocumentEvents,批操作复用 model.startBatch/stopBatch(History 天然兼容),变换过程复用 refreshSelectionBoxes 同步更新

🖼️ Screenshot

以下是改动后的效果
Image

Image

💡 Motivation and Context

当前 X6 的 Transform 插件仅支持对单个节点进行 Resize/Rotate,Selection 插件支持多选后整体拖拽移动,但不支持统一缩放和旋转。在图编辑器场景中,框选后统一变换是一个常见需求(类似 Figma 的多选变换行为)。

API 使用示例:

import { Graph, Selection } from '@antv/x6'

const graph = new Graph({ container, width: 800, height: 600 })

graph.use(
  new Selection({
    rubberband: true,
    multiple: true,
    showNodeSelectionBox: true,
    // 启用组缩放
    resizable: true,
    // 或传入详细配置
    // resizable: { minWidth: 20, minHeight: 20, preserveAspectRatio: true },

    // 启用组旋转(15° 步进吸附)
    rotatable: { grid: 15 },
    // 或 rotatable: true(默认 15° 步进)
  }),
)

新增配置项:

配置项 类型 默认值 说明
resizable boolean | { minWidth?, minHeight?, preserveAspectRatio? } undefined(关闭) 启用框选后统一缩放
rotatable boolean | { grid? } undefined(关闭) 启用框选后统一旋转,grid 为角度吸附步长

新增事件:

事件 触发时机 参数
selection:rotate 旋转开始(mousedown) { cells: Node[], angle: number }
selection:rotating 旋转中(mousemove) { cells: Node[], angle: number }
selection:rotated 旋转结束(mouseup) { cells: Node[], angle: number }
selection:resize 缩放开始(mousedown) { cells: Node[] }
selection:resizing 缩放中(mousemove) { cells: Node[] }
selection:resized 缩放结束(mouseup) { cells: Node[] }

🧩 Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Enhancement (changes that improvement of current feature or performance)
  • Refactoring (changes that neither fixes a bug nor adds a feature)
  • Test Case (changes that add missing tests or correct existing tests)
  • Code style optimization (changes that do not affect the meaning of the code)
  • Docs (changes that only update documentation)
  • Chore (changes that don't modify src or test files)

🔍 Self Check before Merge

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求为图编辑器的 Selection 插件带来了重要的功能升级,允许用户对多个选中的节点进行整体的缩放和旋转操作。这一改进极大地提升了用户在处理复杂图表时的效率和体验,使其能够像在 Figma 等设计工具中一样,对一组元素进行统一的变换。通过引入新的交互手柄和精密的算法,确保了变换的准确性和流畅性,同时保持了对现有代码库的低影响性。

Highlights

  • 多选框整体缩放与旋转功能: 为 Selection 插件新增了框选后统一 Resize 和 Rotate 的能力。当选中两个及以上节点时,选框上将出现 8 个缩放手柄和 1 个旋转手柄,用户可以通过拖拽手柄对所有选中节点进行等比缩放或绕选框中心旋转。
  • 最小侵入性设计: 该功能设计上遵循最小侵入性原则,核心代码仅改动了 model.ts 中的 2 行类型扩展。所有功能逻辑都封装在 Selection 插件内部,新增方法均为 protected,不改变任何已有公共 API。
  • 默认关闭与可配置性: 新功能默认关闭,只有在配置 resizablerotatable 选项时才会启用,确保与现有行为 100% 一致。同时提供了详细的配置项,如 resizable 可配置 minWidthminHeightpreserveAspectRatiorotatable 可配置 grid(角度吸附步长)。
  • 精确的变换算法: 旋转算法采用快照 + 绝对定位,有效避免了增量旋转的浮点累积误差和角度边界跳变问题,保证 360° 旋转后能精确回到原位。缩放算法基于归一化坐标,记录每个节点在选框内的相对位置和尺寸,实现按比例还原缩放。
  • 新增事件: 引入了一系列新的事件,包括 selection:rotateselection:rotatingselection:rotated 用于旋转操作,以及 selection:resizeselection:resizingselection:resized 用于缩放操作,方便开发者监听和响应组变换过程。
Changelog
  • examples/src/pages/case/bpmn.tsx
    • 导入了 Selection 模块。
    • graph.use 中启用了 Selection 插件的 resizablerotatable 配置。
  • examples/src/pages/case/class.tsx
    • 导入了 Selection 模块。
    • graph.use 中启用了 Selection 插件的 resizablerotatable 配置。
  • examples/src/pages/case/dag.tsx
    • 为 Selection 插件实例添加了 showNodeSelectionBoxresizablerotatable 配置。
  • examples/src/pages/case/elk.tsx
    • 导入了 Selection 模块。
    • graph.use 中启用了 Selection 插件的 resizablerotatable 配置。
  • examples/src/pages/case/er.tsx
    • 导入了 Selection 模块。
    • graph.use 中启用了 Selection 插件的 resizablerotatable 配置。
  • examples/src/pages/case/mind.tsx
    • 为 Selection 插件实例添加了 multiplerubberbandshowNodeSelectionBoxresizablerotatable 配置。
  • examples/src/pages/case/swimlane.tsx
    • 导入了 Selection 模块。
    • graph.use 中启用了 Selection 插件的 resizablerotatable 配置。
  • examples/src/pages/plugins/selection/index.tsx
    • 为 Selection 插件实例添加了 resizablerotatable 配置。
  • src/model/model.ts
    • BatchName 类型中新增了 'group-rotate''group-resize'
  • src/plugin/selection/selection.ts
    • 导入了 PointsnapToGridAngleResizeDirection 类型。
    • 新增了用于管理组变换状态的保护属性,如 groupHandlesRenderedgroupRotatinggroupResizing、快照数组、旋转中心和角度等。
    • 为组缩放和旋转手柄的鼠标按下事件添加了事件监听器。
    • 更新了 onMouseUp 方法,以处理组旋转和组缩放结束时的逻辑,包括停止批处理操作和触发相应的事件。
    • 更新了 onMouseMove 方法,以调用 doGroupRotatingdoGroupResizing 进行组变换操作。
    • 新增了 getSelectedNodesgetSelectionBBox 辅助方法,用于获取选中的节点和选择框的边界。
    • 实现了 renderGroupTransformHandlesremoveGroupTransformHandles 方法,用于渲染和移除组变换手柄。
    • 实现了 onGroupRotateMouseDowndoGroupRotating 方法,处理组旋转的鼠标按下和拖动逻辑,包括节点快照、角度计算、网格吸附和节点变换。
    • 实现了 getRotateGrid 方法,用于获取旋转网格配置。
    • 实现了 onGroupResizeMouseDowndoGroupResizing 方法,处理组缩放的鼠标按下和拖动逻辑,包括节点快照、边界框计算、最小尺寸限制、长宽比保持和节点变换。
    • 实现了 getResizeOptions 方法,用于获取缩放配置。
    • clear 方法中增加了移除组变换手柄的逻辑。
    • refreshSelectionBoxes 方法中调用了 renderGroupTransformHandles
    • SelectionImplCommonOptions 接口中新增了 resizablerotatable 配置项。
    • SelectionImplEventArgsRecord 接口中新增了 selection:rotateselection:rotatingselection:rotatedselection:resizeselection:resizingselection:resized 等事件类型。
    • classNames 对象中新增了 groupResizegroupRotate 类名。
    • 扩展了 CommonEventDataaction 类型,新增了 'group-rotating''group-resizing'
    • 定义了 GroupTransformEventDataGroupRotatingEventDataGroupResizingEventDataNodeSnapshotRotateNodeSnapshot 等新接口。
  • src/plugin/selection/style/raw.ts
    • 新增了 .x6-widget-selection-group-resize 样式,定义了 8 个缩放手柄的样式和位置,并设置了不同的光标。
    • 新增了 .x6-widget-selection-group-rotate 样式,定义了旋转手柄的样式和位置。
Activity
  • zhangxilong-43 创建了此拉取请求,标题为“feat(图形多选后如何做统一操作 rotate、resize 等 #2246): 给多选框增加整体rotate 和 resize”,并提供了详细的描述。
  • 拉取请求描述中包含了核心改动、设计要点、效果截图、动机与背景、API 使用示例、新增配置项和新增事件的详细说明。
  • 作者将此更改标记为“New feature”(新功能)。
  • 作者确认代码遵循项目规范,需要更新文档,并已阅读贡献指南,所有新旧测试均已通过。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 为多选框增加了整体旋转和缩放的功能,这是一个很棒的新特性。核心逻辑主要在 src/plugin/selection/selection.ts 中,代码实现质量很高,特别是旋转部分采用了快照和绝对定位来避免累积误差。

我发现了一个关键问题和一些可以改进的地方:

  1. 在计算选中节点组的边界框时,没有考虑节点的旋转角度,这会导致选择框大小不正确,从而影响旋转中心和缩放基准。
  2. 在缩放逻辑中,存在一个可以简化的表达式和一个魔法数字,建议提取为常量以提高代码可读性。

具体的修改建议请看我的评论。

Note: Security Review did not run due to the size of the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant