最可控的图片局部变色方案是用 canvas 像素操作:图片加载完成后再绘制→获取指定区域像素数据→修改 R/G/B/A 值→putImageData 覆盖;需注意跨域限制、坐标换算、尺寸校验及设备像素比适配。

用 canvas 实现图片局部变色最可控
直接操作 DOM 图片元素无法局部着色,必须借助 canvas 读取像素并重绘。核心是:把图片画到 canvas 上 → 获取指定区域的像素数据 → 修改 data 数组中对应位置的 R/G/B 值 → 再写回 canvas。
- 必须等图片加载完成(
img.onload)再执行绘制,否则canvas读不到像素 - 注意跨域限制:如果图片来自其他域名且未开启 CORS,
ctx.getImageData()会抛出SecurityError - 修改像素时,每个像素占 4 个连续字节(R、G、B、A),索引计算公式为:
(y * width + x) * 4 - 建议先用
ctx.putImageData()做整块覆盖,避免逐像素fillRect性能差
getImageData() 和 putImageData() 的边界容易错
这两个 API 对坐标和尺寸极其敏感,常见错误是传入负数、超出画布范围或宽高为 0 —— 这会导致静默失败或白屏。
-
getImageData(x, y, width, height)的x/y是相对于 canvas 左上角的坐标,不是图片原始坐标 - 如果图片被缩放显示(比如
canvas.width≠ 图片自然宽),要按比例换算区域坐标,不能直接用鼠标事件返回的 clientX/clientY - 务必检查
width和height是否 > 0,DOM 中获取的尺寸可能是小数,需Math.floor或Math.round - 修改完
data后,必须调用putImageData(imageData, dx, dy),且dx/dy通常为0, 0(覆盖原位置)
用 globalCompositeOperation 快速覆盖局部颜色(适合简单场景)
如果只是想“盖一层纯色”而非精确调整 RGB,可以用混合模式绕过像素操作,性能更好、代码更短。
- 先用
ctx.globalCompositeOperation = 'source-atop',再fillRect(x, y, w, h),就能只在图片不透明区域上色 - 若要保留原图明暗,改用
'multiply'或'overlay'模式(但兼容性略低,IE 不支持) - 注意:该方法无法实现“仅改变红色通道”这类精细控制,本质是图层叠加
- 必须确保目标区域已绘制图片内容,否则
source-atop会画不出任何东西
移动端触摸坐标需要转换
在手机上用 touchstart 获取位置时,touches[0].clientX 是视口坐标,不是 canvas 坐标,不转换就会偏移。
立即学习“前端免费学习笔记(深入)”;
- 用
canvas.getBoundingClientRect()拿到 canvas 左上角在视口中的真实位置 - 计算公式:
const x = touch.clientX - rect.left,同理算 y - 如果页面有缩放(
viewport或 CSStransform),还要除以window.devicePixelRatio(部分安卓机需额外处理) - 推荐统一用
getBoundingClientRect()+clientX/Y,别依赖offsetX/Y(Safari 不稳定)
实际复杂点往往不在算法,而在坐标系对齐、跨域策略和设备像素比这些细节。稍不注意,看起来逻辑全对,却死活变不了色。










