canvas图片压缩本质是“先缩放、再编码”:用drawimage()等比缩放降低分辨率,再用todataurl('image/jpeg', 0.7)控制jpeg质量;全程前端执行,需处理跨域、exif方向、内存释放及大图异步优化。

前端用 Canvas 压缩图片,本质是“先缩放、再编码”两个关键动作:通过 drawImage() 控制分辨率(第一次压缩),再用 toDataURL() 或 toBlob() 指定格式与质量(第二次压缩)。整个过程不依赖后端,所有操作在浏览器内完成。
核心步骤拆解
实际压缩不是一键调用,而是分四步可控执行:
-
读取文件:用
FileReader将用户选中的file转为dataURL或创建Image对象加载;避免直接用URL.createObjectURL()后忘记revoke,防止内存泄漏 -
创建画布并计算尺寸:根据目标宽高(如最大宽度 1200px)等比缩放,用
img.naturalWidth / img.naturalHeight算出新尺寸;不建议直接设 canvas 宽高为原图尺寸再靠 CSS 缩放——那只是显示变小,导出仍是大图 -
绘制与重采样:调用
ctx.drawImage(img, 0, 0, targetWidth, targetHeight)。浏览器自动进行双线性插值,图像分辨率真正降低 -
导出压缩结果:
canvas.toDataURL('image/jpeg', 0.7)中的0.7是 JPEG 质量系数(0–1),数值越低体积越小,但低于 0.5 可能出现明显色块;PNG 不支持质量参数,仅靠尺寸缩放压缩
关键参数怎么设才合理
没有万能参数,要按使用场景调整:
- 移动端主图:目标宽度设为 750–1200px,JPEG 质量 0.6–0.7,兼顾清晰度与体积(通常压到原图 20%–30%)
- 头像/图标类:固定输出 200×200 或 400×400,质量可降至 0.4–0.5,视觉影响小但体积锐减
-
需透明通道的图:必须用
image/png,但 PNG 无质量参数,只能靠缩小尺寸压缩;若允许降级,转 WebP(image/webp)并设质量 0.8,体积比 PNG 小 40%+,且兼容主流浏览器
避免卡顿:用 Web Worker 异步处理
大图(如 4K 照片)在主线程压缩会阻塞页面交互。解决方案是把压缩逻辑移到 Web Worker:
- 主线程读取
file.arrayBuffer(),传给 Worker;不传dataURL(Base64 太大) - Worker 内用
createImageBitmap()解码图像(比new Image()更高效),再绘制到离屏canvas - 压缩完成后,Worker 返回
Blob或压缩后的arrayBuffer,主线程用URL.createObjectURL()预览或上传
别忽略的细节
很多压缩失败或效果差,其实卡在这些地方:
-
跨域图片限制:如果
img.src是外链且未配 CORS,drawImage()会成功但toDataURL()抛错“Tainted canvas”。解决:服务端加Access-Control-Allow-Origin: *,或前端加img.crossOrigin = 'anonymous' -
EXIF 方向丢失:手机拍的照片带旋转信息(如 orientation=6),Canvas 绘制默认不识别,导致图片横躺。需用库(如
exif-js)读取 orientation 并手动旋转 canvas -
内存释放:每次压缩完记得
URL.revokeObjectURL(url)和清除 canvas DOM(或复用 canvas 元素),尤其在频繁上传场景下
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











