@longzai-intelligence-liquid-glass/webgl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # @longzai-intelligence-liquid-glass/webgl
2
+
3
+ Liquid Glass WebGL 渲染后端:完整 GLSL 管线 + BackdropSource 背景像素策略。
4
+
5
+ ## 用法
6
+
7
+ ```bash
8
+ bun run --cwd packages/webgl lint # 代码检查
9
+ bun run --cwd packages/webgl typecheck # 类型检查(tsgo)
10
+ bun run --cwd packages/webgl test # 单元测试
11
+ bun run --cwd packages/webgl build # 构建(lzi-builder → dist)
12
+ ```
@@ -0,0 +1,238 @@
1
+ import { BackdropSource, LiquidGlassParams, LiquidGlassRenderer, LiquidGlassSize, RendererCapability } from "@longzai-intelligence-liquid-glass/core";
2
+ //#region src/shaders.constants.d.ts
3
+ /**
4
+ * WebGL 着色器
5
+ *
6
+ * 片元主体融合 naughtyduk/liquidGL(udRoundBox + cornerNormal、
7
+ * pow(edge,N) bevel、frost 泊松采样、R/B 对称色散、双光斑 specular)
8
+ * 与 dashersw/liquid-glass-js(页面坐标采样、SDF mask),
9
+ * uniform 语义与 core 参数模型一一对应。
10
+ */
11
+ /**
12
+ * 顶点着色器:全屏 quad
13
+ */
14
+ declare const VERTEX_SHADER = "\nattribute vec2 aPosition;\nvarying vec2 vUv;\n\nvoid main() {\n vUv = aPosition * 0.5 + 0.5;\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}\n";
15
+ /**
16
+ * 片元着色器:液态玻璃管线
17
+ *
18
+ * uniform:
19
+ * - uBackdrop:背景纹理(页面坐标系)
20
+ * - uResolution:玻璃画布尺寸(px)
21
+ * - uHalfSize:形状半宽高(px)
22
+ * - uRadius:圆角半径(px)
23
+ * - uRefraction / uBand / uBevel / uBevelExp:折射参数
24
+ * - uDispersion:色散强度
25
+ * - uBlur / uFrost:模糊与霜化
26
+ * - uTint / uSaturation / uBrightness:着色
27
+ * - uSpecular / uSpecularExp:边缘高光
28
+ * - uTilt:倾斜交互(x,y)
29
+ * - uTime:动画时间(动态 specular 光斑)
30
+ */
31
+ declare const FRAGMENT_SHADER = "\nprecision highp float;\n\nvarying vec2 vUv;\n\nuniform sampler2D uBackdrop;\nuniform vec2 uResolution;\nuniform vec2 uHalfSize;\nuniform float uRadius;\nuniform float uRefraction;\nuniform float uBand;\nuniform float uBevel;\nuniform float uBevelExp;\nuniform float uDispersion;\nuniform float uBlur;\nuniform float uFrost;\nuniform vec4 uTint;\nuniform float uSaturation;\nuniform float uBrightness;\nuniform float uSpecular;\nuniform float uSpecularExp;\nuniform vec2 uTilt;\nuniform float uTime;\n\n/**\n * 圆角矩形 SDF(中心原点)\n */\nfloat sdRoundBox(vec2 p, vec2 b, float r) {\n vec2 q = abs(p) - b + r;\n float outside = length(max(q, 0.0));\n float inside = min(max(q.x, q.y), 0.0);\n return outside + inside - r;\n}\n\n/**\n * SDF 数值梯度 → 单位外法线\n */\nvec2 sdNormal(vec2 p, vec2 b, float r) {\n vec2 e = vec2(1.0, 0.0);\n float dx = sdRoundBox(p + e.xy, b, r) - sdRoundBox(p - e.xy, b, r);\n float dy = sdRoundBox(p + e.yx, b, r) - sdRoundBox(p - e.yx, b, r);\n return normalize(vec2(dx, dy) + 1e-6);\n}\n\n/**\n * 边缘因子:边界 1 → 带内 0\n */\nfloat edgeFactor(float sdf, float band) {\n return clamp(1.0 + sdf / max(band, 1e-4), 0.0, 1.0);\n}\n\n/**\n * frost 泊松圆盘模糊采样\n */\nvec3 frostSample(vec2 uv, float radius) {\n vec3 sum = vec3(0.0);\n float total = 0.0;\n const int TAPS = 12;\n for (int i = 0; i < TAPS; i++) {\n float fi = float(i);\n float angle = fi * 2.399963 + uTime * 0.05;\n float rad = sqrt((fi + 0.5) / float(TAPS)) * radius;\n vec2 offset = vec2(cos(angle), sin(angle)) * rad / uResolution;\n vec3 c = texture2D(uBackdrop, uv + offset).rgb;\n sum += c;\n total += 1.0;\n }\n return sum / total;\n}\n\nvoid main() {\n /**\n * 相对形状中心的像素坐标(含 tilt 平移)\n */\n vec2 p = (vUv - 0.5) * uResolution + uTilt * 20.0;\n\n /**\n * 符号距离与外法线\n */\n float sdf = sdRoundBox(p, uHalfSize, uRadius);\n vec2 normal = sdNormal(p, uHalfSize, uRadius);\n\n /**\n * 边缘因子与高度(弧面 + bevel 幂曲线)\n */\n float edge = edgeFactor(sdf, uBand);\n float height = uRefraction * edge + uBevel * pow(edge, uBevelExp);\n\n /**\n * 折射位移(指向内侧)\n */\n vec2 displacement = -normal * height * uBand;\n\n /**\n * 中心区保护:避免中心拉伸(liquidGL centreBlend 思路)\n */\n float centreBlend = smoothstep(0.15, 0.45, length(p / uHalfSize));\n displacement *= mix(1.0, 1.0, centreBlend);\n\n /**\n * 背景采样坐标(+ 位移)\n */\n vec2 uv = vUv + displacement / uResolution;\n\n /**\n * 色散:R/B 沿位移方向对称缩放(蓝端更大)\n */\n float rOffset = 1.0 - 0.5 * uDispersion;\n float bOffset = 1.0 + 0.7 * uDispersion;\n vec3 color;\n color.r = texture2D(uBackdrop, vUv + displacement * rOffset / uResolution).r;\n color.g = texture2D(uBackdrop, uv).g;\n color.b = texture2D(uBackdrop, vUv + displacement * bOffset / uResolution).b;\n\n /**\n * frost 模糊(与基础 blur 叠加)\n */\n float blurRadius = uBlur + uFrost * 14.0;\n if (blurRadius > 0.5) {\n vec3 blurred = frostSample(uv, blurRadius);\n color = mix(color, blurred, clamp(uFrost * 0.8 + step(0.5, uBlur) * 0.35, 0.0, 1.0));\n }\n\n /**\n * 饱和度与亮度\n */\n float luma = dot(color, vec3(0.299, 0.587, 0.114));\n color = mix(vec3(luma), color, uSaturation) * uBrightness;\n\n /**\n * tint 着色\n */\n color = mix(color, uTint.rgb, uTint.a);\n\n /**\n * 双光斑 rim specular:主光随时间轻移,对侧半强度镜像光\n */\n vec2 lightDir = normalize(vec2(cos(uTime * 0.3), sin(uTime * 0.3)) + vec2(0.6, -0.6));\n float rimMain = pow(clamp(dot(normal, lightDir), 0.0, 1.0), uSpecularExp);\n float rimMirror = pow(clamp(dot(normal, -lightDir), 0.0, 1.0), uSpecularExp) * 0.5;\n color += (rimMain + rimMirror) * uSpecular * edge;\n\n /**\n * SDF mask:形状外全透明(1.5px 抗锯齿过渡)\n */\n float mask = 1.0 - smoothstep(-1.5, 1.5, sdf);\n\n gl_FragColor = vec4(color, mask);\n}\n";
32
+ //#endregion
33
+ //#region src/backdrop-source.utils.d.ts
34
+ /**
35
+ * DOM 帧背景源(帧为可上传纹理的 canvas / image / video)
36
+ */
37
+ type DomBackdropSource = {
38
+ /**
39
+ * 帧是否变化(渲染循环决定是否重传纹理)
40
+ */
41
+ isDirty(): boolean;
42
+ } & BackdropSource<HTMLCanvasElement | HTMLImageElement | HTMLVideoElement>;
43
+ /**
44
+ * 创建 html2canvas 背景源:把指定元素区域快照为 canvas 帧
45
+ *
46
+ * @param target - 被快照的背景元素(通常为玻璃的父容器或 body)
47
+ * @returns DOM 背景源
48
+ */
49
+ declare function createHtml2CanvasSource(target: HTMLElement): DomBackdropSource;
50
+ /**
51
+ * 创建图片背景源
52
+ *
53
+ * @param image - 图片元素(跨域图片需允许 anonymous)
54
+ * @returns DOM 背景源
55
+ */
56
+ declare function createImageSource(image: HTMLImageElement): DomBackdropSource;
57
+ /**
58
+ * 创建视频背景源(播放中每帧均为脏帧)
59
+ *
60
+ * @param video - 视频元素
61
+ * @returns DOM 背景源
62
+ */
63
+ declare function createVideoSource(video: HTMLVideoElement): DomBackdropSource;
64
+ //#endregion
65
+ //#region src/webgl-renderer.utils.d.ts
66
+ /**
67
+ * 渲染上下文(构造入参)
68
+ */
69
+ type WebGLRendererContext = {
70
+ /**
71
+ * 初始参数
72
+ */
73
+ params: LiquidGlassParams;
74
+ /**
75
+ * 初始尺寸
76
+ */
77
+ size: LiquidGlassSize;
78
+ /**
79
+ * 背景源(缺省时渲染纯 tint 玻璃)
80
+ */
81
+ backdropSource?: DomBackdropSource;
82
+ };
83
+ /**
84
+ * WebGL 渲染器
85
+ */
86
+ declare class WebGLRenderer implements LiquidGlassRenderer<HTMLElement> {
87
+ /**
88
+ * 后端标识
89
+ */
90
+ readonly backend: 'webgl';
91
+ /**
92
+ * 宿主元素
93
+ */
94
+ private host;
95
+ /**
96
+ * 渲染 canvas
97
+ */
98
+ private canvas;
99
+ /**
100
+ * WebGL 上下文
101
+ */
102
+ private gl;
103
+ /**
104
+ * 着色器程序
105
+ */
106
+ private program;
107
+ /**
108
+ * 背景纹理
109
+ */
110
+ private texture;
111
+ /**
112
+ * uniform 位置表
113
+ */
114
+ private uniforms;
115
+ /**
116
+ * 当前参数
117
+ */
118
+ private params;
119
+ /**
120
+ * 当前尺寸
121
+ */
122
+ private size;
123
+ /**
124
+ * 背景源
125
+ */
126
+ private source;
127
+ /**
128
+ * 捕获进行中标记(避免并发捕获)
129
+ */
130
+ private capturing;
131
+ /**
132
+ * 渲染循环 id
133
+ */
134
+ private rafId;
135
+ /**
136
+ * 启动时间(动画 specular 用)
137
+ */
138
+ private startTime;
139
+ /**
140
+ * 构造渲染器
141
+ *
142
+ * @param context - 渲染上下文
143
+ */
144
+ constructor(context: WebGLRendererContext);
145
+ /**
146
+ * 挂载到 DOM 元素
147
+ *
148
+ * @param target - 宿主元素
149
+ */
150
+ mount(target: HTMLElement): void;
151
+ /**
152
+ * 热更新参数
153
+ *
154
+ * @param params - 完整参数
155
+ */
156
+ update(params: LiquidGlassParams): void;
157
+ /**
158
+ * 尺寸变化
159
+ *
160
+ * @param size - 新尺寸
161
+ */
162
+ resize(size: LiquidGlassSize): void;
163
+ /**
164
+ * 更换背景源(嵌套玻璃可注入父级 canvas 源)
165
+ *
166
+ * @param source - DOM 背景源
167
+ */
168
+ setBackdropSource(source: DomBackdropSource): void;
169
+ /**
170
+ * 获取能力描述
171
+ *
172
+ * @returns 能力描述
173
+ */
174
+ getCapabilities(): RendererCapability;
175
+ /**
176
+ * 卸载并释放资源(幂等)
177
+ */
178
+ unmount(): void;
179
+ /**
180
+ * 编译着色器程序
181
+ *
182
+ * @param gl - WebGL 上下文
183
+ * @returns 程序;编译失败返回 null
184
+ */
185
+ private buildProgram;
186
+ /**
187
+ * 应用尺寸到 canvas 与 viewport
188
+ */
189
+ private applySize;
190
+ /**
191
+ * 上传背景帧到纹理
192
+ *
193
+ * @param frame - canvas / image / video 帧
194
+ */
195
+ private uploadFrame;
196
+ /**
197
+ * 捕获背景(异步源安全)
198
+ */
199
+ private captureBackdrop;
200
+ /**
201
+ * 启动渲染循环(背景脏时重新捕获,每帧绘制)
202
+ */
203
+ private startLoop;
204
+ /**
205
+ * 上传 uniform 并绘制一帧
206
+ */
207
+ private draw;
208
+ }
209
+ //#endregion
210
+ //#region src/support.utils.d.ts
211
+ /**
212
+ * WebGL 路线支持检测
213
+ */
214
+ /**
215
+ * 支持检测结果
216
+ */
217
+ type WebGLSupport = {
218
+ /**
219
+ * 是否支持 WebGL 路线
220
+ */
221
+ supported: boolean;
222
+ /**
223
+ * 上下文类型(webgl2 / webgl;不支持为空)
224
+ */
225
+ contextType: 'webgl2' | 'webgl' | '';
226
+ /**
227
+ * 不支持原因(支持时为空)
228
+ */
229
+ reason: string;
230
+ };
231
+ /**
232
+ * 检测当前环境对 WebGL 的支持
233
+ *
234
+ * @returns 支持检测结果
235
+ */
236
+ declare function detectWebGLSupport(): WebGLSupport;
237
+ //#endregion
238
+ export { type DomBackdropSource, FRAGMENT_SHADER, VERTEX_SHADER, WebGLRenderer, type WebGLRendererContext, type WebGLSupport, createHtml2CanvasSource, createImageSource, createVideoSource, detectWebGLSupport };
package/dist/index.js ADDED
@@ -0,0 +1,158 @@
1
+ const e=`
2
+ attribute vec2 aPosition;
3
+ varying vec2 vUv;
4
+
5
+ void main() {
6
+ vUv = aPosition * 0.5 + 0.5;
7
+ gl_Position = vec4(aPosition, 0.0, 1.0);
8
+ }
9
+ `,t=`
10
+ precision highp float;
11
+
12
+ varying vec2 vUv;
13
+
14
+ uniform sampler2D uBackdrop;
15
+ uniform vec2 uResolution;
16
+ uniform vec2 uHalfSize;
17
+ uniform float uRadius;
18
+ uniform float uRefraction;
19
+ uniform float uBand;
20
+ uniform float uBevel;
21
+ uniform float uBevelExp;
22
+ uniform float uDispersion;
23
+ uniform float uBlur;
24
+ uniform float uFrost;
25
+ uniform vec4 uTint;
26
+ uniform float uSaturation;
27
+ uniform float uBrightness;
28
+ uniform float uSpecular;
29
+ uniform float uSpecularExp;
30
+ uniform vec2 uTilt;
31
+ uniform float uTime;
32
+
33
+ /**
34
+ * 圆角矩形 SDF(中心原点)
35
+ */
36
+ float sdRoundBox(vec2 p, vec2 b, float r) {
37
+ vec2 q = abs(p) - b + r;
38
+ float outside = length(max(q, 0.0));
39
+ float inside = min(max(q.x, q.y), 0.0);
40
+ return outside + inside - r;
41
+ }
42
+
43
+ /**
44
+ * SDF 数值梯度 → 单位外法线
45
+ */
46
+ vec2 sdNormal(vec2 p, vec2 b, float r) {
47
+ vec2 e = vec2(1.0, 0.0);
48
+ float dx = sdRoundBox(p + e.xy, b, r) - sdRoundBox(p - e.xy, b, r);
49
+ float dy = sdRoundBox(p + e.yx, b, r) - sdRoundBox(p - e.yx, b, r);
50
+ return normalize(vec2(dx, dy) + 1e-6);
51
+ }
52
+
53
+ /**
54
+ * 边缘因子:边界 1 → 带内 0
55
+ */
56
+ float edgeFactor(float sdf, float band) {
57
+ return clamp(1.0 + sdf / max(band, 1e-4), 0.0, 1.0);
58
+ }
59
+
60
+ /**
61
+ * frost 泊松圆盘模糊采样
62
+ */
63
+ vec3 frostSample(vec2 uv, float radius) {
64
+ vec3 sum = vec3(0.0);
65
+ float total = 0.0;
66
+ const int TAPS = 12;
67
+ for (int i = 0; i < TAPS; i++) {
68
+ float fi = float(i);
69
+ float angle = fi * 2.399963 + uTime * 0.05;
70
+ float rad = sqrt((fi + 0.5) / float(TAPS)) * radius;
71
+ vec2 offset = vec2(cos(angle), sin(angle)) * rad / uResolution;
72
+ vec3 c = texture2D(uBackdrop, uv + offset).rgb;
73
+ sum += c;
74
+ total += 1.0;
75
+ }
76
+ return sum / total;
77
+ }
78
+
79
+ void main() {
80
+ /**
81
+ * 相对形状中心的像素坐标(含 tilt 平移)
82
+ */
83
+ vec2 p = (vUv - 0.5) * uResolution + uTilt * 20.0;
84
+
85
+ /**
86
+ * 符号距离与外法线
87
+ */
88
+ float sdf = sdRoundBox(p, uHalfSize, uRadius);
89
+ vec2 normal = sdNormal(p, uHalfSize, uRadius);
90
+
91
+ /**
92
+ * 边缘因子与高度(弧面 + bevel 幂曲线)
93
+ */
94
+ float edge = edgeFactor(sdf, uBand);
95
+ float height = uRefraction * edge + uBevel * pow(edge, uBevelExp);
96
+
97
+ /**
98
+ * 折射位移(指向内侧)
99
+ */
100
+ vec2 displacement = -normal * height * uBand;
101
+
102
+ /**
103
+ * 中心区保护:避免中心拉伸(liquidGL centreBlend 思路)
104
+ */
105
+ float centreBlend = smoothstep(0.15, 0.45, length(p / uHalfSize));
106
+ displacement *= mix(1.0, 1.0, centreBlend);
107
+
108
+ /**
109
+ * 背景采样坐标(+ 位移)
110
+ */
111
+ vec2 uv = vUv + displacement / uResolution;
112
+
113
+ /**
114
+ * 色散:R/B 沿位移方向对称缩放(蓝端更大)
115
+ */
116
+ float rOffset = 1.0 - 0.5 * uDispersion;
117
+ float bOffset = 1.0 + 0.7 * uDispersion;
118
+ vec3 color;
119
+ color.r = texture2D(uBackdrop, vUv + displacement * rOffset / uResolution).r;
120
+ color.g = texture2D(uBackdrop, uv).g;
121
+ color.b = texture2D(uBackdrop, vUv + displacement * bOffset / uResolution).b;
122
+
123
+ /**
124
+ * frost 模糊(与基础 blur 叠加)
125
+ */
126
+ float blurRadius = uBlur + uFrost * 14.0;
127
+ if (blurRadius > 0.5) {
128
+ vec3 blurred = frostSample(uv, blurRadius);
129
+ color = mix(color, blurred, clamp(uFrost * 0.8 + step(0.5, uBlur) * 0.35, 0.0, 1.0));
130
+ }
131
+
132
+ /**
133
+ * 饱和度与亮度
134
+ */
135
+ float luma = dot(color, vec3(0.299, 0.587, 0.114));
136
+ color = mix(vec3(luma), color, uSaturation) * uBrightness;
137
+
138
+ /**
139
+ * tint 着色
140
+ */
141
+ color = mix(color, uTint.rgb, uTint.a);
142
+
143
+ /**
144
+ * 双光斑 rim specular:主光随时间轻移,对侧半强度镜像光
145
+ */
146
+ vec2 lightDir = normalize(vec2(cos(uTime * 0.3), sin(uTime * 0.3)) + vec2(0.6, -0.6));
147
+ float rimMain = pow(clamp(dot(normal, lightDir), 0.0, 1.0), uSpecularExp);
148
+ float rimMirror = pow(clamp(dot(normal, -lightDir), 0.0, 1.0), uSpecularExp) * 0.5;
149
+ color += (rimMain + rimMirror) * uSpecular * edge;
150
+
151
+ /**
152
+ * SDF mask:形状外全透明(1.5px 抗锯齿过渡)
153
+ */
154
+ float mask = 1.0 - smoothstep(-1.5, 1.5, sdf);
155
+
156
+ gl_FragColor = vec4(color, mask);
157
+ }
158
+ `;var n=class{backend=`webgl`;host=null;canvas=null;gl=null;program=null;texture=null;uniforms={};params;size;source=null;capturing=!1;rafId=0;startTime=0;constructor(e){this.params=e.params,this.size=e.size,this.source=e.backdropSource??null}mount(e){this.host=e;let t=document.createElement(`canvas`);t.style.position=`absolute`,t.style.inset=`0`,t.style.width=`100%`,t.style.height=`100%`,t.style.pointerEvents=`none`,t.style.borderRadius=this.params.shape===`circle`?`50%`:`${this.params.cornerRadius}px`,getComputedStyle(e).position===`static`&&(e.style.position=`relative`),e.appendChild(t),this.canvas=t;let n=t.getContext(`webgl2`,{alpha:!0,premultipliedAlpha:!1}),r=n===null?t.getContext(`webgl`,{alpha:!0,premultipliedAlpha:!1}):null,i=n??r;if(i===null||(this.gl=i,this.program=this.buildProgram(i),this.program===null))return;let a=i.createBuffer();i.bindBuffer(i.ARRAY_BUFFER,a),i.bufferData(i.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),i.STATIC_DRAW);let o=i.getAttribLocation(this.program,`aPosition`);i.enableVertexAttribArray(o),i.vertexAttribPointer(o,2,i.FLOAT,!1,0,0),this.texture=i.createTexture(),i.bindTexture(i.TEXTURE_2D,this.texture),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR);for(let e of[`uBackdrop`,`uResolution`,`uHalfSize`,`uRadius`,`uRefraction`,`uBand`,`uBevel`,`uBevelExp`,`uDispersion`,`uBlur`,`uFrost`,`uTint`,`uSaturation`,`uBrightness`,`uSpecular`,`uSpecularExp`,`uTilt`,`uTime`])this.uniforms[e]=i.getUniformLocation(this.program,e);i.useProgram(this.program),i.enable(i.BLEND),i.blendFunc(i.SRC_ALPHA,i.ONE_MINUS_SRC_ALPHA),this.applySize(),this.startTime=performance.now(),this.startLoop()}update(e){this.params=e,this.canvas!==null&&(this.canvas.style.borderRadius=e.shape===`circle`?`50%`:`${e.cornerRadius}px`)}resize(e){this.size=e,this.applySize()}setBackdropSource(e){this.source=e}getCapabilities(){return{backend:`webgl`,supportsRefraction:!0,supportsDispersion:!0,supportsBlur:!0,supportsSpecular:!0,supportsDomBackdrop:!1}}unmount(){cancelAnimationFrame(this.rafId),this.gl!==null&&this.texture!==null&&this.gl.deleteTexture(this.texture),this.source!==null&&this.source.dispose(),this.canvas!==null&&this.canvas.remove(),this.host=null,this.canvas=null,this.gl=null,this.program=null,this.texture=null,this.source=null}buildProgram(n){let r=(e,t)=>{let r=n.createShader(e);return r===null||(n.shaderSource(r,t),n.compileShader(r),!n.getShaderParameter(r,n.COMPILE_STATUS))?null:r},i=r(n.VERTEX_SHADER,e),a=r(n.FRAGMENT_SHADER,t);if(i===null||a===null)return null;let o=n.createProgram();return o===null||(n.attachShader(o,i),n.attachShader(o,a),n.linkProgram(o),!n.getProgramParameter(o,n.LINK_STATUS))?null:o}applySize(){if(this.canvas===null||this.gl===null)return;let e=window.devicePixelRatio>0?window.devicePixelRatio:1;this.canvas.width=Math.max(2,Math.round(this.size.width*e)),this.canvas.height=Math.max(2,Math.round(this.size.height*e)),this.gl.viewport(0,0,this.canvas.width,this.canvas.height)}uploadFrame(e){this.gl!==null&&this.texture!==null&&(this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,e))}async captureBackdrop(){if(!(this.source===null||this.capturing)){this.capturing=!0;try{let e=await this.source.capture();this.uploadFrame(e)}finally{this.capturing=!1}}}startLoop(){let e=()=>{this.source!==null&&this.source.isDirty()&&this.captureBackdrop(),this.draw(),this.rafId=requestAnimationFrame(e)};this.captureBackdrop(),this.rafId=requestAnimationFrame(e)}draw(){if(this.gl===null||this.program===null)return;let e=this.gl,t=this.params;e.useProgram(this.program);let n=t.shape===`circle`?Math.min(t.width,t.height)/2:t.width/2,r=t.shape===`circle`?Math.min(t.width,t.height)/2:t.height/2,i=t.shape===`circle`?Math.min(n,r):Math.min(t.cornerRadius,Math.min(n,r)-.5);e.uniform2f(this.uniforms.uResolution??null,this.size.width,this.size.height),e.uniform2f(this.uniforms.uHalfSize??null,n,r),e.uniform1f(this.uniforms.uRadius??null,i),e.uniform1f(this.uniforms.uRefraction??null,t.refraction),e.uniform1f(this.uniforms.uBand??null,t.refractionHeight),e.uniform1f(this.uniforms.uBevel??null,t.bevel),e.uniform1f(this.uniforms.uBevelExp??null,t.bevelExponent),e.uniform1f(this.uniforms.uDispersion??null,t.dispersion),e.uniform1f(this.uniforms.uBlur??null,t.blur),e.uniform1f(this.uniforms.uFrost??null,t.frost),e.uniform4f(this.uniforms.uTint??null,t.tint.r,t.tint.g,t.tint.b,t.tint.a),e.uniform1f(this.uniforms.uSaturation??null,t.saturation),e.uniform1f(this.uniforms.uBrightness??null,t.brightness),e.uniform1f(this.uniforms.uSpecular??null,t.specular),e.uniform1f(this.uniforms.uSpecularExp??null,t.specularExponent),e.uniform2f(this.uniforms.uTilt??null,0,0),e.uniform1f(this.uniforms.uTime??null,(performance.now()-this.startTime)/1e3),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT),e.drawArrays(e.TRIANGLE_STRIP,0,4)}};async function r(){try{let e=(await import(`html2canvas`)).default;return typeof e==`function`?e:null}catch{return null}}function i(e){let t=null,n=!1;return{id:`html2canvas`,async capture(){let i=await r();return i===null?document.createElement(`canvas`):(t=await i(e,{backgroundColor:null,scale:Math.min(window.devicePixelRatio>0?window.devicePixelRatio:1,2),logging:!1}),n=!0,t)},isDirty(){return n?(n=!1,!0):!1},dispose(){t=null}}}function a(e){let t=!0;return{id:`image`,capture(){return e},isDirty(){return t?(t=!1,!0):e.complete&&e.naturalWidth>0&&t},dispose(){}}}function o(e){return{id:`video`,capture(){return e},isDirty(){return!e.paused&&!e.ended&&e.readyState>=2},dispose(){e.pause()}}}function s(){if(typeof document>`u`)return{supported:!1,contextType:``,reason:`SSR 或非浏览器环境`};let e=document.createElement(`canvas`);return e.getContext(`webgl2`)===null?e.getContext(`webgl`)===null?{supported:!1,contextType:``,reason:`WebGL 上下文不可用`}:{supported:!0,contextType:`webgl`,reason:``}:{supported:!0,contextType:`webgl2`,reason:``}}export{t as FRAGMENT_SHADER,e as VERTEX_SHADER,n as WebGLRenderer,i as createHtml2CanvasSource,a as createImageSource,o as createVideoSource,s as detectWebGLSupport};
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@longzai-intelligence-liquid-glass/webgl",
3
+ "version": "0.0.1",
4
+ "description": "Liquid Glass WebGL 渲染后端(BackdropSource 策略)",
5
+ "license": "UNLICENSED",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "type": "module",
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "lzi-builder",
20
+ "build:prod": "NODE_ENV=production bun run build",
21
+ "prepublishOnly": "bun run build:prod",
22
+ "typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
23
+ "typecheck:app": "lzi-tsgo typecheck tsconfig/app.json",
24
+ "typecheck:node": "lzi-tsgo typecheck tsconfig/node.json",
25
+ "typecheck:test": "lzi-tsgo typecheck tsconfig/test.json",
26
+ "lint": "oxlint && oxfmt --check",
27
+ "lint:fix": "oxlint --fix && oxfmt",
28
+ "test": "lzi-bun-cli test",
29
+ "test:watch": "lzi-bun-cli test --watch",
30
+ "test:coverage": "lzi-bun-cli test --coverage",
31
+ "clean": "lzi-dev-cli clean"
32
+ },
33
+ "dependencies": {
34
+ "@longzai-intelligence-liquid-glass/core": "0.0.1",
35
+ "html2canvas": "^1.4.1"
36
+ },
37
+ "packageManager": "bun@1.3.14"
38
+ }