在浏览器里做液态玻璃

· 技术

液态玻璃和过去的毛玻璃最大的区别是折射。毛玻璃只是把背后的东西模糊掉;液态玻璃的边缘是弯的,光线穿过时会偏折,背景在边缘处被拉伸、挤压。浏览器里没有现成的折射效果,但可以用 SVG 滤镜拼出来。

三层

  1. 折射:用 feDisplacementMap 按一张位移图把背景像素挪开。
  2. 材质:轻度模糊和饱和度提升,由“清透 / 着色”滑块控制强弱。
  3. 高光和暗边:Golden Gate 给玻璃加了更亮的镜面高光和一圈压暗的外边,这两样用 CSS 画,不进滤镜。

位移图怎么算

玻璃边缘的截面取一条凸起的超椭圆曲线 y = (1 − (1 − x)⁴)^¼:中间平,到边缘时平滑地弯下去。对曲线上每个点求法线,再按斯涅尔定律 n₁·sin θ₁ = n₂·sin θ₂(空气 1.0,玻璃 1.5)算出光线偏折后在背景上落点移动了多远。

这个距离只和“离边缘多远”有关,所以先算一条 128 个采样的一维曲线,再按圆角矩形的距离场铺满整张图:红色通道存水平位移,绿色通道存垂直位移,128 表示不动。

<filter id="lg1" color-interpolation-filters="sRGB">
  <feImage href="data:image/png;base64,…" result="m"/>
  <feDisplacementMap in="SourceGraphic" in2="m" scale="18"
      xChannelSelector="R" yChannelSelector="G" result="d"/>
  <feGaussianBlur in="d" stdDeviation="6"/>
  <feColorMatrix type="saturate" values="1.8"/>
</filter>

.menu { backdrop-filter: url(#lg1); }

兼容性

把 SVG 滤镜用在 backdrop-filter 上,目前只有 Chromium 内核支持。Safari 和 Firefox 会退回到普通的模糊加饱和度,高光和暗边照样有,只是边缘不会弯。

几个坑

  • 位移图的尺寸必须和元素一致。元素一变大小就要重算,所以按“宽 × 高 × 圆角”缓存滤镜,用 ResizeObserver 触发更新。
  • 带 backdrop-filter 的元素会成为子元素的“背景根”。玻璃里面再套玻璃,里面那层只能看到外层自己,什么也折射不到。程序坞的提示气泡因此放在程序坞外面。
  • 颜色空间要设成 sRGB,否则位移图的 128 会被当成线性值,整张图都会偏。

← 液态玻璃博客