胶片颗粒感必须主动注入随机性,无法用backdrop-filter实现;主流方案仅有canvas像素扰动、svg feturbulence、webgl着色器采样三条路径,选择取决于性能、兼容性与控制精度需求。

直接用 backdrop-filter 加颗粒感行不通——它只模糊背后内容,不生成噪声。要实现胶片颗粒感,必须主动注入随机性,主流路径只有三条:Canvas 像素扰动、SVG feTurbulence、或 WebGL 着色器采样。选哪条取决于你对性能、兼容性和控制粒度的要求。
Canvas 方案:手动叠加正态分布噪声
这是最可控也最容易理解的方式,适合中低粒子密度(
- 别用
Math.random()均匀加 ±5,这会产生数码噪点;改用 Box-Muller 变换生成近似正态分布的偏移值,让多数颗粒微小、少数略大 - 颗粒强度必须随原始像素亮度变化:暗部
grainScale *= (1 - luma),亮部抑制,模拟胶片 gamma 特性 - 先生成低分辨率噪声图(如 64×64),再双线性缩放到目标尺寸,避免逐像素独立扰动导致的“碎玻璃”感
-
getImageData()前必须确保图片已加载且设置crossOrigin="anonymous",否则会触发 SecurityError - 处理完记得用
Math.min(255, Math.max(0, value))截断每个通道,否则溢出会导致大面积黑/白块
SVG feTurbulence:硬件加速但参数敏感
性能好、可 CSS 控制、支持 CSS 动画过渡,但 baseFrequency 错一点,颗粒就失真。
-
baseFrequency必须设在0.001–0.005区间(不是 0.01 或 0.05),两个值建议略有差异(如0.0012 0.0018)以模拟各向异性胶片纹理 - 必须设
type="turbulence"和numOctaves="2",单层 octave 显得呆板,三层以上移动端易卡顿 - 用
feComposite operator="arithmetic" k1="0" k2="1" k3="1" k4="0"实现线性叠加,比multiply更保细节、不压暗部 - 整个
<svg></svg>要设width="0" height="0"并position: absolute; left: -9999px隐藏,否则可能意外参与文档流布局
WebGL 方案:高帧率但需纹理预处理
适合已有 Three.js 或自建渲染管线的项目,粒子数超 50 万也不掉帧,但噪声纹理不能随便用 PNG 替代。
- 噪声纹理必须是运行时生成的
Uint8Array,尺寸推荐 128×128 或 256×256;直接用哈希函数(如fract(sin(...)))会在移动端出现 tile 边界——精度丢失导致相邻像素 hash 值突变 - 纹理应含 RGB 三通道:R 控制亮度扰动,G/B 微调色偏(如 R+2, G−1, B−1 表现暖粒),避免灰度单通道
- 采样坐标要用
uv + noise.rg * grainScale,其中grainScale是 uniform,典型值为0.002–0.008 - 若用
THREE.Texture,务必设texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping,否则边缘拉伸会破坏颗粒自然感
最容易被忽略的是色彩空间问题:Canvas 像素默认在 sRGB 编码域,但噪声叠加应在近似线性光下做,否则暗部噪点会被指数放大。实际项目里,不校正也能用,但只要发现颗粒在阴影里“炸开”或亮部发灰,就得考虑加一层 gamma 补偿——这不是玄学,是物理成像逻辑的必然映射。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











