最快方案是CSS filter: invert(100%),但仅对img等替换元素生效;Canvas中可靠方案是getImageData+手动翻转RGB值,兼顾跨浏览器一致性与像素级控制。

直接用 CSS filter: invert(100%) 最快,但只对元素本身生效
给 <img> 标签加一行样式就能反色:style="filter: invert(100%)"。它不操作像素,而是对渲染结果做实时滤镜,性能好、写法简单。
常见错误现象:加了却没变化 —— 很可能图片是背景图(background-image),而 filter 对 background 不生效;或者父容器有 overflow: hidden 且反色后边缘溢出被裁掉。
- 必须作用于块级或替换元素(如
<img>、<video>、<iframe>),不能用于伪元素或文字 - Safari 需要加
-webkit-filter: invert(100%)前缀(iOS 16.4+ 和 macOS Ventura 已基本原生支持,但老版本仍需) - 多个滤镜组合时顺序重要:
filter: brightness(1.2) invert(100%)和invert(100%) brightness(1.2)效果不同 - 动画友好:配合
transition: filter 0.2s可实现平滑切换
ctx.filter = "invert(100%)" 在 Canvas 里基本不可靠
Canvas 的 ctx.filter 是对后续绘制行为起作用,不是重绘已有内容。你设了 ctx.filter = "invert(100%)",再调用 ctx.drawImage(img, 0, 0),图像才反色;但若画布上已有像素(比如之前用 putImageData() 写入的),它完全不会改变那些像素。
更麻烦的是兼容性:Safari 一直未实现 ctx.filter 对 putImageData() 或文本等非绘制操作的影响,Chrome 也只在部分版本中对 drawImage() 有效。
立即学习“前端免费学习笔记(深入)”;
- 别指望它“反转整个 canvas 当前画面”——做不到
- 它不修改
ImageData.data,所以无法和像素级处理(如灰度、蒙版)串联使用 - 调试时用
console.log(ctx.filter)能看到值,但不代表浏览器真执行了
真正可控的反色:用 getImageData() + 手动翻转 RGB
这是唯一能 100% 确保跨浏览器一致、可调试、可组合的方案。核心就三步:读像素 → 改 r/g/b → 写回去。Alpha 通道通常不动(除非你要反透明度)。
关键细节:
- 图像必须同源(或开启 CORS),否则
getImageData()抛SecurityError - canvas 尺寸最好等于图像原始宽高,否则缩放会插值模糊,影响像素计算精度
- 遍历
data数组时按i += 4走,每次处理一个像素的 R/G/B/A 四个字节 - 写法简洁:
data[i] = 255 - data[i](R)、data[i+1] = 255 - data[i+1](G)、data[i+2] = 255 - data[i+2](B) -
Uint8ClampedArray自动截断,不用Math.min/max判断越界
大图或视频帧反色,得绕开主线程阻塞
一张 2000×2000 的图有 400 万个像素,纯 JS 遍历改 data 数组在低端机上可能卡顿 50ms+,放在动画帧里会掉帧。
两个实际可用的优化路径:
- 用
OffscreenCanvas把像素计算移到 Web Worker 里,主线程只管 draw —— 注意 Chrome/Edge 支持良好,Safari 目前不支持 OffscreenCanvas 的getImageData - WebGL 方案:写一个简单 fragment shader,把纹理每个像素输出为
vec4(1.0 - textureColor.rgb, textureColor.a)。适合频繁、实时场景(如摄像头流),但开发成本高一截
小图(getImageData() 就够了;别为了“看起来高级”过早引入 WebGL 或 Worker,调试成本和兼容性代价往往远超收益。



















