液态玻璃和过去的毛玻璃最大的区别是折射。毛玻璃只是把背后的东西模糊掉;液态玻璃的边缘是弯的,光线穿过时会偏折,背景在边缘处被拉伸、挤压。浏览器里没有现成的折射效果,但可以用 SVG 滤镜拼出来。
三层
- 折射:用
feDisplacementMap按一张位移图把背景像素挪开。 - 材质:轻度模糊和饱和度提升,由“清透 / 着色”滑块控制强弱。
- 高光和暗边: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 会被当成线性值,整张图都会偏。