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

CSS旋转图像的Canvas坐标转换:实现选中区域高亮

旋转图像选中区域坐标转原图坐标方案

场景:页面中有一个通过CSS transform: rotate(degree) 实现视觉旋转的img元素(原图未实际旋转),用户在旋转后的图像上选择区域后,拿到的参数(nativeImageWidth、nativeImageHeight、positionX、positionY、width、height、angle)均相对旋转后的图像。需要将这些坐标转换为原图对应坐标,在Canvas绘制高亮矩形后,替换img的src为处理后的图像数据。

坐标转换核心逻辑

解决这个问题的关键是逆旋转变换:把旋转后图像上的点映射回原图坐标系,步骤如下:

  1. 将旋转后图像的选中点平移到以图像中心为原点的坐标系
  2. 对该点进行逆角度的旋转变换(抵消CSS旋转的影响)
  3. 将变换后的点平移回原图的坐标系
  4. 处理特殊角度(90/270度)的宽高互换问题

转换公式

假设:

  • 旋转后图像宽高:90/270度时,rotatedWidth = nativeImageHeight、rotatedHeight = nativeImageWidth;其他角度与原图一致
  • 旋转中心坐标:cx = rotatedWidth / 2,cy = rotatedHeight / 2
  • 角度转弧度:rad = -angle * Math.PI / 180(负号表示逆旋转)

对于旋转后区域左上角点(x,y),转换到原图的坐标(newX, newY)公式:

// 平移到中心原点
tx = x - cx;
ty = y - cy;

// 逆旋转变换
newTx = tx * Math.cos(rad) - ty * Math.sin(rad);
newTy = tx * Math.sin(rad) + ty * Math.cos(rad);

// 平移回原图坐标系
newX = newTx + nativeImageWidth / 2;
newY = newTy + nativeImageHeight / 2;

实现代码

坐标转换函数

function getTranslatedRect(x, y, w, h, angle, nativeImageWidth, nativeImageHeight) {
    // 判断是否为90/270度旋转(宽高互换)
    const is90or270 = angle % 180 === 90;
    const rotatedWidth = is90or270 ? nativeImageHeight : nativeImageWidth;
    const rotatedHeight = is90or270 ? nativeImageWidth : nativeImageHeight;

    // 旋转中心坐标
    const cx = rotatedWidth / 2;
    const cy = rotatedHeight / 2;
    // 角度转弧度(逆旋转用负角度)
    const rad = (-angle) * Math.PI / 180;

    // 转换选中区域左上角坐标
    const tx = x - cx;
    const ty = y - cy;
    const newTx = tx * Math.cos(rad) - ty * Math.sin(rad);
    const newTy = tx * Math.sin(rad) + ty * Math.cos(rad);
    let newX = newTx + nativeImageWidth / 2;
    let newY = newTy + nativeImageHeight / 2;

    // 处理宽高互换
    let newWidth = is90or270 ? h : w;
    let newHeight = is90or270 ? w : h;

    // 边界校验:避免坐标超出原图范围
    newX = Math.max(0, newX);
    newY = Math.max(0, newY);
    newWidth = Math.min(newWidth, nativeImageWidth - newX);
    newHeight = Math.min(newHeight, nativeImageHeight - newY);

    return {
        newX,
        newY,
        newWidth,
        newHeight,
        newCanvasWidth: nativeImageWidth,
        newCanvasHeight: nativeImageHeight
    };
}

完整运行代码

function getTranslatedRect(x, y, w, h, angle, nativeImageWidth, nativeImageHeight) {
    const is90or270 = angle % 180 === 90;
    const rotatedWidth = is90or270 ? nativeImageHeight : nativeImageWidth;
    const rotatedHeight = is90or270 ? nativeImageWidth : nativeImageHeight;

    const cx = rotatedWidth / 2;
    const cy = rotatedHeight / 2;
    const rad = (-angle) * Math.PI / 180;

    const tx = x - cx;
    const ty = y - cy;
    const newTx = tx * Math.cos(rad) - ty * Math.sin(rad);
    const newTy = tx * Math.sin(rad) + ty * Math.cos(rad);
    let newX = newTx + nativeImageWidth / 2;
    let newY = newTy + nativeImageHeight / 2;

    let newWidth = is90or270 ? h : w;
    let newHeight = is90or270 ? w : h;

    newX = Math.max(0, newX);
    newY = Math.max(0, newY);
    newWidth = Math.min(newWidth, nativeImageWidth - newX);
    newHeight = Math.min(newHeight, nativeImageHeight - newY);

    return {
        newX,
        newY,
        newWidth,
        newHeight,
        newCanvasWidth: nativeImageWidth,
        newCanvasHeight: nativeImageHeight
    };
}

// 执行图像处理逻辑
let img = document.getElementById('translatedImage');
// 示例参数:旋转90度后选中的区域
let translatedRect = getTranslatedRect(60, 35, 120, 65, 90, 350, 500);

let canvas = document.createElement('canvas');
canvas.width = translatedRect.newCanvasWidth;
canvas.height = translatedRect.newCanvasHeight;

let ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'rgba(255, 165, 0, 0.5)'; // 半透明高亮,避免遮挡原图
ctx.fillRect(translatedRect.newX, translatedRect.newY, translatedRect.newWidth, translatedRect.newHeight);

img.src = canvas.toDataURL('image/jpg', 1.0);
.canvas {
    width: 500px;
    height: 500px;
    text-align: center;
    margin: 10px 0;
}

.rotated {
    transform: rotate(90deg);
}
<h3>旋转后图像(用户选中区域)</h3>
<div class="canvas">
    <img id="translatedImage" class="rotated" src="https://i.ibb.co/fNDs6qQ/Untitled.png" crossorigin="anonymous">
</div>

<h3>原图</h3>
<div class="canvas">
    <img id="originalImage" src="https://i.ibb.co/fNDs6qQ/Untitled.png" crossorigin="anonymous">
</div>

说明

  • 函数加入了边界校验,避免转换后的坐标超出原图范围,防止Canvas绘制出错
  • 高亮颜色使用半透明,方便对比原图与处理后的效果
  • 支持任意角度旋转(0/90/180/270/自定义角度均适用)

内容的提问来源于stack exchange,提问作者S Ala

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 18:07:13