用 canvas 实现马赛克需 getImageData 采样+fillRect 填色或 drawImage 缩放(配合 imageSmoothingEnabled = false),禁用 putImageData 直接放大;须同源加载,局部打码需坐标校准与区域限定循环。

用 canvas 的 getImageData + putImageData 实现马赛克
HTML5 本身没有“马赛克”原生 API,必须靠 canvas 手动采样重绘。核心思路是:把图像按块划分,每块取左上角(或平均)像素,再用这个颜色填满整块。
关键限制:图片必须同源,否则 getImageData 会因跨域抛出 SecurityError。本地直接双击打开 HTML 文件(file:// 协议)也触发该错误,得用本地服务器(如 python3 -m http.server)跑。
实操建议:
- 先用
drawImage把图片画到 canvas 上,确保尺寸匹配 - 用
ctx.getImageData(x, y, width, height)读取每个马赛克单元的原始像素 - 计算该区域内所有像素的平均 RGB 值(或直接取
[0]位置的值,更“硬边”) - 用
ctx.fillStyle+fillRect填满对应区域
ctx.putImageData 不适合直接做马赛克?
很多人误以为把降采样后的 ImageData 用 putImageData 放大就能马赛克——不行。因为 putImageData 是逐像素贴图,不会插值也不会重复填充;它只是把内存里那一坨数据原样写回去。你要的是“1 像素 → N×N 块”,得靠循环 + fillRect 或反复 drawImage 缩放绘制。
立即学习“前端免费学习笔记(深入)”;
更高效的做法是:用 drawImage 把小区域缩放到大尺寸(比如把 8×8 区域 draw 到 64×64),浏览器默认用最近邻插值(imageSmoothingEnabled = false),效果就是清晰马赛克块。
实操建议:
- 设马赛克粒度为
blockSize = 16 - 循环遍历
i += blockSize,j += blockSize - 每次调用
ctx.drawImage(canvas, i, j, blockSize, blockSize, i, j, blockSize * 4, blockSize * 4) - 务必提前设
ctx.imageSmoothingEnabled = false,否则边缘模糊
如何对局部区域(比如人脸)动态打码?
全图马赛克简单,但只对某块区域(如坐标 {x:100, y:200, width:80, height:80})处理,就得截取子 canvas 或限定循环范围。
基于HTML5 canvas实现图片马赛克特效,非常不错的HTML5图片特效代码,支持自定义马赛克模糊程度,挺不错的H5页面特效,当然还需要JS的支持。
注意 canvas 坐标系和 DOM 坐标可能不一致(尤其有 CSS 缩放时),推荐用 getBoundingClientRect() 校准鼠标位置后再映射到 canvas 像素坐标。
实操建议:
- 创建临时
offscreenCanvas(或复用同一 canvas 的不同区域)做局部处理 - 只在目标矩形内循环:例如
for (let y = roi.y; y - 避免重复读取整图
getImageData,只读 ROI 区域:ctx.getImageData(roi.x, roi.y, roi.width, roi.height) - 若需实时响应(如拖拽选区),别在
mousemove里反复重绘整图,只重绘 ROI 及其周边缓存区
移动端触摸事件下马赛克响应迟钝?
原因常是:在 touchmove 中高频调用 getImageData(它很慢),或未防抖/节流,导致 UI 线程卡死。
真实项目中,应把计算逻辑移到 Web Worker,或改用纯 CSS 滤镜模拟(仅限简单效果):比如 filter: blur(8px) contrast(2),但不是真马赛克,且无法精确控制块大小。
实操建议:
- 禁用
touchmove默认行为:e.preventDefault()防止页面滚动干扰 - 用
requestAnimationFrame节流重绘,而非直接在事件回调里执行 - 马赛克粒度动态调整:手指移动快时用大块(
blockSize = 32),静止后切回精细(blockSize = 8) -
安卓 WebView 和 iOS Safari 对
getImageData性能差异明显,建议实测blockSize > 4时帧率
实际最难的不是算法,是跨域加载、触摸坐标映射、以及在低端 Android 设备上维持 30fps —— 这些地方一不留神就白屏或卡死。









