@avenra/ 液晶
基于物理的液态玻璃UI效应为网. 斯内尔定律折射被烤成一幅SVG流离地图,分光相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相相接相接相接相接相接相接相接相相接相接相接相接相.
浏览器注释 : SVG迁移效应要求
backdrop-filter: url()这是 仅铬。所有其他浏览器都收到 CSSblur + saturate倒下,看起来仍然像被冰冻的玻璃.
安装
npm install @avenra/liquid-glass
<!-- CDN (production) -->
<script src="https://cdn.jsdelivr.net/npm/@avenra/liquid-glass/dist/liquid-glass.umd.min.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@avenra/liquid-glass/styles/liquid-glass.css"
/>
快开始
npm/ 捆绑器
import { createLiquidButton, createLiquidSwitch, createLiquidSlider } from '@avenra/liquid-glass';
import '@avenra/liquid-glass/styles';
const btn = createLiquidButton('#myBtn', { label: 'Save' });
btn.on('click', (e: MouseEvent) => console.log('clicked'));
const sw = createLiquidSwitch('#toggle', { checked: false });
sw.on('change', ({ checked }) => console.log('switch:', checked));
const sl = createLiquidSlider('#slider', { min: 0, max: 100, value: 50 });
sl.on('input', ({ value }) => updateVolume(value));
sl.on('change', ({ value }) => savePreference(value)); // fires on release
CDN / 脚本标签
<script src="https://cdn.jsdelivr.net/npm/@avenra/liquid-glass/dist/liquid-glass.umd.min.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@avenra/liquid-glass/styles/liquid-glass.css"
/>
<button id="btn"></button>
<script>
const { createLiquidButton } = LiquidGlass;
createLiquidButton('#btn', { label: 'Click me' }).on('click', () => alert('hello'));
</script>
0-JS — 数据属性
<button data-liquid-button data-label="Subscribe"></button>
<div data-liquid-switch data-checked="true"></div>
<div data-liquid-slider data-min="0" data-max="100" data-value="50"></div>
<div data-liquid-glass></div>
import { init } from '@avenra/liquid-glass';
const handles = init(); // scans document and applies everything
Theming — CSS 自定义属性
在你的风格表里覆盖这些
:root {
--lg-tint: rgba(255, 255, 255, 0.05); /* glass body fill */
--lg-border: rgba(255, 255, 255, 0.2); /* 1px edge border */
--lg-shadow-out: 0 10px 30px rgba(0, 0, 0, 0.3); /* drop shadow */
--lg-shadow-hi: inset 0 2px 5px rgba(255, 255, 255, 0.25); /* top highlight */
--lg-shadow-lo: inset 0 -2px 5px rgba(0, 0, 0, 0.15); /* bottom shade */
--lg-accent: #3b82f6; /* slider fill / progress fill gradient start */
--lg-accent-2: #60a5fa; /* gradient end */
}
API 参考
通用选项( 所有组件继承这些选项)
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
bezelWidth | number | 20 | Px 折射活性贝泽环宽度 |
glassThickness | number | 80 | 虚拟玻璃深度(较大 = 更平相射线漂移) |
refractiveIndex | number | 1.5 | 折射n2(1.0=无弯曲,2.0=强)指数 |
profile | 'convexSquircle' | 'convexCircle' | 'concave' | fn | 'convexSquircle' | Bezel 表面形状 |
blur | number | 0.5 | 迁移前的高斯文模糊度 |
saturation | number | 1.3 | 迁移后颜色饱和倍数 |
specularSlope | number | 0.8 | 光谱突出强度 0–1 |
filterMode | 'screen' | 'composite' | 'screen' | SVG 过滤器混合管 |
width 编号 : height | number | 自动 | 覆盖地图生成的元素维度 |
radius | number | null | 自动 | 角半径覆盖( null = 从 CSS 读取) |
常见的手柄 API( 每个工厂都返回)
handle.on(event, fn); // subscribe — returns handle (chainable)
handle.off(event, fn); // unsubscribe a specific listener
handle.once(event, fn); // subscribe once — auto-removed after first fire
handle.destroy(); // clean up all injected DOM and stop animations
所有活动包括: destroy 事件于 destroy() 名为.
createLiquidGlass(target, options?)
将玻璃效果应用到任何元素中.
const glass = createLiquidGlass('#myCard', {
bezelWidth: 24,
glassThickness: 120,
refractiveIndex: 2.0,
});
glass.on('resize', ({ width, height }) => console.log(width, height));
glass.refresh(); // rebuild maps (auto-called on resize)
glass.destroy();
| 活动 | 有效载荷 |
|---|---|
resize | { width: number; height: number } |
destroy | —— 说 |
createLiquidButton(target, options?)
const btn = createLiquidButton('#myBtn', { label: 'Save Changes' });
btn.on('click', (e: MouseEvent) => handleSave(e));
btn.setLabel('Saved ✓');
btn.destroy();
| 选项 | 类型 | 说明 |
|---|---|---|
label | string | 按钮文本 |
| 活动 | 有效载荷 |
|---|---|
click | MouseEvent |
mouseenter 编号 : mouseleave | MouseEvent |
mousedown 编号 : mouseup | MouseEvent |
destroy | —— 说 |
createLiquidSwitch(target, options?)
const sw = createLiquidSwitch('#toggle', { checked: true });
sw.on('change', ({ checked }) => applyTheme(checked));
sw.checked = false; // programmatic setter
sw.toggle();
sw.destroy();
| 选项 | 类型 | 默认 |
|---|---|---|
checked | boolean | false |
| 活动 | 有效载荷 |
|---|---|
change | { checked: boolean; element: Element } |
destroy | —— 说 |
createLiquidSlider(target, options?)
const sl = createLiquidSlider('#slider', { min: 0, max: 100, value: 50, step: 5 });
sl.on('input', ({ value }) => updateUI(value)); // while dragging
sl.on('change', ({ value }) => saveToServer(value)); // on release
sl.value = 75; // programmatic setter
sl.destroy();
| 选项 | 类型 | 默认 |
|---|---|---|
min 编号 : max | number | 0 编号 : 100 |
value | number | 50 |
step | number | 1 |
| 活动 | 有效载荷 |
|---|---|
input | { value: number; element: Element } |
change | { value: number; element: Element } |
destroy | —— 说 |
createLiquidCursor(container, options?)
取代内部系统光标的玻璃 Orb container。矿石生长在悬浮上,收缩在点击上.
const cursor = createLiquidCursor('#stage', { size: 90 });
cursor.destroy(); // restores the default cursor
| 选项 | 类型 | 默认 |
|---|---|---|
size | number | 90 |
createLiquidInput(target, options?)
玻璃包装 <input> 每个键盘上都有微振动,焦点上则有大小弹簧.
const inp = createLiquidInput('#searchBox', {
placeholder: 'Search…',
type: 'text',
});
inp.on('input', ({ value }) => runSearch(value));
inp.on('focus', () => showSuggestions());
inp.value = 'prefilled text'; // getter/setter
inp.focus();
inp.destroy();
| 选项 | 类型 | 默认 |
|---|---|---|
placeholder | string | '' |
value | string | '' |
type | string | 'text' |
| 活动 | 有效载荷 |
|---|---|
focus | FocusEvent |
blur | FocusEvent |
input | { value: string; element: Element } |
destroy | —— 说 |
createLiquidDial(target, options?)
扶轮把手-指针将它不断地拖转.
const dial = createLiquidDial('#knob', { value: 0 });
dial.on('change', ({ angle }) => setFilter(angle));
dial.angle = 90; // programmatic setter (degrees)
dial.destroy();
| 选项 | 类型 | 默认 |
|---|---|---|
value | number (分数) | 0 |
| 活动 | 有效载荷 |
|---|---|
change | { angle: number; element: Element } |
destroy | —— 说 |
createLiquidTooltip(target, options)
玻璃丸会弹出在悬浮/聚焦的触发元素上方.
// Assuming #myTooltip is positioned relative to its parent:
const tip = createLiquidTooltip('#myTooltip', {
trigger: '#triggerBtn',
text: 'More details',
});
tip.on('show', () => {});
tip.show(); // programmatic show
tip.hide(); // programmatic hide
tip.toggle();
tip.setText('Updated text');
tip.destroy();
| 选项 | 类型 | 需求 |
|---|---|---|
trigger | string | Element | ✓ |
text | string | —— 说 |
| 活动 | 有效载荷 |
|---|---|
show 编号 : hide | —— 说 |
destroy | —— 说 |
createLiquidProgress(target, options?)
Fluid 进度栏,在值更新时带有 squish 动画.
const bar = createLiquidProgress('#progressBar', { value: 0 });
bar.on('change', ({ value }) => console.log(value + '%'));
bar.value = 65; // triggers spring + squish
bar.destroy();
| 选项 | 类型 | 默认 |
|---|---|---|
value | number 0 - 100 个 | 0 |
| 活动 | 有效载荷 |
|---|---|
change | { value: number; element: Element } |
destroy | —— 说 |
init(options?)
扫描 DOM 为 data-liquid-* 属性,并自动应用匹配厂.
import { init } from '@avenra/liquid-glass';
const handles = init(); // whole document
const handles = init({ root: '#myApp' }); // limit to subtree
支持的数据属性 :
| 属性 | 通过 data-* |
|---|---|
data-liquid-button | data-label |
data-liquid-switch | data-checked |
data-liquid-slider | data-min, (中文(简体) ), data-max, (中文(简体) ), data-value, (中文(简体) ), data-step |
data-liquid-glass | data-bezel-width, (中文(简体) ), data-refractive-index, (中文(简体) ), data-profile等 (中文(简体) ). |
回返 AnyHandle[] 每个匹配元素一个手柄.
类型脚本
从包根导出所有类型 :
import type {
LiquidGlassHandle,
LiquidGlassOptions,
LiquidGlassEventMap,
LiquidButtonHandle,
LiquidButtonOptions,
LiquidButtonEventMap,
LiquidSwitchHandle,
LiquidSwitchOptions,
LiquidSwitchEventMap,
LiquidSliderHandle,
LiquidSliderOptions,
LiquidSliderEventMap,
LiquidCursorHandle,
LiquidCursorOptions,
LiquidInputHandle,
LiquidInputOptions,
LiquidInputEventMap,
LiquidDialHandle,
LiquidDialOptions,
LiquidDialEventMap,
LiquidTooltipHandle,
LiquidTooltipOptions,
LiquidTooltipEventMap,
LiquidProgressHandle,
LiquidProgressOptions,
LiquidProgressEventMap,
AnyHandle,
} from '@avenra/liquid-glass';
高级——低级API
使用原始物理引擎构建一个完全定制的玻璃组件:
import {
Spring,
PROFILES,
buildMaps,
createFilterSVG,
injectImages,
setScale,
nextFilterId,
supportsBackdropFilter,
} from '@avenra/liquid-glass';
const { dispUrl, specUrl, maxDisplacement } = buildMaps({
width: 200,
height: 140,
radius: 70,
bezelWidth: 30,
glassThickness: 150,
refractiveIndex: 1.5,
});
const filterId = nextFilterId('my-lens');
const { svg, refs } = createFilterSVG({ filterId, width: 200, height: 140 });
injectImages(refs, dispUrl, specUrl);
myElement.appendChild(svg);
if (supportsBackdropFilter()) {
innerEl.style.backdropFilter = `url("#${filterId}")`;
}
// Animation loop — spring the displacement scale
const spring = new Spring(maxDisplacement, 300, 20);
spring.setTarget(maxDisplacement * 1.5);
requestAnimationFrame(function loop() {
setScale(refs, spring.update(1 / 60));
if (!spring.isSettled()) requestAnimationFrame(loop);
});
浏览器兼容性
| 特性 | 颜色 | 边缘 | 火花 | 萨法里 |
|---|---|---|---|---|
| SVG 移动 + 光谱折射 | ✓ | ✓ | —— 说 | —— 说 |
| CSS 模糊/饱和倒置 | ✓ | ✓ | ✓ | ✓ |
从源头构建
npm install
npm run build # outputs dist/
npm test # build + run physics unit tests
许可证
麻省理工学院
支助
需要帮忙吗? 请看我们的 支助指南。 。 。 .
捐款
欢迎捐款! 请读我们的 贡献指南 财务报告和审定财务报表 行为守则 在提交拉动请求之前.
警卫
如果你发现安全隐患 请见我们 安全政策。 。 。 .