canvas drawimage裁剪核心是源区域(sx,sy,sw,sh)与目标区域(dx,dy,dw,dh)的精准对应,易错点在于坐标逻辑混淆、边界越界及onload绑定时机错误。

大图直接扔进 <img> 标签里加载,卡顿、白屏、内存爆掉——这不是图的问题,是加载方式错了。真正有效的切片加载,核心不在“怎么切”,而在“怎么按需画”和“怎么不崩”。
canvas drawImage 裁剪参数别写反
切片本质是内存级像素提取,不是生成新文件。drawImage() 的 9 个参数中,最容易搞混的是前四(源区域)和后四(目标区域)的坐标逻辑:
- 前四个:
drawImage(img, sx, sy, sw, sh)—— 从原图左上角起,向右sx、向下sy的位置开始,截取宽sw、高sh的一块 - 后四个:
drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)—— 把刚才那块,画到 canvas 上坐标(dx, dy)处,拉伸/缩放为宽dw、高dh - 常见错误:把
sx/sy当成 canvas 坐标,结果所有碎片都叠在左上角;或把sw/sh设成固定值却没检查是否超出原图边界,导致某几块空白
toBlob 导出时闭包和顺序必须手动管
循环调用 toBlob() 看似简单,但浏览器不会等你——它异步执行,且所有回调共享同一个循环变量。不处理,最后导出的 100 张图可能全是第 100 块。
- 用立即执行函数包裹:
(function(canvas, idx) { canvas.toBlob(blob => saveAs(blob, `tile_${idx}.png`)); })(tempCanvas, i); - 要严格编号,别依赖循环索引;改用
Promise.all(tiles.map((c, i) => toBlobPromise(c, i)))更稳妥 - 导出前务必重置临时 canvas:
tempCanvas.width = tempCanvas.height = 0,否则内存持续增长,尤其在移动端容易触发 GC 卡顿
大图切片前必须检查 naturalWidth × naturalHeight
部分 Android WebView 和旧版 Safari 对单个 canvas 像素总数有限制(常见阈值为 16777216 ≈ 4096×4096)。一张 7000×5000 的图,哪怕只是读取 img.width,也可能返回缩放后尺寸,误导你。
- 永远用
img.naturalWidth和img.naturalHeight判断真实像素量 - 超限时,先用
createImageBitmap()或 Canvas 2D 缩放降采样,再切;别直接用原图创建大 canvas - 切完每一块后,立刻释放该块 canvas:
tempCanvas.width = 0; tempCanvas.height = 0;,不是靠 GC 等着回收
最常被跳过的其实是加载时机判断——img.onload 必须绑定在 img.src 赋值之前,否则某些缓存场景下会直接跳过,drawImage 静默失败,canvas 一片黑,还查不出错在哪。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











