网页版苹果液态玻璃(Liquid Glass)

apple-liquid-glass-webgl 是一个零依赖的 WebGL2 库,让任意 HTML 元素获得苹果风格的液态玻璃效果。 把它指向一个元素,它就会折射该元素背后真实的页面内容,并跟随滚动、缩放和动画。

npm install apple-liquid-glass-webgl

一句话答案。两行代码就能得到效果:import { LiquidGlass } from 'apple-liquid-glass-webgl', 然后 new LiquidGlass('.navbar')。元素的内容、布局和事件保持不变,玻璃绘制在子元素之后,形状和圆角取自元素的 CSS border-radius。支持 React、Next.js、Vue、Svelte 和原生 HTML;没有 WebGL2 时自动回退到 backdrop-filter。

为什么在 Safari 和 Firefox 上也不会退化。大多数液态玻璃库用 backdrop-filter: url(#filter) 驱动 SVG 置换贴图,而只有 Chromium 支持这种写法:Safari 和 Firefox 会接受这个属性、却静默丢掉 SVG 部分,最后只剩一层平铺的模糊。这个库完全不用它 —— 它把元素背后的页面重绘成纹理,再在 WebGL2 片元着色器里弯折这张纹理,而 WebGL2 在 Chrome、Edge、Firefox 和 Safari 15+ 上都已支持。而且你不会失去 DOM:被重绘的只有背景,元素自己的子节点原封不动, 文字仍然是真正的文字 —— 可以选中、可以翻译、可以被读屏软件读取。这正是大多数人选择 WebGL 方案时不得不放弃的东西。

四块液态玻璃表面折射流动的大理石背景,随后切换到地球背景并加大背景预模糊
四块玻璃表面折射实时背景。这些全部来自公开 API,演练场运行的就是同一份代码。

它是什么

apple-liquid-glass-webgl 是一个用 WebGL2 在网页上渲染苹果液态玻璃材质的开源(MIT)JavaScript 包。 浏览器不允许 WebGL 读取页面像素,所以这个库自己把玻璃背后的内容重绘成一张纹理,再用有向距离场(SDF)折射着色器处理它。 得到的表面会弯折背后的页面、在边缘收拢场景、让颜色通道轻微分离,并带有边缘光、镜面高光和轮廓细线 —— 这是苹果材质真实的光学行为,而不是一个模糊的矩形。

它可以用在普通 DOM 元素(导航栏、卡片、提示条、侧边栏)上,可以把一组元素画在同一张画布上, 也可以用在你自己的 canvas、游戏或 WebGL 场景中。零运行时依赖,原生 ES 模块,附带 TypeScript 声明,无需构建配置。

三种方案怎么选:CSS、SVG 置换贴图,还是 WebGL?

网页上做液态玻璃只有三条路,各自的短板不同。纯 CSS backdrop-filter 只能模糊和着色,不能弯折光线。 用 backdrop-filter: url(#filter) 驱动 SVG 置换贴图确实能折射,但只在 Chromium 上有效 —— Safari 和 Firefox 会丢掉滤镜,只剩一层模糊。WebGL 着色器则在任何支持 WebGL2 的浏览器上都能折射, 代价是要自己重绘背景。本库属于第三种。

CSS backdrop-filterSVG 置换贴图apple-liquid-glass-webgl
模糊与着色支持支持支持(frost、tint、tintTone)
玻璃体内折射不支持支持支持(refraction)
Safari / Firefox 上的折射不适用不支持,退化为模糊支持,两者都有 WebGL2
边缘捕捉:边界处场景被压缩不支持受 ±127px 位移范围限制支持(edgeReach、edgeWidth)
色散不支持按通道近似支持(dispersion)
边缘光、背景反射、镜面高光只能用渐变模拟只能用渐变模拟由真实背景计算得出
控件按压形变不支持不支持支持(pressure)
内容保持原生 DOM是是是,玻璃绘制在子元素之后
开销合成器每帧滤镜链一个 WebGL2 上下文,仅在变化时重绘

三者并不冲突:backdrop-filter 就是本库内置的回退方案,没有 WebGL2 的环境会退化成常规毛玻璃。

人们对 WebGL 玻璃方案最常见的顾虑 —— 会牺牲语义化 HTML 和无障碍 —— 在这里不成立:被画进 canvas 的只有背景, 元素自己的子节点从不参与重绘,所以文字可选中、可被读屏软件读取,链接可聚焦,事件处理也照常工作。

快速开始

import { LiquidGlass } from 'apple-liquid-glass-webgl';

new LiquidGlass('.navbar');

集成到此为止。玻璃上要放文字时需要一点“体积感”,tint 的曲线很缓,可读的取值大约在 0.5–0.8:

new LiquidGlass('.panel', { frost: 0.45, tint: 0.5, tintTone: 'light' }); // 深色文字
new LiquidGlass('.toast', { frost: 0.3,  tint: 0.7, tintTone: 'dark'  }); // 白色文字

卡片网格要共用一张画布,而不是每张卡片一个上下文:

new LiquidGlass('.card-grid', { targets: '.card', frost: 0.3, tint: 0.2 });
const glass = new LiquidGlass('.card', options);
await glass.ready;              // 首帧绘制完成、图片加载完成
glass.update({ tint: 0.4 });    // 合并选项
glass.refresh();                // 重新读取选择器并重绘
glass.destroy();                // 还原元素,释放 WebGL 上下文

玻璃背后是什么

默认情况下你不需要提供任何背景。backdrop: 'auto' 会按 CSS 绘制顺序(含 z-index) 重绘元素下方的页面:背景、边框、<img>、<video>、<canvas> 和文字。 滚动到固定导航栏下方的标题,会出现在玻璃里,并且和页面上的位置严格对齐。

要折射别的东西,就告诉它背后是什么:元素选择器('#hero-video')、图片地址('/wallpaper.jpg')、 类似 object-fit 的适配对象、自定义绘制函数 (ctx, region) => {},或它们组成的数组(自下而上)。 所有内容都按页面坐标注册。跨域图片需要 CORS 响应头。auto 不会重绘 ::before/::after、阴影、CSS 滤镜、SVG 和表单控件 —— 这些用绘制函数补上。

框架用法(React / Vue / Svelte)

// React:在 useEffect 中创建,清理时 destroy(StrictMode 双次挂载下安全)
function Glass({ options, ...props }) {
  const ref = useRef(null);
  useEffect(() => {
    const glass = new LiquidGlass(ref.current, options);
    return () => glass.destroy();
  }, []);
  return <div ref={ref} {...props} />;
}

// Vue
onMounted(() => { glass = new LiquidGlass(el.value, { tint: 0.2 }); });
onBeforeUnmount(() => glass.destroy());

自带控件:导航栏与开关

import { LiquidGlassNavbar, LiquidGlassSwitch } from 'apple-liquid-glass-webgl';

const nav = new LiquidGlassNavbar('#nav', {
  items: [{ value: 'home', label: '首页' }, { value: 'work', label: '作品' }],
  value: 'home',
  onChange: (value) => router.push(value),
});

const toggle = new LiquidGlassSwitch('#dark-mode', {
  checked: false,
  onChange: (checked) => document.documentElement.classList.toggle('dark', checked),
});

按住时旋钮会原地绽放,拖过中点即切换状态;方向键、Home/End 和空格都可用,带完整 ARIA。尺寸由你的 CSS 决定。

兼容性、性能与限制

常见问题

网页上怎么做出苹果的液态玻璃效果?

从 npm 安装 apple-liquid-glass-webgl,然后 new LiquidGlass('.navbar')。 元素内容、布局和事件都不受影响,形状取自元素的 CSS border-radius。不需要壁纸、不需要 canvas、不需要构建步骤。

和 CSS 毛玻璃(backdrop-filter)有什么不同?

backdrop-filter 只能模糊和着色;液态玻璃还会折射、在边缘收拢场景、让颜色轻微分离。 这个库把页面重绘后交给 WebGL2 的 SDF 折射着色器,因此有边缘捕捉、色散、边缘光、背景反射、镜面高光和轮廓细线。

支持 React / Vue / Svelte 吗?

支持,且不依赖框架:元素存在后创建,卸载时 destroy()。React 的 StrictMode 双次挂载下安全,SSR 时导入也安全。

必须自己提供背景图吗?

不需要。默认 backdrop: 'auto' 折射的就是真实页面,并按页面坐标持续对齐。

Safari 和 Firefox 上能用吗?折射会不会失效?

能用,且折射完整保留。这正是其他方案最常翻车的地方:大多数液态玻璃库依赖 backdrop-filter: url(#filter),而只有 Chromium 支持它,Safari 和 Firefox 会丢掉滤镜只留下模糊。 本库在 WebGL2 着色器里弯折背景,而 WebGL2 在 Chrome、Edge、Firefox 和 Safari 15+ 上均已支持(桌面端和移动端)。

会不会像其他 WebGL 方案那样丢失 DOM 和无障碍?

不会。只有背景被重绘进 canvas,元素自己的子节点不动,因此文字仍可选中、可翻译、可被读屏软件读取, 事件、布局和 CSS 都不受影响。玻璃位于子元素之后(z-index: -1)。

可以在 Next.js 里用吗?不用 React 行不行?

都可以。它是一个与框架无关的类,原生 HTML 加 <script type="module"> 就够了。 Next.js App Router 里,在创建实例的组件文件顶部加 'use client' 即可,不需要任何 React 封装包。

不支持 WebGL2 怎么办?

自动回退到 CSS backdrop-filter;元素上的 data-liquid-glass 属性会标明当前是 webgl 还是 fallback。

是免费开源的吗?

是,MIT 许可证,零运行时依赖,原生 ES 模块 + TypeScript 声明。源码见 GitHub,包发布在 npm。

包信息

包名apple-liquid-glass-webgl(npm)
版本2.6.0
许可证MIT
运行时依赖无
主要导出LiquidGlass、LiquidGlassWebGL、LiquidGlassNavbar、LiquidGlassSwitch、getDefaultMaterial
仓库github.com/Oliverrr2424/webgl-apple-liquid-glass
演练场oliverrr2424.github.io/webgl-apple-liquid-glass
机器可读摘要/llms.txt、/llms-full.txt