CSS filter 属性支持 grayscale()、sepia()、blur()、contrast()、brightness()、saturate()、hue-rotate() 等函数,可链式组合且顺序影响结果;但无法实现高光/阴影分离、胶片颗粒或局部模糊等像素级控制,此类需求须借助 canvas getImageData 手动处理。
直接用 filter 属性就能实现大多数常见滤镜效果,但真要控制高光/阴影分离、胶片颗粒或局部模糊,必须进 canvas 拿像素手动算——CSS 只是快捷入口,不是万能开关。
CSS filter 是最轻量的方案,支持 grayscale()、sepia()、blur()、contrast()、brightness()、saturate()、hue-rotate() 等函数,还能链式组合:
blur(2px) contrast(1.2) 和 contrast(1.2) blur(2px) 渲染不同drop-shadow() 不是滤镜,它不感知图像内容,只在元素外框加投影,别指望它模拟浮雕或晕影sepia(0.8) 加 contrast(0.85) + blur(0.3px) 能凑出老照片感,但无法还原褪色偏移或青橙分离filter: url(#svg-filter) 支持不稳定,kernelUnitLength 设非 1 值可能直接失效当你要做灰度加权转换、通道非线性映射、颗粒叠加或方向性边缘增强,getImageData() 是绕不开的起点:
img.onload = () => { ctx.drawImage(img, 0, 0); const data = ctx.getImageData(0, 0, w, h).data; }
data 是 Uint8ClampedArray,每 4 个值一组:R、G、B、A;别忘了最后设 data[i+3] = 255,否则透明区域漏底gray(x+1,y+1) - gray(x,y),再映射到 0–255 同时加到 RGB 三通道一张 1920×1080 图片,主线程跑完整像素循环很容易卡顿 100ms+,尤其在移动端:
立即学习“前端免费学习笔记(深入)”;
OffscreenCanvas:Chrome/Firefox 支持,可在 Web Worker 里创建并调用 getContext('2d'),完全不阻塞 UIrequestIdleCallback 分块处理,每次只算 50 行,留出空闲时间响应滚动或点击img.onload 里直接全量处理——图片加载完成 ≠ 渲染就绪,先检查 canvas.offsetWidth 是否为正数sharp)仍是更稳的选择filter: url(#id) 可调用内联 SVG 中定义的 <feConvolveMatrix> 或 <feTurbulence>,适合浮雕、噪点等硬件加速场景,但兼容性和调试成本高:
<feConvolveMatrix> 的 targetX/targetY 建议设为 1,确保卷积中心对齐;preserveAlpha="true" 必须开启,否则 PNG 透明区失真<feTurbulence> 做胶片颗粒时,baseFrequency 应设为 0.002–0.005,不是 0.05;numOctaves="2" 比默认 1 更自然precision mediump float 下系数易被截断,建议显式声明 highp 并实机测试