Canvas 2D 实现色彩分离需用 getImageData + putImageData 对 R/G/B 通道分别偏移整数像素并边界检查;WebGL 方案更高效,须在 Fragment Shader 中对纹理三次采样并归一化偏移量。
getImageData + putImageData 实现色彩分离HTML5 本身没有内置“色彩分离”滤镜,所谓“HTML5 滤镜”实际依赖 Canvas 的像素级操作。核心是把 R、G、B 通道分别偏移不同像素,再合成——不是调色,而是错位。
常见错误是直接改 CSS filter 或 SVG filter,它们不支持通道级位移;也有人误用 ctx.filter(仅支持模糊/亮度等基础效果,不支持分离)。
实操建议:
canvas.getContext('2d') 获取上下文,drawImage 把图片画进去ctx.getImageData(0, 0, width, height) 拿到原始像素数组 data(Uint8ClampedArray,每 4 个值为 R/G/B/A)ImageData,遍历原数组,把 R 通道写入偏移 (x + 2, y) 位置,G 写入 (x, y + 2),B 写入 (x - 2, y + 1) —— 偏移量可调,但必须是整数像素IndexSizeError
ctx.putImageData() 贴回画布gl_FragColor 分别采样偏移纹理Canvas 2D 方案在大图或动画中容易卡顿,真正实用的色彩分离得上 WebGL。本质是 Fragment Shader 中对同一纹理做三次采样,R/G/B 各自加不同 vec2 偏移。
立即学习“前端免费学习笔记(深入)”;
关键点:
texture2D(u_texture, v_uv + u_offsetR) 取 R 分量,同理 G/Bu_offsetR、u_offsetG、u_offsetB 是 uniform,单位是纹理坐标(0.0–1.0),不是像素,需按画布宽高归一化mix() 简单叠加,要分别赋值:gl_FragColor = vec4(r, g, b, 1.0),否则通道会混合失真ShaderMaterial 封装,但注意默认材质会启用 lighting,得设 lights: false
做不了真分离,只能模拟色偏有人试过 filter: sepia(1) hue-rotate(90deg) 或 SVG 的 ,这些只是线性变换 RGB 值,所有像素统一计算,不会产生通道错位效果——也就是没“分离感”。
典型表现:
feColorMatrix 的 matrix 参数是 5×4 变换矩阵,只能缩放/平移颜色值,不能空间位移像素getImageData 有尺寸限制iOS 15+ 的 Safari 在 canvas 宽高乘积超 ~1600万 像素时,getImageData 会直接返回空数据且不报错——这是静默降级,极易被忽略。
应对方式:
canvas.width * canvas.height 判断是否超限,超了就缩放图片再处理requestAnimationFrame 里反复调用 getImageData,它触发强制同步读取,严重阻塞主线程
色彩分离真正的复杂点不在算法,而在像素边界的越界检查、跨平台读取限制、以及 WebGL 下纹理坐标的归一化换算——这三处出错,效果就全歪了。
来电咨询