你以为毛玻璃效果只能在 macOS 上跑?今天 Safari 把这件事彻底原生化了
设计师兴奋地甩来一个毛玻璃效果的参考图,说「这个在 iOS 上能跑吗」。你心里咯噔一下——上一次在 iPhone 上跑 backdrop-filter,滚动的时候帧率直接掉到 15fps,设计师的脸比你手机屏幕还黑。这件事,卡了很多人好几年。但 Safari 17 以后,苹果终于把 GPU 合成那层窗户纸捅破了。
backdrop-filter 和 filter 的根本区别
filter 是给元素本身和它所有的内容加滤镜,backdrop-filter 只模糊元素背后的那层内容,元素本身的文字和图片完全不受影响。所以一个毛玻璃卡片文字是清晰的,背景是模糊的。
/* filter 模糊了元素本身 */
.with-filter { filter: blur(10px); }
/* backdrop-filter 只模糊背景,元素内容不受影响 */
.with-backdrop-filter {
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px); /* Safari 17 以下必须写这行 */
background: rgba(255, 255, 255, 0.15);
}
Safari 之前为什么卡
每个 backdrop-filter 元素都会触发浏览器为它背后那一层内容单独创建一个 GPU 合成层。如果背后是复杂的滚动区域,浏览器不仅要渲染内容,还要实时跑多 pass 的高斯模糊 shader,然后把结果和元素本身合成。Safari 之前的实现没有对这种情况做足够优化,滚动时合成层反复重建,帧率直接崩溃。
iOS 上这个问题更明显,因为 iOS 的 Safari 没有桌面版 Safari 的那么激进的 GPU 加速策略。
Safari 17/18 做了什么
iOS Safari 17 开始,backdrop-filter 的 GPU 合成路径得到了显著优化。苹果在 WebKit 层引入了更高效的离屏渲染缓存机制——如果 backdrop-filter 的参数不变化(比如一个导航栏的模糊效果),Safari 会复用之前的渲染结果而不是每帧重新计算。滚动时合成层的更新频率大幅降低,帧率从之前的 15fps 回到了 60fps。
/* 导航栏毛玻璃效果,现在在 iOS 上可以放心用 */
.nav-glass {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 12px 16px;
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
/* Safari 17+ 不需要再 hack transform: translateZ(0) 了 */
}
生产级毛玻璃三件套
一个视觉完整且性能可控的毛玻璃卡片:
.glass-card {
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}
/* 无障碍降级 */
@media (prefers-reduced-transparency: reduce) {
.glass-card {
backdrop-filter: none;
background: rgba(255, 255, 255, 0.92);
}
}
性能三原则
尽管 Safari 修好了,backdrop-filter 仍然是你工具箱里最重的属性之一。用的时候盯紧三件事:
模糊值有上限。 模糊半径和 GPU 成本是二次方关系,blur(40px) 的渲染代价大约是 blur(20px) 的 4 倍。实际项目里 10-16px 就够用了,再大视觉收益递减,性能成本反而上来。
限制同时使用的数量。 每个 backdrop-filter 都会创建一个独立合成层,合成层多了 GPU 内存会告急。推荐每个页面不超过 8-10 个 backdrop-filter 元素,禁止在循环生成的列表项里逐个加 backdrop-filter。
不要在滚动时修改 blur 值。 有些团队想实现「滚动时模糊渐变」效果,这会在滚动时反复触发合成层的重新计算,是 backdrop-filter 最糟糕的用法。想实现模糊变化,用 opacity 代替。
三步下一步
第一步:移除 Safari workaround。现在 Safari 17+ 的 iOS 设备已经覆盖了大多数苹果用户,检查项目里有没有 transform: translateZ(0) hack 是专门为了解决 backdrop-filter 滚动抖动加的,有的话可以开始逐步移除。
第二步:建立性能基准。用 Chrome DevTools Layers 面板(Rendering → Show layer borders)看一下现有页面有多少合成层,记录当前帧率。然后对毛玻璃元素加 will-change: backdrop-filter 之后再测一次,确认帧率没有下降。
第三步:定团队规则。给 backdrop-filter 设一个使用上限:单页不超过 10 个,模糊值不超过 16px,禁止在 scroll 事件里改 blur 值。这三条规则写进 CSS 规范或 Code Review Checklist,之后新加毛玻璃效果都要过这条线。
结论
backdrop-filter 这个属性本身没有问题,是 Safari 之前的实现有 bug。现在 Safari 17/18 把 GPU 合成那层优化补上了,iOS 上的毛玻璃效果终于从「能用吗」变成「怎么用好」的问题。
设计师可以大胆提毛玻璃效果的方案,不用再问「Safari 能跑吗」。前端工程师可以开始清理那些 transform hack 和 Canvas 降级方案。无障碍规范要求尊重 prefers-reduced-transparency,记得写。
评论区
登录后可评论。