近乎全黑的页面中央是一行绿色大字。字形本身是清晰的,但它下面还压着一层同样的字——那一层被一团不断变化的噪声揉得东倒西歪,边缘化开、向上散出绿色的光晕,看上去像文字正在冒出鬼气。整个效果由 CSS 配一段内联 SVG 滤镜完成,JavaScript 只有四行,作用是把标题文字复制进一个自定义属性,好让伪元素能显示同样的内容。
这个效果由哪几层叠成
三层同样的字。真实的 h1 是白色的清晰层,::after 是一层薄薄的绿色描边,::before 才是被滤镜揉过的鬼影层,三层严格重合。
光晕是六层阴影叠出来的。都往上偏移、模糊半径依次加大、透明度依次减小,于是荧光只往上飘,符合「热气上升」的直觉。
文字内容不用写两遍。脚本把 h1 的文本写进一个 CSS 变量,两个伪元素的 content 都读这个变量,改标题时只改 HTML 一处。
噪声是怎么让字动起来的
滤镜里先用 feTurbulence 生成一张分形噪声图,再让它的色相在一秒内转满一圈——这一步是整个效果动起来的唯一来源:
<animate attributeName="values" from="0" to="360" dur="1s" repeatCount="indefinite" />
色相一直在变,等于这张噪声图在不停地翻滚。最后 feDisplacementMap 用它去推挤文字的像素,推多远由一个位移强度控制。所以画面里那种「一直在揉」的感觉,并不是文字在动,而是拿来推挤它的那张噪声图在动。
能改的地方
荧光颜色在伪元素的多层 text-shadow 里,统一换掉那个绿色就能做成红色、紫色或冷蓝。抖动幅度在滤镜的位移强度上,数值调小会变成轻微的水波感,调大就接近完全溶解。噪声的粗细由 feTurbulence 的基础频率控制,值越小纹理越大团。动画快慢改那一行的时长即可,放慢到三四秒会更接近缓慢飘散的烟雾。
常见问题
换了标题文字,鬼影层还是旧的?
伪元素读的是脚本写入的 CSS 变量,而脚本只在页面加载时执行一次。如果标题是后端渲染或前端动态生成的,需要在内容更新后再调用一次那段赋值逻辑。
中文显示效果会变差吗?
不会,滤镜作用于渲染结果,和文字是哪种语言无关。只是原配的英文装饰字体不含中文,中文会回退到系统字体;想要更厚重的笔画,可以换一款中文黑体或美术字体。
会不会很耗性能?
湍流滤镜逐像素计算,字号越大开销越高。整页只放一两处标题是没问题的,但不建议给长段正文或大量元素套同一个滤镜。
适合用在哪
万圣节与恐怖主题活动页、游戏站点的标题、音乐与潮牌的视觉页面,也可以把滤镜单独摘出来用在图标或图片上。滤镜链与阴影配方的完整写法都在源码里,下载后可查看。