@edinstvo/glass 0.0.0-stage → 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +141 -2
- package/css/glass.css +33 -0
- package/dist/backdrop.d.ts +11 -0
- package/dist/backdrop.js +104 -0
- package/dist/backdrop.js.map +1 -0
- package/dist/color.d.ts +4 -0
- package/dist/color.js +29 -0
- package/dist/color.js.map +1 -0
- package/dist/define.d.ts +1 -0
- package/dist/define.js +3 -0
- package/dist/define.js.map +1 -0
- package/dist/element.d.ts +26 -0
- package/dist/element.js +113 -0
- package/dist/element.js.map +1 -0
- package/dist/geometry.d.ts +17 -0
- package/dist/geometry.js +27 -0
- package/dist/geometry.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +47 -0
- package/dist/interaction.js +91 -0
- package/dist/interaction.js.map +1 -0
- package/dist/material.d.ts +114 -0
- package/dist/material.js +44 -0
- package/dist/material.js.map +1 -0
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.js +231 -0
- package/dist/renderer.js.map +1 -0
- package/dist/shader.d.ts +3 -0
- package/dist/shader.js +109 -0
- package/dist/shader.js.map +1 -0
- package/dist/surface.d.ts +43 -0
- package/dist/surface.js +224 -0
- package/dist/surface.js.map +1 -0
- package/package.json +52 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Makar K
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,142 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<h1>@edinstvo/glass</h1>
|
|
4
|
+
|
|
5
|
+
<p><b>Real glass for the web. One material, the same on macOS and Windows.</b></p>
|
|
6
|
+
|
|
7
|
+
<p>
|
|
8
|
+
A WebGL lens with refraction, dispersion, frost and light,<br>
|
|
9
|
+
wrapped in a single web component that works in any framework.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p>
|
|
13
|
+
<a href="https://www.npmjs.com/package/@edinstvo/glass"><img src="https://img.shields.io/npm/v/@edinstvo/glass?style=for-the-badge&labelColor=000000&color=1a1a1a&label=npm" alt="npm version"></a>
|
|
14
|
+
<img src="https://img.shields.io/badge/WebGL-2-1a1a1a?style=for-the-badge&labelColor=000000&logo=webgl&logoColor=white" alt="WebGL 2">
|
|
15
|
+
<img src="https://img.shields.io/badge/dependencies-0-1a1a1a?style=for-the-badge&labelColor=000000" alt="Zero dependencies">
|
|
16
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-1a1a1a?style=for-the-badge&labelColor=000000" alt="MIT license"></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p>
|
|
20
|
+
<a href="#install"><b>Install</b></a>
|
|
21
|
+
·
|
|
22
|
+
<a href="#material">Material</a>
|
|
23
|
+
·
|
|
24
|
+
<a href="#backdrop">Backdrop</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="#limits">Limits</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<br>
|
|
32
|
+
|
|
33
|
+
## Principles
|
|
34
|
+
|
|
35
|
+
> **Optics, not blur.** Light bends through a curved edge by Snell's law, so glass behaves like glass
|
|
36
|
+
> instead of a tinted rectangle.
|
|
37
|
+
>
|
|
38
|
+
> **Quiet by default.** A frame is drawn only when something changes. Nothing on screen moves, nothing
|
|
39
|
+
> runs on the GPU.
|
|
40
|
+
>
|
|
41
|
+
> **One context.** Every element on the page shares a single WebGL context, however many there are.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm i @edinstvo/glass
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<script type="module">
|
|
51
|
+
import "@edinstvo/glass/define";
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<edinstvo-glass material="regular" interaction="calm">
|
|
55
|
+
Hello
|
|
56
|
+
</edinstvo-glass>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The corner radius comes from CSS `border-radius`, the size from your layout. Everything else is optional.
|
|
60
|
+
|
|
61
|
+
Not a fan of custom elements? Attach glass to any node:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import { attachGlass } from "@edinstvo/glass";
|
|
65
|
+
|
|
66
|
+
const glass = attachGlass(element, { material: "frost" });
|
|
67
|
+
glass.set({ material: { frost: 0.4 } });
|
|
68
|
+
glass.destroy();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Material
|
|
72
|
+
|
|
73
|
+
Set a preset with `material`, then override anything with an attribute, a `--glass-*` CSS variable or a property.
|
|
74
|
+
|
|
75
|
+
| Attribute | Range | What it does |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `refraction` | 0 to 1 | How hard the edge bends what is behind it |
|
|
78
|
+
| `depth` | 0 to 96 px | Width of the curved edge |
|
|
79
|
+
| `splay` | 0 to 1 | Spreads the curve from the edge towards the centre |
|
|
80
|
+
| `dispersion` | 0 to 1 | How far colours split wherever the glass bends |
|
|
81
|
+
| `frost` | 0 to 1 | Matte surface, a true gaussian up to 20 px |
|
|
82
|
+
| `transparency` | 0 to 1 | How much of the backdrop survives the tint |
|
|
83
|
+
| `tint` | any CSS colour | Glass colour. `auto` follows the theme |
|
|
84
|
+
| `lift` | 0 to 1 | Brightens everything behind the glass |
|
|
85
|
+
| `light` | 0 to 1 | Shading across the curve, bright towards the light and dark away from it |
|
|
86
|
+
| `rim` | 0 to 1 | Thin light line along the edge, brightest in line with the light |
|
|
87
|
+
| `glint` | 0 to 1 | Sharp highlight along the edge. Off in every preset |
|
|
88
|
+
| `light-angle` | -180 to 180° | Where the light comes from. 0 is the top |
|
|
89
|
+
| `bloom` | 0 to 1 | Glow inside the glass and around it |
|
|
90
|
+
|
|
91
|
+
### Presets
|
|
92
|
+
|
|
93
|
+
`pure` · `clear` · `regular` · `frost` · `lens` · `prism`
|
|
94
|
+
|
|
95
|
+
`regular` and `clear` are tuned against the system glass on macOS, pixel by pixel. `pure` is the same lens with no frost at all.
|
|
96
|
+
|
|
97
|
+
### Interaction
|
|
98
|
+
|
|
99
|
+
`none` · `calm` · `lively`, or your own `{ follow, press, stretch }`.
|
|
100
|
+
Light follows the pointer, the surface swells under a press. `prefers-reduced-motion` turns all of it off.
|
|
101
|
+
|
|
102
|
+
## Backdrop
|
|
103
|
+
|
|
104
|
+
WebGL can't read the pixels of a page, so glass needs to be told what is behind it.
|
|
105
|
+
With `source="auto"` it looks for itself: the nearest `<img>`, `<video>`, `<canvas>`, CSS `url()` background
|
|
106
|
+
or solid colour under the element.
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<edinstvo-glass source="#scene"></edinstvo-glass>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
glass.source = canvas;
|
|
114
|
+
glass.invalidate();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A playing video updates on its own. For a canvas you draw on, call `invalidate()` after drawing or set `live`.
|
|
118
|
+
|
|
119
|
+
## Plain CSS
|
|
120
|
+
|
|
121
|
+
No JavaScript, or no GPU? `@edinstvo/glass/glass.css` gives you `.glass`, `.glass-clear` and `.glass-frost`
|
|
122
|
+
on top of `backdrop-filter`. The element falls back to the same look by itself when WebGL is missing.
|
|
123
|
+
|
|
124
|
+
## Limits
|
|
125
|
+
|
|
126
|
+
- Text and regular DOM behind the glass aren't refracted, only the sources listed above. Draw them to a canvas if you need them.
|
|
127
|
+
- CSS gradients aren't read yet. Glass skips them and looks further down the stack.
|
|
128
|
+
- Cross-origin images need CORS headers, otherwise that element falls back to CSS.
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm run demo
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm test
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
[MIT](LICENSE)
|
package/css/glass.css
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--glass-radius: 24px;
|
|
3
|
+
--glass-shadow: 0 8px 32px rgb(0 0 0 / .1);
|
|
4
|
+
--glass-fallback: rgb(249 249 249 / .22);
|
|
5
|
+
--glass-edge: rgb(255 255 255 / .5);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@media (prefers-color-scheme: dark) {
|
|
9
|
+
:root:not([data-theme="light"]) {
|
|
10
|
+
--glass-shadow: 0 8px 32px rgb(0 0 0 / .4);
|
|
11
|
+
--glass-fallback: rgb(28 28 30 / .55);
|
|
12
|
+
--glass-edge: rgb(255 255 255 / .22);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:root[data-theme="dark"] {
|
|
17
|
+
--glass-shadow: 0 8px 32px rgb(0 0 0 / .4);
|
|
18
|
+
--glass-fallback: rgb(28 28 30 / .55);
|
|
19
|
+
--glass-edge: rgb(255 255 255 / .22);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.glass,
|
|
23
|
+
.glass-clear,
|
|
24
|
+
.glass-frost {
|
|
25
|
+
border-radius: var(--glass-radius);
|
|
26
|
+
box-shadow: var(--glass-shadow), inset 0 0 0 .5px var(--glass-edge);
|
|
27
|
+
background: var(--glass-fallback);
|
|
28
|
+
-webkit-backdrop-filter: blur(var(--glass-blur, 12px)) saturate(1.6);
|
|
29
|
+
backdrop-filter: blur(var(--glass-blur, 12px)) saturate(1.6);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.glass-clear { --glass-blur: 2px; background: transparent; }
|
|
33
|
+
.glass-frost { --glass-blur: 28px; }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Box } from "./geometry.js";
|
|
2
|
+
export type GlassImage = HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap | OffscreenCanvas;
|
|
3
|
+
export type GlassSource = "auto" | string | GlassImage;
|
|
4
|
+
export interface Backdrop {
|
|
5
|
+
image: GlassImage;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
map: Box;
|
|
9
|
+
live: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveBackdrop(host: HTMLElement, rect: DOMRect, source: GlassSource, onReady: () => void): Backdrop | null;
|
package/dist/backdrop.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { parseColor, prefersDark } from "./color.js";
|
|
2
|
+
import { fitBox } from "./geometry.js";
|
|
3
|
+
const viewport = () => ({ x: 0, y: 0, w: innerWidth, h: innerHeight });
|
|
4
|
+
const images = new Map();
|
|
5
|
+
const solids = new Map();
|
|
6
|
+
function naturalSize(image) {
|
|
7
|
+
if (image instanceof HTMLImageElement)
|
|
8
|
+
return image.complete ? [image.naturalWidth, image.naturalHeight] : [0, 0];
|
|
9
|
+
if (image instanceof HTMLVideoElement)
|
|
10
|
+
return image.readyState >= 2 ? [image.videoWidth, image.videoHeight] : [0, 0];
|
|
11
|
+
return [image.width, image.height];
|
|
12
|
+
}
|
|
13
|
+
function whenReady(image, onReady) {
|
|
14
|
+
if (image instanceof HTMLImageElement)
|
|
15
|
+
image.addEventListener("load", onReady, { once: true });
|
|
16
|
+
if (image instanceof HTMLVideoElement)
|
|
17
|
+
image.addEventListener("loadeddata", onReady, { once: true });
|
|
18
|
+
}
|
|
19
|
+
function solid(color) {
|
|
20
|
+
let canvas = solids.get(color);
|
|
21
|
+
if (!canvas) {
|
|
22
|
+
canvas = document.createElement("canvas");
|
|
23
|
+
canvas.width = canvas.height = 1;
|
|
24
|
+
const context = canvas.getContext("2d");
|
|
25
|
+
context.fillStyle = color;
|
|
26
|
+
context.fillRect(0, 0, 1, 1);
|
|
27
|
+
solids.set(color, canvas);
|
|
28
|
+
}
|
|
29
|
+
return { image: canvas, width: 1, height: 1, map: viewport(), live: false };
|
|
30
|
+
}
|
|
31
|
+
function fromImage(image, onReady) {
|
|
32
|
+
const [width, height] = naturalSize(image);
|
|
33
|
+
if (!width || !height) {
|
|
34
|
+
whenReady(image, onReady);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
let map = viewport();
|
|
38
|
+
if (image instanceof Element && image.isConnected) {
|
|
39
|
+
const rect = image.getBoundingClientRect();
|
|
40
|
+
const fit = getComputedStyle(image).objectFit;
|
|
41
|
+
if (rect.width && rect.height)
|
|
42
|
+
map = fitBox({ x: rect.left, y: rect.top, w: rect.width, h: rect.height }, width, height, image instanceof HTMLCanvasElement && fit === "fill" ? "fill" : fit);
|
|
43
|
+
}
|
|
44
|
+
const live = image instanceof HTMLVideoElement ? !image.paused : image instanceof HTMLCanvasElement || image instanceof OffscreenCanvas;
|
|
45
|
+
return { image, width, height, map, live };
|
|
46
|
+
}
|
|
47
|
+
function loadImage(url, onReady) {
|
|
48
|
+
let image = images.get(url);
|
|
49
|
+
if (!image) {
|
|
50
|
+
image = new Image();
|
|
51
|
+
image.crossOrigin = "anonymous";
|
|
52
|
+
image.src = url;
|
|
53
|
+
images.set(url, image);
|
|
54
|
+
}
|
|
55
|
+
if (!image.complete)
|
|
56
|
+
image.addEventListener("load", onReady, { once: true });
|
|
57
|
+
return image;
|
|
58
|
+
}
|
|
59
|
+
function fromBackground(element, onReady) {
|
|
60
|
+
const style = getComputedStyle(element);
|
|
61
|
+
const url = /url\(["']?(.+?)["']?\)/.exec(style.backgroundImage)?.[1];
|
|
62
|
+
if (url) {
|
|
63
|
+
const image = loadImage(url, onReady);
|
|
64
|
+
if (!image.complete || !image.naturalWidth)
|
|
65
|
+
return null;
|
|
66
|
+
const rect = element.getBoundingClientRect();
|
|
67
|
+
const page = element === document.body || element === document.documentElement;
|
|
68
|
+
const box = style.backgroundAttachment === "fixed" || page ? viewport() : { x: rect.left, y: rect.top, w: rect.width, h: rect.height };
|
|
69
|
+
const fit = style.backgroundSize === "cover" ? "cover" : style.backgroundSize === "contain" ? "contain" : style.backgroundSize === "100% 100%" ? "fill" : "none";
|
|
70
|
+
return { image, width: image.naturalWidth, height: image.naturalHeight, map: fitBox(box, image.naturalWidth, image.naturalHeight, fit), live: false };
|
|
71
|
+
}
|
|
72
|
+
if (parseColor(style.backgroundColor)[3] > 0.99)
|
|
73
|
+
return solid(style.backgroundColor);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
function discover(host, rect, onReady) {
|
|
77
|
+
const stack = document.elementsFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
78
|
+
for (const element of stack) {
|
|
79
|
+
if (element === host || host.contains(element) || element.hasAttribute("data-engine"))
|
|
80
|
+
continue;
|
|
81
|
+
if (element instanceof HTMLImageElement || element instanceof HTMLCanvasElement || element instanceof HTMLVideoElement) {
|
|
82
|
+
const found = fromImage(element, onReady);
|
|
83
|
+
if (found)
|
|
84
|
+
return found;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const found = fromBackground(element, onReady);
|
|
88
|
+
if (found !== undefined)
|
|
89
|
+
return found;
|
|
90
|
+
}
|
|
91
|
+
return solid(prefersDark() ? "#000" : "#fff");
|
|
92
|
+
}
|
|
93
|
+
export function resolveBackdrop(host, rect, source, onReady) {
|
|
94
|
+
if (source === "auto")
|
|
95
|
+
return discover(host, rect, onReady);
|
|
96
|
+
if (typeof source === "string") {
|
|
97
|
+
const element = document.querySelector(source);
|
|
98
|
+
if (element instanceof HTMLImageElement || element instanceof HTMLCanvasElement || element instanceof HTMLVideoElement)
|
|
99
|
+
return fromImage(element, onReady);
|
|
100
|
+
return element ? (fromBackground(element, onReady) ?? null) : null;
|
|
101
|
+
}
|
|
102
|
+
return fromImage(source, onReady);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=backdrop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backdrop.js","sourceRoot":"","sources":["../src/backdrop.ts"],"names":[],"mappings":"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"}
|
package/dist/color.d.ts
ADDED
package/dist/color.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const cache = new Map();
|
|
2
|
+
let probe = null;
|
|
3
|
+
export function parseColor(color) {
|
|
4
|
+
const known = cache.get(color);
|
|
5
|
+
if (known)
|
|
6
|
+
return known;
|
|
7
|
+
probe ??= document.createElement("canvas").getContext("2d", { willReadFrequently: true });
|
|
8
|
+
if (!probe)
|
|
9
|
+
return [1, 1, 1, 1];
|
|
10
|
+
probe.clearRect(0, 0, 1, 1);
|
|
11
|
+
probe.fillStyle = "#fff";
|
|
12
|
+
probe.fillStyle = color;
|
|
13
|
+
probe.fillRect(0, 0, 1, 1);
|
|
14
|
+
const [r, g, b, a] = probe.getImageData(0, 0, 1, 1).data;
|
|
15
|
+
const rgba = a ? [r / a, g / a, b / a, a / 255] : [0, 0, 0, 0];
|
|
16
|
+
cache.set(color, rgba);
|
|
17
|
+
return rgba;
|
|
18
|
+
}
|
|
19
|
+
export function prefersDark() {
|
|
20
|
+
const theme = document.documentElement.dataset.theme;
|
|
21
|
+
if (theme)
|
|
22
|
+
return theme === "dark";
|
|
23
|
+
return matchMedia("(prefers-color-scheme: dark)").matches;
|
|
24
|
+
}
|
|
25
|
+
export function resolveTint(tint) {
|
|
26
|
+
const [r, g, b] = parseColor(tint === "auto" ? (prefersDark() ? "#1c1c1e" : "#f9f9f9") : tint);
|
|
27
|
+
return [r, g, b];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.js","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"AAEA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;AACtC,IAAI,KAAK,GAAoC,IAAI,CAAC;AAElD,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,KAAK,KAAK,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1F,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAChC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;IACxB,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,MAAM,IAAI,GAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC;IACrD,IAAI,KAAK;QAAE,OAAO,KAAK,KAAK,MAAM,CAAC;IACnC,OAAO,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/F,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACnB,CAAC"}
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/define.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { GlassSource } from "./backdrop.js";
|
|
2
|
+
import type { Interaction, InteractionName } from "./interaction.js";
|
|
3
|
+
import { type Material, type PresetName } from "./material.js";
|
|
4
|
+
declare const Base: typeof HTMLElement;
|
|
5
|
+
export declare class EdinstvoGlass extends Base {
|
|
6
|
+
static observedAttributes: string[];
|
|
7
|
+
private surface;
|
|
8
|
+
private sourceValue;
|
|
9
|
+
private materialValue;
|
|
10
|
+
private interactionValue;
|
|
11
|
+
constructor();
|
|
12
|
+
connectedCallback(): void;
|
|
13
|
+
disconnectedCallback(): void;
|
|
14
|
+
attributeChangedCallback(): void;
|
|
15
|
+
get source(): GlassSource;
|
|
16
|
+
set source(value: GlassSource);
|
|
17
|
+
get material(): PresetName | Partial<Material>;
|
|
18
|
+
set material(value: PresetName | Partial<Material>);
|
|
19
|
+
get interaction(): InteractionName | Interaction;
|
|
20
|
+
set interaction(value: InteractionName | Interaction);
|
|
21
|
+
invalidate(): void;
|
|
22
|
+
private sync;
|
|
23
|
+
private composeMaterial;
|
|
24
|
+
}
|
|
25
|
+
export declare function defineGlass(tag?: string): void;
|
|
26
|
+
export {};
|
package/dist/element.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { kebab, numericKeys, presets } from "./material.js";
|
|
2
|
+
import { GlassSurface, placeCanvas } from "./surface.js";
|
|
3
|
+
const Base = (globalThis.HTMLElement ?? class {
|
|
4
|
+
});
|
|
5
|
+
const css = `
|
|
6
|
+
:host {
|
|
7
|
+
display: inline-block;
|
|
8
|
+
position: relative;
|
|
9
|
+
isolation: isolate;
|
|
10
|
+
border-radius: var(--glass-radius, 24px);
|
|
11
|
+
box-shadow: var(--glass-shadow, 0 8px 32px rgb(0 0 0 / .1));
|
|
12
|
+
}
|
|
13
|
+
:host(:not([data-engine="webgl"])) {
|
|
14
|
+
background: var(--glass-fallback, rgb(255 255 255 / .22));
|
|
15
|
+
-webkit-backdrop-filter: blur(16px) saturate(1.6);
|
|
16
|
+
backdrop-filter: blur(16px) saturate(1.6);
|
|
17
|
+
}
|
|
18
|
+
.content {
|
|
19
|
+
display: block;
|
|
20
|
+
position: relative;
|
|
21
|
+
z-index: 1;
|
|
22
|
+
border-radius: inherit;
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
const materialAttributes = new Map(numericKeys.map((key) => [kebab(key), key]));
|
|
26
|
+
export class EdinstvoGlass extends Base {
|
|
27
|
+
static observedAttributes = ["material", "interaction", "source", "live", "tint", ...materialAttributes.keys()];
|
|
28
|
+
surface;
|
|
29
|
+
sourceValue;
|
|
30
|
+
materialValue;
|
|
31
|
+
interactionValue;
|
|
32
|
+
constructor() {
|
|
33
|
+
super();
|
|
34
|
+
const root = this.attachShadow({ mode: "open" });
|
|
35
|
+
const style = document.createElement("style");
|
|
36
|
+
style.textContent = css;
|
|
37
|
+
const canvas = document.createElement("canvas");
|
|
38
|
+
canvas.setAttribute("part", "glass");
|
|
39
|
+
placeCanvas(canvas);
|
|
40
|
+
const content = document.createElement("span");
|
|
41
|
+
content.className = "content";
|
|
42
|
+
content.setAttribute("part", "content");
|
|
43
|
+
content.append(document.createElement("slot"));
|
|
44
|
+
root.append(style, canvas, content);
|
|
45
|
+
this.surface = new GlassSurface(this, canvas);
|
|
46
|
+
}
|
|
47
|
+
connectedCallback() {
|
|
48
|
+
this.sync();
|
|
49
|
+
this.surface.connect();
|
|
50
|
+
}
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
this.surface.disconnect();
|
|
53
|
+
}
|
|
54
|
+
attributeChangedCallback() {
|
|
55
|
+
if (this.isConnected)
|
|
56
|
+
this.sync();
|
|
57
|
+
}
|
|
58
|
+
get source() {
|
|
59
|
+
return this.sourceValue ?? this.getAttribute("source") ?? "auto";
|
|
60
|
+
}
|
|
61
|
+
set source(value) {
|
|
62
|
+
this.sourceValue = value;
|
|
63
|
+
this.sync();
|
|
64
|
+
}
|
|
65
|
+
get material() {
|
|
66
|
+
return this.materialValue ?? (this.getAttribute("material") ?? "regular");
|
|
67
|
+
}
|
|
68
|
+
set material(value) {
|
|
69
|
+
this.materialValue = value;
|
|
70
|
+
this.sync();
|
|
71
|
+
}
|
|
72
|
+
get interaction() {
|
|
73
|
+
return this.interactionValue ?? (this.getAttribute("interaction") ?? "none");
|
|
74
|
+
}
|
|
75
|
+
set interaction(value) {
|
|
76
|
+
this.interactionValue = value;
|
|
77
|
+
this.sync();
|
|
78
|
+
}
|
|
79
|
+
invalidate() {
|
|
80
|
+
this.surface.invalidate();
|
|
81
|
+
}
|
|
82
|
+
sync() {
|
|
83
|
+
const live = this.getAttribute("live");
|
|
84
|
+
this.surface.set({
|
|
85
|
+
material: this.composeMaterial(),
|
|
86
|
+
interaction: this.interaction,
|
|
87
|
+
source: this.source,
|
|
88
|
+
live: live === null ? undefined : live !== "false",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
composeMaterial() {
|
|
92
|
+
const overrides = {};
|
|
93
|
+
for (const [attribute, key] of materialAttributes) {
|
|
94
|
+
const value = this.getAttribute(attribute);
|
|
95
|
+
if (value !== null)
|
|
96
|
+
overrides[key] = parseFloat(value);
|
|
97
|
+
}
|
|
98
|
+
const tint = this.getAttribute("tint");
|
|
99
|
+
if (tint)
|
|
100
|
+
overrides.tint = tint;
|
|
101
|
+
if (!Object.keys(overrides).length)
|
|
102
|
+
return this.material;
|
|
103
|
+
const base = this.material;
|
|
104
|
+
return typeof base === "string" ? { ...presetOf(base), ...overrides } : { ...base, ...overrides };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const presetOf = (name) => presets[name] ?? presets.regular;
|
|
108
|
+
export function defineGlass(tag = "edinstvo-glass") {
|
|
109
|
+
if (!customElements.get(tag))
|
|
110
|
+
customElements.define(tag, class extends EdinstvoGlass {
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=element.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface Box {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
w: number;
|
|
5
|
+
h: number;
|
|
6
|
+
}
|
|
7
|
+
export type Fit = "fill" | "cover" | "contain" | "none";
|
|
8
|
+
export declare function fitBox(box: Box, naturalW: number, naturalH: number, fit: Fit): Box;
|
|
9
|
+
export declare function uvTransform(rect: {
|
|
10
|
+
left: number;
|
|
11
|
+
top: number;
|
|
12
|
+
}, map: Box, scaleX: number, scaleY: number, dpr: number): {
|
|
13
|
+
origin: readonly [number, number];
|
|
14
|
+
perPixel: readonly [number, number];
|
|
15
|
+
};
|
|
16
|
+
export declare function cornerRadius(value: string, w: number, h: number): number;
|
|
17
|
+
export declare function lightVector(angle: number): [number, number];
|
package/dist/geometry.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function fitBox(box, naturalW, naturalH, fit) {
|
|
2
|
+
if (fit === "fill" || !naturalW || !naturalH)
|
|
3
|
+
return box;
|
|
4
|
+
if (fit === "none")
|
|
5
|
+
return { x: box.x, y: box.y, w: naturalW, h: naturalH };
|
|
6
|
+
const pick = fit === "cover" ? Math.max : Math.min;
|
|
7
|
+
const scale = pick(box.w / naturalW, box.h / naturalH);
|
|
8
|
+
const w = naturalW * scale;
|
|
9
|
+
const h = naturalH * scale;
|
|
10
|
+
return { x: box.x + (box.w - w) / 2, y: box.y + (box.h - h) / 2, w, h };
|
|
11
|
+
}
|
|
12
|
+
export function uvTransform(rect, map, scaleX, scaleY, dpr) {
|
|
13
|
+
return {
|
|
14
|
+
origin: [(rect.left - map.x) / map.w, (rect.top - map.y) / map.h],
|
|
15
|
+
perPixel: [scaleX / (dpr * map.w), scaleY / (dpr * map.h)],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function cornerRadius(value, w, h) {
|
|
19
|
+
const number = parseFloat(value) || 0;
|
|
20
|
+
const px = value.trim().endsWith("%") ? (number / 100) * Math.min(w, h) : number;
|
|
21
|
+
return Math.min(px, Math.min(w, h) / 2);
|
|
22
|
+
}
|
|
23
|
+
export function lightVector(angle) {
|
|
24
|
+
const radians = (angle * Math.PI) / 180;
|
|
25
|
+
return [Math.sin(radians), -Math.cos(radians)];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,MAAM,CAAC,GAAQ,EAAE,QAAgB,EAAE,QAAgB,EAAE,GAAQ;IAC3E,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC;IACzD,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC5E,MAAM,IAAI,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC3B,MAAM,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC3B,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAmC,EAAE,GAAQ,EAAE,MAAc,EAAE,MAAc,EAAE,GAAW;IACpH,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAU;QAC1E,QAAQ,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAU;KACpE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,CAAS,EAAE,CAAS;IAC9D,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjF,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;IACxC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { EdinstvoGlass, defineGlass } from "./element.js";
|
|
2
|
+
export { GlassSurface, attachGlass, type GlassOptions } from "./surface.js";
|
|
3
|
+
export { type Material, type PresetName, presets, ranges, resolveMaterial } from "./material.js";
|
|
4
|
+
export { type Interaction, type InteractionName, interactions } from "./interaction.js";
|
|
5
|
+
export type { GlassImage, GlassSource } from "./backdrop.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { EdinstvoGlass, defineGlass } from "./element.js";
|
|
2
|
+
export { GlassSurface, attachGlass } from "./surface.js";
|
|
3
|
+
export { presets, ranges, resolveMaterial } from "./material.js";
|
|
4
|
+
export { interactions } from "./interaction.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAkC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EAA0C,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface Interaction {
|
|
2
|
+
follow: number;
|
|
3
|
+
press: number;
|
|
4
|
+
stretch: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const interactions: {
|
|
7
|
+
readonly none: {
|
|
8
|
+
readonly follow: 0;
|
|
9
|
+
readonly press: 0;
|
|
10
|
+
readonly stretch: 0;
|
|
11
|
+
};
|
|
12
|
+
readonly calm: {
|
|
13
|
+
readonly follow: 0.5;
|
|
14
|
+
readonly press: 0.4;
|
|
15
|
+
readonly stretch: 0;
|
|
16
|
+
};
|
|
17
|
+
readonly lively: {
|
|
18
|
+
readonly follow: 1;
|
|
19
|
+
readonly press: 1;
|
|
20
|
+
readonly stretch: 1;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type InteractionName = keyof typeof interactions;
|
|
24
|
+
export declare class Spring {
|
|
25
|
+
private stiffness;
|
|
26
|
+
private damping;
|
|
27
|
+
value: number;
|
|
28
|
+
target: number;
|
|
29
|
+
private velocity;
|
|
30
|
+
constructor(value?: number, stiffness?: number, damping?: number);
|
|
31
|
+
step(dt: number): void;
|
|
32
|
+
get settled(): boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare class Motion {
|
|
35
|
+
private host;
|
|
36
|
+
private wake;
|
|
37
|
+
x: Spring;
|
|
38
|
+
y: Spring;
|
|
39
|
+
held: Spring;
|
|
40
|
+
private springs;
|
|
41
|
+
private cleanup;
|
|
42
|
+
constructor(host: HTMLElement, wake: () => void);
|
|
43
|
+
get moving(): boolean;
|
|
44
|
+
attach(): void;
|
|
45
|
+
detach(): void;
|
|
46
|
+
step(dt: number, interaction: Interaction): void;
|
|
47
|
+
}
|