图片旋转后Canvas尺寸未适配:需同步适配Overlay及裁剪功能
解决Canvas图片裁剪工具旋转时的尺寸适配问题
核心思路
旋转90°/270°时,必须同步交换Canvas的宽高,同时调整图片绘制的旋转中心、Overlay遮罩的尺寸,最后修正裁剪坐标的转换逻辑,确保裁剪区域对应原始图片的正确位置。
1. 旋转时切换Canvas宽高并正确绘制图片
维护当前旋转角度的状态,每次旋转后根据角度切换Canvas的宽高,以画布中心为旋转中心绘制图片,确保图片始终居中适配新的Canvas尺寸:
// 初始化变量 const canvas = document.getElementById('cropCanvas'); const ctx = canvas.getContext('2d'); const img = new Image(); let currentRotation = 0; // 仅记录0/90/180/270四个角度 let imgWidth, imgHeight; // 图片加载完成后初始化画布 img.onload = () => { imgWidth = img.width; imgHeight = img.height; canvas.width = imgWidth; canvas.height = imgHeight; drawRotatedImage(); updateOverlay(); }; img.src = 'your-image-path'; // 绘制旋转后的图片 function drawRotatedImage() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.save(); // 将旋转中心移至画布中心 ctx.translate(canvas.width / 2, canvas.height / 2); ctx.rotate((currentRotation * Math.PI) / 180); // 居中绘制图片(以图片中心对齐画布中心) ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight); ctx.restore(); } // 旋转触发函数(示例:点击按钮旋转90度) function rotate90() { currentRotation = (currentRotation + 90) % 360; // 90/270度时交换Canvas宽高 if (currentRotation === 90 || currentRotation === 270) { [canvas.width, canvas.height] = [imgHeight, imgWidth]; } else { [canvas.width, canvas.height] = [imgWidth, imgHeight]; } drawRotatedImage(); updateOverlay(); }
2. 适配Overlay遮罩
Overlay(Canvas上层的DOM遮罩层)需要同步Canvas的宽高和位置,若有裁剪框,需根据旋转角度交换其宽高以保持比例:
function updateOverlay() { const overlay = document.getElementById('cropOverlay'); const cropBox = document.getElementById('cropBox'); // 同步遮罩层与Canvas的尺寸、位置 overlay.style.width = `${canvas.width}px`; overlay.style.height = `${canvas.height}px`; overlay.style.left = `${canvas.offsetLeft}px`; overlay.style.top = `${canvas.offsetTop}px`; // 旋转90/270度时交换裁剪框的宽高(保持原比例) if (currentRotation === 90 || currentRotation === 270) { const tempWidth = cropBox.style.width; cropBox.style.width = cropBox.style.height; cropBox.style.height = tempWidth; } // 让裁剪框始终居中 cropBox.style.left = `${(canvas.width - parseFloat(cropBox.style.width)) / 2}px`; cropBox.style.top = `${(canvas.height - parseFloat(cropBox.style.height)) / 2}px`; }
3. 修正旋转后的裁剪坐标
旋转后画布坐标系与原始图片不一致,需将裁剪框坐标转换为原始图片对应区域,通过临时Canvas反向旋转实现正确裁剪:
function getCroppedResult() { const cropBox = document.getElementById('cropBox'); const cropX = parseFloat(cropBox.style.left); const cropY = parseFloat(cropBox.style.top); const cropW = parseFloat(cropBox.style.width); const cropH = parseFloat(cropBox.style.height); const tempCanvas = document.createElement('canvas'); const tempCtx = tempCanvas.getContext('2d'); // 根据旋转角度调整临时画布尺寸并反向旋转 switch(currentRotation) { case 90: tempCanvas.width = cropH; tempCanvas.height = cropW; tempCtx.translate(tempCanvas.width / 2, tempCanvas.height / 2); tempCtx.rotate(-Math.PI / 2); // 反向旋转回0度 tempCtx.drawImage(canvas, -cropY - cropH, cropX, cropH, cropW); break; case 180: tempCanvas.width = cropW; tempCanvas.height = cropH; tempCtx.translate(tempCanvas.width / 2, tempCanvas.height / 2); tempCtx.rotate(Math.PI); tempCtx.drawImage(canvas, -cropX - cropW, -cropY - cropH, cropW, cropH); break; case 270: tempCanvas.width = cropH; tempCanvas.height = cropW; tempCtx.translate(tempCanvas.width / 2, tempCanvas.height / 2); tempCtx.rotate(Math.PI / 2); tempCtx.drawImage(canvas, cropY, -cropX - cropW, cropH, cropW); break; default: // 0度 tempCanvas.width = cropW; tempCanvas.height = cropH; tempCtx.drawImage(canvas, cropX, cropY, cropW, cropH); break; } // 返回裁剪后的图片(可按需转为Blob或直接插入DOM) return tempCanvas.toDataURL('image/png'); }
关键注意事项
- 始终以画布中心作为旋转中心,避免图片偏移;
- 旋转后必须重新计算Overlay和裁剪框的位置,确保交互区域与Canvas匹配;
- 裁剪时的坐标转换要严格对应旋转角度,反向旋转临时画布是最直观的实现方式。
内容的提问来源于stack exchange,提问作者Noel john
相关产品推荐
相关产品推荐

