You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将SVG元素自中心旋转转换为多元素绕共同中心旋转

解决多SVG矩形绕共同原点旋转且无跳动/拖动偏移问题

核心逻辑:绕共同原点旋转的正确矩阵推导

要让元素绕共同原点(cx, cy)旋转θ角度且无跳动,必须抛弃直接修改transform-origin的思路,而是通过矩阵组合实现绕点旋转。正确的变换步骤为:

  1. 平移元素,使共同原点移动到坐标系原点:translate(-cx, -cy)
  2. 执行旋转:rotate(θ)
  3. 平移回原位置:translate(cx, cy)

所有选中元素使用同一共同原点计算矩阵,即可保证元素间的距离和角度关系完全不变。

修正你的矩阵变换代码

你之前的代码错误混合了元素中心与共同原点的平移逻辑,以下是修正后的实现:

const selectedElement = select(`[id="${id}"]`);
const elementCenter = { x: centerOfElement.x, y: centerOfElement.y };
const commonOrigin = { x: centerOrigin!.x, y: centerOrigin!.y };
const angleRad = angleDegrees * Math.PI / 180;

// 1. 构建绕共同原点旋转的基础矩阵
const translateToCommonOrigin = mat2d.fromTranslation([], [-commonOrigin.x, -commonOrigin.y]);
const rotateMatrix = mat2d.fromRotation([], angleRad);
const translateBack = mat2d.fromTranslation([], [commonOrigin.x, commonOrigin.y]);

// 组合矩阵:先平移到共同原点 → 旋转 → 平移回原位置
let rotationTransform = mat2d.create();
mat2d.multiply(rotationTransform, translateToCommonOrigin, rotateMatrix);
mat2d.multiply(rotationTransform, rotationTransform, translateBack);

// 2. 合并元素现有变换矩阵(如果存在)
const currentTransform = selectedElement.attr('transform');
let currentMatrix = mat2d.create(); // 默认单位矩阵
if (currentTransform) {
  const [a, b, c, d, e, f] = currentTransform.match(/matrix\(([^)]+)\)/)[1]
    .split(',')
    .map(Number);
  currentMatrix = [a, b, c, d, e, f];
}

// SVG矩阵是右乘逻辑,新变换需作用在现有变换之后,所以顺序为:新矩阵 × 现有矩阵
const finalMatrix = mat2d.create();
mat2d.multiply(finalMatrix, rotationTransform, currentMatrix);

// 应用最终矩阵到元素
selectedElement.attr(
  'transform',
  `matrix(${finalMatrix[0]}, ${finalMatrix[1]}, ${finalMatrix[2]}, ${finalMatrix[3]}, ${finalMatrix[4]}, ${finalMatrix[5]})`
);

解决旋转后拖动偏移问题

旋转后拖动出现方向偏移,是因为鼠标偏移量是全局坐标系的,而元素已处于旋转后的局部坐标系。需要将全局偏移转换为元素局部坐标系的偏移:

// 假设拖动时获取的全局偏移为dx、dy
const currentMatrix = ...; // 获取元素当前的transform矩阵
const angleRad = Math.atan2(currentMatrix[1], currentMatrix[0]); // 从矩阵中提取当前旋转角度
const cos = Math.cos(angleRad);
const sin = Math.sin(angleRad);

// 将全局偏移转换为元素局部坐标系的偏移
const localDx = dx * cos + dy * sin;
const localDy = -dx * sin + dy * cos;

// 更新矩阵的平移分量(矩阵第4、5位对应e、f)
currentMatrix[4] += localDx;
currentMatrix[5] += localDy;

selectedElement.attr('transform', `matrix(${currentMatrix.join(', ')})`);

关键注意事项

  • 始终不要修改transform-origin:所有变换逻辑完全通过matrix实现,避免因transform-origin变更导致的元素跳动。
  • 统一共同原点:所有选中元素必须使用同一个共同原点计算旋转矩阵,才能保证元素间相对位置不变。

内容的提问来源于stack exchange,提问作者Petr Stoklas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 19:40:56