liquid-gl 2.1.1 → 2.2.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 +45 -24
- package/liquidGL-helper.js +362 -0
- package/liquidGL.js +1260 -787
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# liquidGL – Liquid Glass - Powered by WebGL
|
|
1
|
+
# liquidGL – Liquid Glass - Powered by WebGPU/WebGL
|
|
2
2
|
|
|
3
3
|
<a href="https://liquidgl.naughtyduk.com"><img src="https://raw.githubusercontent.com/naughtyduk/liquidGL/main/assets/liquidGL-npm-preview.gif" alt="liquidGL" width="100%" height="auto"/></a>
|
|
4
4
|
|
|
5
|
-
### v2.
|
|
5
|
+
### v2.2.1
|
|
6
6
|
|
|
7
7
|
> [!NOTE]
|
|
8
8
|
> `liquidGL` is free to use for both non-commercial and commercial purposes.
|
|
@@ -16,10 +16,8 @@
|
|
|
16
16
|
> | `html2canvas` | 86.3 ms | 85.3 ms | 134.8 ms | 16.2 ms |
|
|
17
17
|
>
|
|
18
18
|
> Measured over 7 alternating runs of the full home page at `resolution: 2` (2880×10036 output), Chrome 150.
|
|
19
|
-
>
|
|
20
|
-
> **v2.0.0 changed tilt behaviour.** The tilt interaction now eases symmetrically over the new `tiltEase` option (default `400`ms) in both directions, replacing the previous hard-coded `0.12s` ease-in and `0.4s` ease-out. If you depended on the old timing, set `tiltEase` explicitly. All other defaults are unchanged and existing configurations render identically.
|
|
21
19
|
|
|
22
|
-
`liquidGL` turns any fixed or sticky-positioned element into a perfectly refracted, glossy "glass pane" rendered in WebGL.
|
|
20
|
+
`liquidGL` turns any fixed or sticky-positioned element into a perfectly refracted, glossy "glass pane" rendered in WebGPU (with automatic fallback to WebGL).
|
|
23
21
|
|
|
24
22
|
<a href="https://liquidgl.naughtyduk.com" target="_blank" rel="noopener noreferrer"><img src="https://raw.githubusercontent.com/naughtyduk/liquidGL/main/assets/try-it-out-npm.png" alt="Try It Out" width="120"></a>
|
|
25
23
|
|
|
@@ -31,6 +29,10 @@
|
|
|
31
29
|
|
|
32
30
|
**Features**
|
|
33
31
|
|
|
32
|
+
- **2.2.0 - WebGPU rendering** — `liquidGL` now renders with WebGPU where available, with an automatic fallback chain of WebGPU → WebGL2 → WebGL1 → CSS `backdrop-filter`. Nothing to configure, the chain is fully automatic. Choose where the chain starts with the new `engine` option (`'auto'`, `'webgpu'`, `'webgl2'`, `'webgl'`), or test quickly via the URL parameter `?liquidGL-engine=webgl2`.
|
|
33
|
+
|
|
34
|
+
- **2.2.0 - Helper GUI is now a separate file** — the dev GUI no longer ships in the main bundle. Import `liquid-gl/liquidGL-helper.js` in development to keep using `helper: true`. If the module is missing you'll get a console error and everything else works as normal.
|
|
35
|
+
|
|
34
36
|
- **2.1.0 - Helper GUI** — A helper GUI is now available to adjust the liquidGL options in real-time during development, helping you to achieve the perfect aesthetic. To enable it, set `helper: true` in the options object when calling `liquidGL()`.
|
|
35
37
|
|
|
36
38
|
- **Sticky positioning support** — `position: sticky` elements can now be glassified. Sticky lenses are measured every animation frame, so the glass pane tracks the element through its flowing and stuck phases, then releases with it at the end of its containing block — inside nested scroll containers as well as the main document.
|
|
@@ -78,18 +80,29 @@ const glassEffect = liquidGL({
|
|
|
78
80
|
});
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
Optional dev helper GUI (only needed when using `helper: true`):
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
// Vite / modern bundlers – tree-shaken out of production builds
|
|
87
|
+
if (import.meta.env.DEV) await import("liquid-gl/liquidGL-helper.js");
|
|
88
|
+
|
|
89
|
+
// or webpack flavour:
|
|
90
|
+
// if (process.env.NODE_ENV !== "production") await import("liquid-gl/liquidGL-helper.js");
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
> `liquidGL` renders with WebGPU (falling back automatically to WebGL2 → WebGL1) and snapshots the page with its own built-in rasteriser, so it has no runtime dependencies.
|
|
82
94
|
|
|
83
95
|
---
|
|
84
96
|
|
|
85
97
|
## Overview
|
|
86
98
|
|
|
87
|
-
`liquidGL` recreates Apple's "Liquid Glass" aesthetic in the browser with an ultra-light WebGL shader. It turns any DOM element into a beautiful, refracting glass pane. To overcome WebGL's security limitations on reading live screen pixels, `liquidGL` uses an innovative offscreen rendering technique. This allows it to refract dynamic content like videos, text animations, and more in real-time, delivering a smooth and interactive experience.
|
|
99
|
+
`liquidGL` recreates Apple's "Liquid Glass" aesthetic in the browser with an ultra-light WebGPU/WebGL shader. It turns any DOM element into a beautiful, refracting glass pane. To overcome WebGL's security limitations on reading live screen pixels, `liquidGL` uses an innovative offscreen rendering technique. This allows it to refract dynamic content like videos, text animations, and more in real-time, delivering a smooth and interactive experience.
|
|
88
100
|
|
|
89
101
|
### Key Features
|
|
90
102
|
|
|
91
103
|
| Feature | Supported | Feature | Supported |
|
|
92
104
|
| :------------------------------------- | :-------: | :------------------------------- | :-------: |
|
|
105
|
+
| WebGPU Rendering `[NEW]` | ✅ | Helper GUI `[NEW]` | ✅ |
|
|
93
106
|
| Real-time Refraction (static content) | ✅ | Magnification Control | ✅ |
|
|
94
107
|
| Real-time Refraction (video) | ✅ | Dynamic Element Support | ✅ |
|
|
95
108
|
| Real-time Refraction (text animations) | ✅ | GSAP-Ready Animations | ✅ |
|
|
@@ -126,23 +139,30 @@ Next, initialise the library with the selector for your target element.
|
|
|
126
139
|
import liquidGL from "liquid-gl";
|
|
127
140
|
|
|
128
141
|
const glassEffect = liquidGL({
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
engine: "auto", // Renderer chain: "auto" tries WebGPU then WebGL; force with "webgpu", "webgl2" or "webgl"
|
|
143
|
+
snapshot: "body", // The area used for refraction, <body> recommended and default
|
|
144
|
+
target: ".liquidGL", // CSS selector for the element(s) to glass-ify
|
|
145
|
+
resolution: 2.0, // The quality of the snapshot
|
|
146
|
+
refraction: 0.01, // Base refraction strength (0–1)
|
|
147
|
+
aberration: 0, // Chromatic aberration strength (0–1). 0 = off
|
|
148
|
+
bevelDepth: 0.08, // Intensity of the edge bevel (0–1)
|
|
149
|
+
bevelWidth: 0.15, // Width of the bevel as a proportion of the element (0–1)
|
|
150
|
+
frost: 0, // Subtle blur radius in px. 0 = crystal clear
|
|
151
|
+
shadow: true, // Adds a soft drop-shadow under the pane
|
|
152
|
+
specular: true, // Animated light highlights (slightly more GPU)
|
|
153
|
+
reveal: "fade", // Reveal animation
|
|
154
|
+
tilt: false, // Whether tilt on hover is enabled
|
|
155
|
+
tiltFactor: 5, // If tilt is enabled, how much tilt
|
|
156
|
+
tiltEase: 400, // Tilt settle duration in ms, on hover in and out
|
|
157
|
+
magnify: 1, // Magnification of lens content
|
|
158
|
+
helper: false, // Show debug helper - note requires liquidGL-helper.js module
|
|
144
159
|
on: {
|
|
145
160
|
init(instance) {
|
|
161
|
+
// The `init` callback fires once liquidGL has taken its snapshot
|
|
162
|
+
// and rendered the first frame. It's the ideal place to hide or
|
|
163
|
+
// prepare elements for reveal animations (e.g. with GSAP, ScrollTrigger)
|
|
164
|
+
// because it ensures the content is visible to the snapshot before
|
|
165
|
+
// you hide it from the user.
|
|
146
166
|
console.log("liquidGL ready!", instance);
|
|
147
167
|
},
|
|
148
168
|
},
|
|
@@ -217,6 +237,7 @@ Passing `gsap` lets `liquidGL` drive its render loop from the GSAP ticker and ke
|
|
|
217
237
|
| `target` | string | `'.liquidGL'` | **Required.** CSS selector for the element(s) to glassify. |
|
|
218
238
|
| `snapshot` | string | `'body'` | CSS selector for the element to snapshot. |
|
|
219
239
|
| `resolution` | number | `2.0` | Resolution of the background snapshot (clamped 0.1–3.0). Higher is sharper but uses more memory. |
|
|
240
|
+
| `engine` | string | `'auto'` | Render backend chain: `'auto'` (WebGPU → WebGL2 → WebGL1 → CSS), `'webgpu'` (WebGPU → CSS), `'webgl2'` (WebGL2 → WebGL1 → CSS) or `'webgl'` (WebGL1 → CSS). |
|
|
220
241
|
| `refraction` | number | `0.01` | Base refraction offset applied across the pane (0–1). |
|
|
221
242
|
| `aberration` | number | `0` | Chromatic aberration strength (0–1). Scales with the refraction offset, so dispersion is strongest at the bevel. `0` disables it and skips the extra texture samples. |
|
|
222
243
|
| `bevelDepth` | number | `0.08` | Additional refraction on the edge to simulate depth (0–1). |
|
|
@@ -229,7 +250,7 @@ Passing `gsap` lets `liquidGL` drive its render loop from the GSAP ticker and ke
|
|
|
229
250
|
| `tiltFactor` | number | `5` | Depth of the tilt in degrees (0–25 recommended). |
|
|
230
251
|
| `tiltEase` | number | `400` | Duration in ms for the tilt to settle, applied symmetrically on hover-in and hover-out. `0` applies the tilt instantly. |
|
|
231
252
|
| `magnify` | number | `1` | Magnification factor of the lens (clamped 0.001–3.0). `1` is no magnification. |
|
|
232
|
-
| `helper` | boolean | `false` | Loads the helper GUI for live tweaking of the liquidGL options.
|
|
253
|
+
| `helper` | boolean | `false` | Loads the helper GUI for live tweaking of the liquidGL options. Requires `liquid-gl/liquidGL-helper.js` to be imported; logs a console error if missing. |
|
|
233
254
|
| `on.init` | function | `—` | Callback that runs once the first render completes. Receives the lens instance. |
|
|
234
255
|
|
|
235
256
|
> The `target` parameter is required; all others are optional.
|
|
@@ -253,7 +274,7 @@ Passing `gsap` lets `liquidGL` drive its render loop from the GSAP ticker and ke
|
|
|
253
274
|
| Question | Answer |
|
|
254
275
|
| :----------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
255
276
|
| Is there a resize handler? | Yes resize is handled in the library and debounced to 250ms for performance. |
|
|
256
|
-
| Does the effect work on mobile? | Yes the library
|
|
277
|
+
| Does the effect work on mobile? | Yes the library uses WebGPU where available, falls back through WebGL2 and WebGL1, and provides a frosted CSS `backdrop-filter` as a backup for older devices. |
|
|
257
278
|
| I have a preloader, how should I initialise `liquidGL()`? | Add the `data-liquid-ignore` attribute to your preloader's top-level container to exclude it from the snapshot. You can then call `liquidGL()` inside a `DOMContentLoaded` listener as you normally would. |
|
|
258
279
|
| What is the correct way to use `liquidGL` with page animations? | Lets say you have a preloader, above the fold intro animations and scroll animations on your page. You would:<br><br>1) set the `data-liquid-ignore` attribute on your preloader<br>2) animate your preloader and set up your initial animation states<br>3) then call `liquidGL();`<br>4) optionally, in the `on.init();` callback, you can run post snapshot scripts, such as animating the `target` element |
|
|
259
280
|
| Can I use `liquidGL` on multiple elements? | Yes, any element which has the class declared as your `target` will be glassified. Note **all elements must use the same `z-index`** due to shared canvas optimisations, if you use different `z-index` values for multiple targets, the highest value will be used by `liquidGL`. |
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* liquidGL Helper – Dev/debug GUI for liquidGL
|
|
3
|
+
* -----------------------------------------------------------------------------
|
|
4
|
+
*
|
|
5
|
+
* Author: NaughtyDuk© – https://liquidgl.naughtyduk.com
|
|
6
|
+
* Licence: MIT
|
|
7
|
+
* Version: v2.2.1
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
(() => {
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
/* --------------------------------------------------
|
|
14
|
+
* Helper GUI System
|
|
15
|
+
* ------------------------------------------------*/
|
|
16
|
+
let helperGUIs = [];
|
|
17
|
+
let lilGuiLoaded = false;
|
|
18
|
+
let lilGuiLoadPromise = null;
|
|
19
|
+
|
|
20
|
+
function loadLilGui() {
|
|
21
|
+
if (lilGuiLoaded) {
|
|
22
|
+
return Promise.resolve();
|
|
23
|
+
}
|
|
24
|
+
if (lilGuiLoadPromise) {
|
|
25
|
+
return lilGuiLoadPromise;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
lilGuiLoadPromise = new Promise((resolve, reject) => {
|
|
29
|
+
if (typeof lil !== "undefined") {
|
|
30
|
+
lilGuiLoaded = true;
|
|
31
|
+
injectHelperStyles();
|
|
32
|
+
resolve();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const script = document.createElement("script");
|
|
37
|
+
script.src =
|
|
38
|
+
"https://cdn.jsdelivr.net/npm/lil-gui@0.19.1/dist/lil-gui.umd.min.js";
|
|
39
|
+
script.integrity =
|
|
40
|
+
"sha384-2eNPNc7Cms+nVcpmQPotBpthLWCwjAGbkp0Y+3MUQqwPbmTpMFmbh2a230Gkns0x";
|
|
41
|
+
script.crossOrigin = "anonymous";
|
|
42
|
+
script.onload = () => {
|
|
43
|
+
lilGuiLoaded = true;
|
|
44
|
+
injectHelperStyles();
|
|
45
|
+
resolve();
|
|
46
|
+
};
|
|
47
|
+
script.onerror = () => {
|
|
48
|
+
reject(new Error("Failed to load lil-gui"));
|
|
49
|
+
};
|
|
50
|
+
document.head.appendChild(script);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return lilGuiLoadPromise;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function injectHelperStyles() {
|
|
57
|
+
if (document.getElementById("liquidgl-helper-styles")) return;
|
|
58
|
+
|
|
59
|
+
const style = document.createElement("style");
|
|
60
|
+
style.id = "liquidgl-helper-styles";
|
|
61
|
+
style.textContent = `
|
|
62
|
+
@media screen and (max-width: 768px) {
|
|
63
|
+
.lil-gui.root.liquidgl-helper {
|
|
64
|
+
width: 61vw;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.lil-gui.root.liquidgl-helper,
|
|
69
|
+
.lil-gui.liquidgl-helper .lil-gui {
|
|
70
|
+
--background-color: rgb(9 9 11 / 85%);
|
|
71
|
+
--widget-color: rgb(39 39 42 / 50%);
|
|
72
|
+
--hover-color: rgb(39 39 42 / 70%);
|
|
73
|
+
--focus-color: rgb(39 39 42 / 90%);
|
|
74
|
+
--number-color: #fafafa;
|
|
75
|
+
--string-color: #fafafa;
|
|
76
|
+
--font-size: 13px;
|
|
77
|
+
--input-font-size: 13px;
|
|
78
|
+
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
79
|
+
--font-family-mono: monospace;
|
|
80
|
+
--padding: 10px;
|
|
81
|
+
--spacing: 10px;
|
|
82
|
+
--widget-height: 28px;
|
|
83
|
+
--title-height: 28px;
|
|
84
|
+
--name-width: 45%;
|
|
85
|
+
--slider-knob-width: 4px;
|
|
86
|
+
--slider-input-width: 27%;
|
|
87
|
+
--color-input-width: 27%;
|
|
88
|
+
--slider-input-min-width: 45px;
|
|
89
|
+
--color-input-min-width: 45px;
|
|
90
|
+
--folder-indent: 8px;
|
|
91
|
+
--widget-padding: 0 10px;
|
|
92
|
+
--widget-border-radius: 4px;
|
|
93
|
+
--checkbox-size: 16px;
|
|
94
|
+
--scrollbar-width: 6px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.lil-gui.root.liquidgl-helper {
|
|
98
|
+
border-radius: 12px !important;
|
|
99
|
+
border: 0.5px solid #1e1e20 !important;
|
|
100
|
+
backdrop-filter: blur(16px);
|
|
101
|
+
box-shadow: 0 4px 16px rgb(0 0 0 / 20%) !important;
|
|
102
|
+
position: fixed !important;
|
|
103
|
+
top: 1rem !important;
|
|
104
|
+
right: 1rem !important;
|
|
105
|
+
left: auto !important;
|
|
106
|
+
z-index: 999999999 !important;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.lil-gui.liquidgl-helper .title {
|
|
110
|
+
background: transparent;
|
|
111
|
+
border-bottom: 0.5px solid #1e1e20;
|
|
112
|
+
border-radius: 12px 12px 0 0 !important;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.lil-gui.liquidgl-helper .title button {
|
|
116
|
+
padding: 12px 16px !important;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.lil-gui.liquidgl-helper.closed .title {
|
|
120
|
+
border-radius: 12px !important;
|
|
121
|
+
border-bottom: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.lil-gui.liquidgl-helper .children {
|
|
125
|
+
border-top: 0.5px solid #1e1e20;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@media (max-width: 768px) {
|
|
129
|
+
.lil-gui.root.liquidgl-helper {
|
|
130
|
+
top: 1rem !important;
|
|
131
|
+
right: 1rem !important;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
document.head.appendChild(style);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function createHelperGUI(lenses, options, instanceIndex) {
|
|
139
|
+
if (typeof lil === "undefined") return;
|
|
140
|
+
|
|
141
|
+
const lensList = (Array.isArray(lenses) ? lenses : [lenses]).filter(
|
|
142
|
+
Boolean,
|
|
143
|
+
);
|
|
144
|
+
if (!lensList.length) return;
|
|
145
|
+
|
|
146
|
+
const gui = new lil.GUI({
|
|
147
|
+
title: "liquidGL Helper",
|
|
148
|
+
closeFolders: true,
|
|
149
|
+
});
|
|
150
|
+
gui.domElement.classList.add("liquidgl-helper");
|
|
151
|
+
gui.$title.style.cursor = "default";
|
|
152
|
+
|
|
153
|
+
const topOffset = 1 + instanceIndex * 3;
|
|
154
|
+
gui.domElement.style.top = `${topOffset}rem`;
|
|
155
|
+
|
|
156
|
+
const state = lensList[0].options;
|
|
157
|
+
|
|
158
|
+
const applyToLenses = (key, value) => {
|
|
159
|
+
lensList.forEach((ln) => {
|
|
160
|
+
if (!ln) return;
|
|
161
|
+
ln.options[key] = value;
|
|
162
|
+
if (key === "shadow") ln.setShadow(value);
|
|
163
|
+
if (key === "tilt") ln.setTilt(value);
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const refractionFolder = gui.addFolder("Refraction");
|
|
168
|
+
refractionFolder
|
|
169
|
+
.add(state, "refraction", 0, 0.1, 0.001)
|
|
170
|
+
.name("Refraction")
|
|
171
|
+
.onChange((v) => applyToLenses("refraction", v));
|
|
172
|
+
refractionFolder
|
|
173
|
+
.add(state, "aberration", 0, 1, 0.01)
|
|
174
|
+
.name("Aberration")
|
|
175
|
+
.onChange((v) => applyToLenses("aberration", v));
|
|
176
|
+
refractionFolder
|
|
177
|
+
.add(state, "bevelDepth", 0, 0.2, 0.001)
|
|
178
|
+
.name("Bevel Depth")
|
|
179
|
+
.onChange((v) => applyToLenses("bevelDepth", v));
|
|
180
|
+
refractionFolder
|
|
181
|
+
.add(state, "bevelWidth", 0, 0.5, 0.001)
|
|
182
|
+
.name("Bevel Width")
|
|
183
|
+
.onChange((v) => applyToLenses("bevelWidth", v));
|
|
184
|
+
refractionFolder
|
|
185
|
+
.add(state, "magnify", 1, 5, 0.1)
|
|
186
|
+
.name("Magnify")
|
|
187
|
+
.onChange((v) => applyToLenses("magnify", v));
|
|
188
|
+
|
|
189
|
+
const surfaceFolder = gui.addFolder("Surface");
|
|
190
|
+
surfaceFolder
|
|
191
|
+
.add(state, "frost", 0, 10, 0.1)
|
|
192
|
+
.name("Frost")
|
|
193
|
+
.onChange((v) => applyToLenses("frost", v));
|
|
194
|
+
surfaceFolder
|
|
195
|
+
.add(state, "specular")
|
|
196
|
+
.name("Specular")
|
|
197
|
+
.onChange((v) => applyToLenses("specular", v));
|
|
198
|
+
surfaceFolder
|
|
199
|
+
.add(state, "shadow")
|
|
200
|
+
.name("Shadow")
|
|
201
|
+
.onChange((v) => applyToLenses("shadow", v));
|
|
202
|
+
|
|
203
|
+
const tiltFolder = gui.addFolder("Tilt");
|
|
204
|
+
tiltFolder
|
|
205
|
+
.add(state, "tilt")
|
|
206
|
+
.name("Tilt")
|
|
207
|
+
.onChange((v) => applyToLenses("tilt", v));
|
|
208
|
+
tiltFolder
|
|
209
|
+
.add(state, "tiltFactor", 0, 25, 0.1)
|
|
210
|
+
.name("Tilt Factor")
|
|
211
|
+
.onChange((v) => applyToLenses("tiltFactor", v));
|
|
212
|
+
tiltFolder
|
|
213
|
+
.add(state, "tiltEase", 0, 1000, 10)
|
|
214
|
+
.name("Tilt Ease")
|
|
215
|
+
.onChange((v) => applyToLenses("tiltEase", v));
|
|
216
|
+
|
|
217
|
+
const initFolder = gui.addFolder("Initialisation");
|
|
218
|
+
const reinitState = {
|
|
219
|
+
reveal: options.reveal,
|
|
220
|
+
resolution: options.resolution,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
initFolder
|
|
224
|
+
.add(reinitState, "reveal", ["none", "fade"])
|
|
225
|
+
.name("Reveal")
|
|
226
|
+
.onFinishChange((value) => {
|
|
227
|
+
if (
|
|
228
|
+
confirm(
|
|
229
|
+
"Changing reveal applies on the next initialisation. Continue?",
|
|
230
|
+
)
|
|
231
|
+
) {
|
|
232
|
+
applyToLenses("reveal", value);
|
|
233
|
+
} else {
|
|
234
|
+
reinitState.reveal = options.reveal;
|
|
235
|
+
gui.controllersRecursive().forEach((c) => c.updateDisplay());
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
initFolder
|
|
240
|
+
.add(reinitState, "resolution", 0.5, 3, 0.25)
|
|
241
|
+
.name("Resolution")
|
|
242
|
+
.onFinishChange((value) => {
|
|
243
|
+
if (
|
|
244
|
+
confirm(
|
|
245
|
+
"Changing resolution re-captures the page snapshot. Continue?",
|
|
246
|
+
)
|
|
247
|
+
) {
|
|
248
|
+
applyToLenses("resolution", value);
|
|
249
|
+
const renderer = window.__liquidGLRenderer__;
|
|
250
|
+
if (renderer) {
|
|
251
|
+
renderer._snapshotResolution = Math.max(0.1, Math.min(3.0, value));
|
|
252
|
+
renderer.captureSnapshot();
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
reinitState.resolution = options.resolution;
|
|
256
|
+
gui.controllersRecursive().forEach((c) => c.updateDisplay());
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const copyButton = {
|
|
261
|
+
copySettings: () => {
|
|
262
|
+
const code = generateInitCode(options);
|
|
263
|
+
const controller = gui.controllers.find(
|
|
264
|
+
(c) => c.property === "copySettings",
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (!controller) return;
|
|
268
|
+
|
|
269
|
+
const originalName = controller._name;
|
|
270
|
+
controller.disable();
|
|
271
|
+
controller.name("✓ Copied");
|
|
272
|
+
|
|
273
|
+
const copySuccess = () => {
|
|
274
|
+
setTimeout(() => {
|
|
275
|
+
controller.name(originalName);
|
|
276
|
+
controller.enable();
|
|
277
|
+
}, 1500);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
281
|
+
navigator.clipboard
|
|
282
|
+
.writeText(code)
|
|
283
|
+
.then(() => {
|
|
284
|
+
copySuccess();
|
|
285
|
+
})
|
|
286
|
+
.catch((err) => {
|
|
287
|
+
console.error("Clipboard error:", err);
|
|
288
|
+
fallbackCopy(code);
|
|
289
|
+
});
|
|
290
|
+
} else {
|
|
291
|
+
fallbackCopy(code);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function fallbackCopy(text) {
|
|
295
|
+
const textarea = document.createElement("textarea");
|
|
296
|
+
textarea.value = text;
|
|
297
|
+
textarea.style.position = "fixed";
|
|
298
|
+
textarea.style.opacity = "0";
|
|
299
|
+
document.body.appendChild(textarea);
|
|
300
|
+
textarea.select();
|
|
301
|
+
try {
|
|
302
|
+
document.execCommand("copy");
|
|
303
|
+
copySuccess();
|
|
304
|
+
} catch (err) {
|
|
305
|
+
console.error("Copy failed:", err);
|
|
306
|
+
controller.name(originalName);
|
|
307
|
+
controller.enable();
|
|
308
|
+
prompt("Copy this code manually:", text);
|
|
309
|
+
}
|
|
310
|
+
document.body.removeChild(textarea);
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
gui.add(copyButton, "copySettings").name("Copy settings");
|
|
315
|
+
|
|
316
|
+
refractionFolder.close();
|
|
317
|
+
surfaceFolder.close();
|
|
318
|
+
tiltFolder.close();
|
|
319
|
+
initFolder.close();
|
|
320
|
+
|
|
321
|
+
helperGUIs.push({ gui, lenses: lensList });
|
|
322
|
+
return gui;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function generateInitCode(options) {
|
|
326
|
+
const lines = ["liquidGL({"];
|
|
327
|
+
|
|
328
|
+
lines.push(` target: "${options.target}",`);
|
|
329
|
+
lines.push(` snapshot: "${options.snapshot}",`);
|
|
330
|
+
lines.push(` resolution: ${options.resolution},`);
|
|
331
|
+
lines.push(` refraction: ${options.refraction},`);
|
|
332
|
+
lines.push(` aberration: ${options.aberration},`);
|
|
333
|
+
lines.push(` bevelDepth: ${options.bevelDepth},`);
|
|
334
|
+
lines.push(` bevelWidth: ${options.bevelWidth},`);
|
|
335
|
+
lines.push(` frost: ${options.frost},`);
|
|
336
|
+
lines.push(` shadow: ${options.shadow},`);
|
|
337
|
+
lines.push(` specular: ${options.specular},`);
|
|
338
|
+
lines.push(` reveal: "${options.reveal}",`);
|
|
339
|
+
lines.push(` tilt: ${options.tilt},`);
|
|
340
|
+
lines.push(` tiltFactor: ${options.tiltFactor},`);
|
|
341
|
+
lines.push(` tiltEase: ${options.tiltEase},`);
|
|
342
|
+
lines.push(` magnify: ${options.magnify},`);
|
|
343
|
+
lines.push(` helper: false,`);
|
|
344
|
+
lines.push(`});`);
|
|
345
|
+
|
|
346
|
+
return lines.join("\n");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* --------------------------------------------------
|
|
350
|
+
* Registration
|
|
351
|
+
* ------------------------------------------------*/
|
|
352
|
+
window.__liquidGLHelper__ = (lenses, options) => {
|
|
353
|
+
loadLilGui()
|
|
354
|
+
.then(() => {
|
|
355
|
+
const instanceIndex = helperGUIs.length;
|
|
356
|
+
createHelperGUI(lenses, options, instanceIndex);
|
|
357
|
+
})
|
|
358
|
+
.catch((err) => {
|
|
359
|
+
console.error("liquidGL: Failed to load helper GUI:", err);
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
})();
|