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

图片旋转后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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:00:37