核心是交互基于显示尺寸而导出必须映射回原始像素:拖拽框仅为视觉参考,真实裁剪坐标需用缩放比(naturalWidth/rect.width)将鼠标位置实时换算为原图坐标,并在导出前对x、y、width、height做边界校验与最小尺寸归一化。

高分辨率图片在 Canvas 上裁剪时,坐标换算的核心是:**所有交互操作基于显示尺寸,但导出必须映射回原始像素**。用户拖拽的裁剪框位置、宽高,只是视觉参考;真正裁剪用的坐标,必须按原图实际尺寸重新计算。
缩放比必须提前计算并缓存
图片加载完成后,立刻获取 img.naturalWidth 和 img.naturalHeight,再除以 Canvas 的当前显示宽高(注意不是 canvas 元素的 width/height 属性,而是实际渲染尺寸,可用 getBoundingClientRect() 获取),得出缩放比:
-
横向缩放比 =
img.naturalWidth / canvas.getBoundingClientRect().width -
纵向缩放比 =
img.naturalHeight / canvas.getBoundingClientRect().height - 若图片等比缩放显示,两个比值一致,可只存一个;否则需分别处理(比如容器强制宽高比时)
鼠标坐标要实时转换为原图坐标
监听 pointerdown 和 pointermove 时,用 canvas.getBoundingClientRect() 获取画布左上角在视口中的位置,再减去 clientX/Y,得到相对于画布的坐标,最后乘以对应方向的缩放比:
- 原图 X 坐标 =
(clientX - rect.left) * scaleX - 原图 Y 坐标 =
(clientY - rect.top) * scaleY - 这个换算必须在每次移动时执行,不能只做一次——因为缩放、滚动、响应式重排都可能改变
rect
裁剪区域导出前必须归一化校验
用户可能拖出越界、负值或过小的区域。导出前必须用原始尺寸做边界检查和修正:
立即学习“前端免费学习笔记(深入)”;
x = Math.max(0, Math.min(x, naturalWidth - width))y = Math.max(0, Math.min(y, naturalHeight - height))width = Math.max(20, Math.min(width, naturalWidth - x))height = Math.max(20, Math.min(height, naturalHeight - y))
归一化后,再传给 ctx.drawImage(img, x, y, width, height, 0, 0, outputWidth, outputHeight) 裁剪输出。
移动端和缩放适配要特别注意
移动端存在设备像素比(devicePixelRatio),Canvas 绘制时若未处理,会导致模糊或错位:
- 创建输出 Canvas 时,设其
width和height为目标尺寸 × window.devicePixelRatio - 同时用 CSS 设置 Canvas 的
style.width和style.height为目标尺寸(如400px),让浏览器自动缩放渲染 - 这样既保持清晰度,又不影响坐标换算逻辑(因为缩放比仍基于 CSS 尺寸)



















