CSS旋转图像的Canvas坐标转换:实现选中区域高亮
旋转图像选中区域坐标转原图坐标方案
场景:页面中有一个通过CSS transform: rotate(degree) 实现视觉旋转的img元素(原图未实际旋转),用户在旋转后的图像上选择区域后,拿到的参数(nativeImageWidth、nativeImageHeight、positionX、positionY、width、height、angle)均相对旋转后的图像。需要将这些坐标转换为原图对应坐标,在Canvas绘制高亮矩形后,替换img的src为处理后的图像数据。
坐标转换核心逻辑
解决这个问题的关键是逆旋转变换:把旋转后图像上的点映射回原图坐标系,步骤如下:
- 将旋转后图像的选中点平移到以图像中心为原点的坐标系
- 对该点进行逆角度的旋转变换(抵消CSS旋转的影响)
- 将变换后的点平移回原图的坐标系
- 处理特殊角度(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
相关产品推荐
相关产品推荐

