图像裁剪需将CSS像素坐标经Canvas缩放比换算为真实像素坐标,再按原始分辨率映射:sx = left×(canvas.width/rect.width)×(img.naturalWidth/canvas.width),sy同理;drawImage必须用精确源坐标与宽高,避免四舍五入偏差。

图像裁剪结合缩放比例精准定位,核心在于把用户看到的裁剪框位置(CSS像素)正确换算成原始图片的真实像素坐标。这一步出错,导出图就会偏移、切头切脚——不是代码没写对,而是坐标没对齐。
必须区分三套尺寸:显示尺寸、Canvas像素尺寸、图片原始分辨率
很多人混淆这三者,导致计算全错:
- img.width / img.height 是 CSS 渲染尺寸(可能被 CSS 缩放拉伸),不能用于计算裁剪起点
- img.naturalWidth / img.naturalHeight 才是图片真实像素尺寸,所有裁剪计算必须基于它
- canvas.width / canvas.height 是 Canvas 的实际像素容量(比如 retina 屏上可能是 800×600),而 canvas.clientWidth / clientHeight 是它在页面上占的 CSS 像素大小
用 getBoundingClientRect + devicePixelRatio 换算真实坐标
假设你在画布上拖出了一个裁剪框,其 left=50px、top=30px、width=200px、height=150px(都是相对于 canvas 元素的 CSS 像素):
- 先获取 canvas 的缩放比:
const rect = canvas.getBoundingClientRect(); const scale = canvas.width / rect.width; - 把裁剪框的 CSS 坐标转为 Canvas 像素坐标:
const realX = 50 * scale; const realY = 30 * scale; - 再映射到原始图片像素空间:
sx = realX * (img.naturalWidth / canvas.width); sy = realY * (img.naturalHeight / canvas.height);
drawImage 九参数必须用真实源坐标,不能“估”
算出 sx、sy、sw、sh 后,直接传给 drawImage(img, sx, sy, sw, sh, 0, 0, targetW, targetH):
-
sx/sy必须是整数或保留小数(Canvas 支持亚像素绘制),但不能四舍五入成近似值——哪怕差 0.5 像素,高清屏下就明显偏移 -
sw/sh要严格按目标宽高比和原始图尺寸推导,例如原图 1920×1080,目标 400×300(4:3),因 1920/1080 ≈ 1.78 > 1.33,所以sh = 1080,sw = 1080 × (4/3) = 1440,sx = (1920 − 1440)/2 = 240 - 最终调用:
ctx.drawImage(img, 240, 0, 1440, 1080, 0, 0, 400, 300)
导出前别忘了跨域与内存清理
如果图片来自 CDN 或用户上传:
- 加载时加
img.crossOrigin = 'anonymous',否则toDataURL报错 - 用户上传文件后,用
URL.createObjectURL(file)赋值,导出完成立即URL.revokeObjectURL(img.src) - 避免多次重复创建 canvas,复用已有上下文,减少内存抖动


















