用canvas实现裁剪预览最可控:先加载图片获取naturalwidth/height,再用getboundingclientrect计算鼠标在图内坐标,最后drawimage截取并校验边界。

用 canvas 实现裁剪预览最可控
直接靠 CSS 裁剪(比如 object-fit 或 clip-path)只能视觉遮盖,无法真正提取裁剪区域。要预览「用户拖选后实际会保存哪一块」,必须用 canvas 绘制原始图、计算选区坐标、再 drawImage 截取——这是唯一能保证像素级准确的方式。
常见错误是把图片直接设为 canvas 背景或用 img 标签叠加,结果坐标错乱、缩放失真。关键点:所有坐标运算必须基于图片原始宽高,且 canvas 的 width/height 属性值不能用 CSS 缩放覆盖。
- 先用
URL.createObjectURL(file)加载本地图片,避免跨域问题 - 用
img.onload读取真实img.naturalWidth/img.naturalHeight - 把
canvas的width和height设为原始尺寸,再用 CSS 控制显示大小
getBoundingClientRect() 是定位选区坐标的唯一可靠方式
鼠标事件的 clientX/clientY 是相对于视口的,而图片可能被缩放、滚动、有外边距。硬写偏移量(比如减去 offsetLeft)在响应式布局里极易出错。
正确做法:对图片元素调用 getBoundingClientRect(),再用鼠标坐标减去它的 left 和 top,得到图片内的相对坐标。注意:这个计算必须在图片渲染完成、CSS 尺寸确定后执行,否则矩形位置不准。
- 监听
mousedown时立刻获取rect = img.getBoundingClientRect() - 后续
mousemove中用e.clientX - rect.left算 X,e.clientY - rect.top算 Y - 如果图片设置了
max-width: 100%,rect.width就是当前显示宽度,但原始宽高仍需从img.naturalWidth换算比例
裁剪逻辑必须做边界校验,否则 drawImage 报错
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh) 的 sx/sy(源图起始点)和 sw/sh(源图宽高)一旦超出图片范围,整个绘制会静默失败——预览 canvas 一片空白,控制台也不报错。
必须手动限制:比如用户拖出画布外的选区,要把 sx 卡在 0 ~ img.naturalWidth 之间,sw 不能超过 img.naturalWidth - sx。不校验的话,用户随便一拉就看不到预览了。
- 计算
sx = Math.max(0, Math.min(startX, endX)),同理处理sy -
sw = Math.abs(endX - startX),但最终要Math.min(sw, img.naturalWidth - sx) - 缩放预览时,目标区域
dw/dh可按需设为固定值(如 200×200),不影响源图截取
移动端 touch 事件需要单独适配,pageX/Y 不等于 clientX/Y
在手机上,touchstart 的 touches[0].clientX 才等价于桌面端的 clientX;用 pageX 会包含页面滚动偏移,导致选区飘移。另外,iOS Safari 对 canvas 的 toDataURL() 有尺寸限制(超 5000px 可能返回空字符串),大图裁剪得先缩放到安全尺寸再操作。
- 统一用
e.touches?.[0]?.clientX || e.clientX兼容 touch/mouse - 监听
touchmove时加e.preventDefault()阻止默认滚动,否则手指一滑就触发页面滚动 - 若原始图宽高 > 4000px,建议先用
canvas缩放到 80% 再进行裁剪交互,保持精度又避坑
实际裁剪预览的核心就三步:加载图 → 算坐标 → 画局部。最难的不是代码量,而是所有尺寸换算必须严格区分「自然尺寸」「显示尺寸」「canvas 像素尺寸」——这三个概念混在一起,预览必然错位。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











