核心是先等图加载完、再算裁剪区、最后用drawimage九参数绘制;需在img.onload中执行,处理跨域、内存泄漏,并按目标宽高比居中裁剪后缩放至指定尺寸,再通过toblob导出精确像素的证件照。

网页端用 Canvas 裁剪并缩放证件照,核心是“先等图加载完、再算裁剪区、最后 drawImage 九参数绘制”,整个过程不依赖第三方库也能稳定实现。
图片必须加载完成才能操作
用户选中文件后,不能立刻绘图——Image 对象的 naturalWidth/naturalHeight 在 onload 前为 0,此时调用 drawImage 不报错但不出图。正确做法是把所有裁剪逻辑放在 img.onload 回调里:
- 用
FileReader读取文件,得到 Data URL 后赋给img.src - 如果是 CDN 或跨域图片(比如用户粘贴的外链),记得加
img.crossOrigin = 'anonymous',否则后续导出或读像素会触发安全错误 - 上传本地文件后,建议用
URL.createObjectURL(file)设置 src,处理完及时调用URL.revokeObjectURL()防止内存泄漏
证件照常用裁剪逻辑:居中+等比缩放
证件照通常要求固定宽高比(如 1:1、3:4、2:3),裁剪时要优先保留人脸主体,避免拉伸变形。计算裁剪区域的关键是匹配目标比例:
- 设原图尺寸为
img.width × img.height,目标区域为targetW × targetH - 若
img.width / img.height ≥ targetW / targetH(原图更宽):以高度满裁,sh = img.height,sw = img.height × (targetW / targetH) - 若
img.width / img.height (原图更高):以宽度满裁,<code>sw = img.width,sh = img.width / (targetW / targetH) - 再算居中起点:
sx = (img.width − sw) / 2,sy = (img.height − sh) / 2
例如:上传一张 1800×1200 的照片,要裁成 300×400 的 3:4 证件照。因 1800/1200 = 1.5 img.width/img.height 和 targetW/targetH 比较,此处 1800/1200 = 1.5 > 0.75,所以按高度裁,sh = 1200,sw = 1200 × 0.75 = 900,sx = (1800−900)/2 = 450,sy = 0。
用 drawImage 九参数一次性裁剪+缩放
Canvas 本身没有“裁剪后自动缩放”功能,但 ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh) 这种写法能一步到位:
-
sx/sy/sw/sh是你在原图上框出的像素区域(即上面算出的居中裁剪区) -
dx/dy一般为 0, 0,表示从 canvas 左上角开始画 -
dw/dh就是你想要的最终输出尺寸,比如 300×400,它会自动把裁出的区域等比缩放到这个大小
执行后,canvas 上就显示了裁剪并缩放好的证件照,但注意:这只是渲染结果,还没生成新图片文件。
导出裁剪结果用于上传
drawImage 只是画到 canvas 上,要真正拿到可上传的图片数据,得调用:
-
canvas.toDataURL('image/jpeg', 0.9):返回 base64 字符串,适合小图或调试预览 -
canvas.toBlob(callback, 'image/jpeg', 0.9):更推荐,直接生成 Blob 对象,可转为 FormData 提交到后端
如果后端要求特定尺寸(如严格 300×400 像素),确保你创建的输出 canvas 就是那个尺寸:outputCanvas.width = 300; outputCanvas.height = 400;,再调用 drawImage 绘制,这样 toBlob 输出的就是精确尺寸。











