
3 分钟#design #css
毛玻璃也是一种纸:一次材质设计
Liquid Glass 用久了会冷。直到我把一层纸纹压进玻璃中央,才明白玻璃和纸不是两个阵营,而是同一个材质轴上的两端。
Liquid Glass 刚流行那阵子,我把所有卡片都换成了毛玻璃。
一周之后我把它改回来了。原因很简单:冷。
玻璃折射、高光边、饱和度拉满的背景,单看每一屏都很漂亮,连着读三篇东西就像在机场广告灯箱前面站了二十分钟。
后来我意识到问题不在玻璃本身,而在"玻璃背后什么都没有"。玻璃之所以像玻璃,是因为它压着东西。
一个念头:纸是被抽干的玻璃
某个凌晨我盯着磨砂玻璃杯看,突然想到:如果玻璃背后压着的是一张纸呢?
磨砂玻璃本来就是"不完全透明"的材料,它把背后的东西糊成一层薄薄的色块——这不正是被水浸润的纸吗?反过来,纸是抽干了水分的玻璃:不透明、不折射、但保留了纤维的肌理。
于是我把两者放到同一根轴上:
高含水量 ── 玻璃(透明·折射·水印纸纹 .04)
│
│ 同一个材质轴
│
低含水量 ── 纸(不透明·暖白·纸纹本体 .55)
这件事的好处立刻显现:入口页和文章页不再是两个网站,它们只是同一块材料的两端。
玻璃的三要素
要让一块面板真的读作"玻璃",而不是"贴了层灰膜的方框",必须同时具备三样东西。
1. 顶边高光
--specular: inset 0 1.5px 0 rgba(255,255,255,.85),
inset 0 -1px 0 rgba(255,255,255,.14);
只有顶边一条 1.5px 的亮线,加底边一条 1px 的微反。少一条,面板立刻变平。
2. 背景折射
backdrop-filter: blur(18px) saturate(170%);
blur 决定"毛"的程度,saturate 决定它吸上来多少颜色。只模糊不增饱和,玻璃会显脏。
3. 中央纸纹
这是我自己加的那一笔。用 feTurbulence 生成一层噪点,铺在玻璃里,opacity: .04:
.glass::after {
background-image: url("data:image/svg+xml,..."); /* 静态可平铺,不动画 */
opacity: var(--grain-opacity);
mix-blend-mode: var(--grain-mix); /* 亮色 multiply / 暗色 screen */
}
它几乎看不见。但把 opacity 从 .04 调到 0 再对比,玻璃会立刻从"压着纸的磨砂玻璃"变回"一块塑料"。
纸的那一半
纸态反而更简单:不透明度拉满,纸纹浓度从 .04 提到 .55,投影全部去掉。
.paper {
background: var(--bg);
--grain-opacity: .55;
box-shadow: none; /* 纸不抬离页面 */
}
纸不投影是条硬规矩。加了阴影的纸就是卡片,不是纸。
两条性能红线
材质用得爽,但有两件事千万别做:
- 不动画
blur半径。 每一帧都要重新做一次高斯模糊,中端手机上直接掉帧。 - 不动画纸纹。
feTurbulence是 CPU 渲染的,动一下就是灾难。纸纹只能是静态的可平铺贴图。
所以我的圆斑只用 transform 漂移,纸纹一次生成、永不再动。
那个开关
材质轴需要一个显式开关,不然用户永远不知道"纸态"存在。
页头那枚 💧 / 📄 的药丸做的就是这件事:<html data-surface="glass|paper">,三值同拍过渡——--glass-bg、backdrop-filter、--grain-opacity——240ms。
入口页默认给你玻璃,文章页默认给你纸,但每一页你都可以亲手把它们调回去。
毛玻璃也是一种纸。玻璃是纸的含水态,纸是玻璃的干态,我们只是在同一个材质轴上滑动。
这句话后来被我写进了设计文档的第一行。