Canvas马赛克原理是通过getImageData获取像素数据,将每个块(如8×8)替换为左上角像素颜色;需同源、图片加载完成;块尺寸影响性能与效果,建议从8起步并做坐标对齐;动态更新应仅重绘变化区域并用requestAnimationFrame节流;移动端需换算触摸坐标,iOS需注意内存限制。

Canvas getImageData 马赛克原理很直接
马赛克不是靠 CSS 滤镜,HTML5 里真正可控的实现方式是操作 canvas 的像素数据。核心就是:把一块区域内的所有像素,替换成该区域左上角(或平均)像素的颜色。
关键限制在于:getImageData 只能在同源 canvas 上调用,跨域图片会触发安全错误;且必须等图片完全加载(img.onload)后再绘图,否则读到的是空数据。
- 先用
ctx.drawImage(img, 0, 0)把图片画进离屏 canvas - 再用
ctx.getImageData(x, y, width, height)获取目标区域原始像素 - 遍历每个“马赛克块”(比如 8×8 像素),取块内第一个像素值,批量赋给整个块
- 最后用
ctx.putImageData()写回
马赛克块大小直接影响性能和效果
块尺寸不是越大越好。16×16 看起来“糊”,但处理快;4×4 更细腻,但循环量翻 16 倍。实际建议从 blockSize = 8 起手调试。
注意:块尺寸必须能整除目标区域宽高,否则边缘会被截断。更稳妥的做法是用 Math.floor 对坐标做对齐:
立即学习“前端免费学习笔记(深入)”;
const x0 = Math.floor(x / blockSize) * blockSize; const y0 = Math.floor(y / blockSize) * blockSize;
这样无论鼠标点在哪,都能自动吸附到最近的马赛克格子起点。
用 requestAnimationFrame 实现动态马赛克区域
如果想让马赛克随鼠标移动实时更新(比如打码工具),别在 mousemove 里直接重绘整张图——太卡。只重绘变化区域即可:
- 记录上一次马赛克区域
lastRect = {x, y, w, h} - 新位置计算出
newRect,用unionRect合并两个区域作为重绘范围 - 仅对这个合并区域执行
getImageData → 处理 → putImageData - 用
requestAnimationFrame节流,避免帧率暴跌
漏掉这一步,小图都可能掉到 10fps 以下。
移动端 touch 事件要额外处理缩放与坐标偏移
在 iPhone 或安卓浏览器里,touchstart 的 clientX/clientY 是视口坐标,不是 canvas 像素坐标。必须换算:
const rect = canvas.getBoundingClientRect(); const x = (e.touches[0].clientX - rect.left) * (canvas.width / rect.width); const y = (e.touches[0].clientY - rect.top) * (canvas.height / rect.height);
不处理这个,手指点的位置和马赛克出现的位置会严重错位,尤其页面有缩放或滚动时。另外 iOS Safari 对 getImageData 的内存限制更严,块尺寸超过 32 容易直接崩溃。










