@marcosdemik/liquidglass 1.1.1 → 2.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 +44 -31
- package/dist/index.cjs +189 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -14
- package/dist/index.d.ts +11 -14
- package/dist/index.js +189 -92
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@marcosdemik/liquidglass)
|
|
5
5
|
[](https://github.com/MarcosDemik/liquidglass/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
A React component that creates a **Liquid Glass** UI effect
|
|
7
|
+
A React component that creates a **Liquid Glass** UI effect with real-time refraction, specular highlights, and smooth GSAP animations.
|
|
8
8
|
|
|
9
9
|
Built with SVG filters and WebGL displacement maps.
|
|
10
10
|
|
|
@@ -32,7 +32,7 @@ import { LiquidGlassButton } from "@marcosdemik/liquidglass";
|
|
|
32
32
|
|
|
33
33
|
function App() {
|
|
34
34
|
return (
|
|
35
|
-
<LiquidGlassButton width={320} height={60} radius={60}
|
|
35
|
+
<LiquidGlassButton width={320} height={60} radius={60}>
|
|
36
36
|
Click me
|
|
37
37
|
</LiquidGlassButton>
|
|
38
38
|
);
|
|
@@ -58,30 +58,28 @@ The component includes a `"use client"` directive, so it works out of the box wi
|
|
|
58
58
|
| `width` | `number` | `300` | Button width in pixels |
|
|
59
59
|
| `height` | `number` | `56` | Button height in pixels |
|
|
60
60
|
| `radius` | `number` | `60` | Border radius in pixels |
|
|
61
|
-
| `glassColor` | `string` | `"
|
|
61
|
+
| `glassColor` | `string` | `"transparent"` | Background tint color of the glass |
|
|
62
62
|
|
|
63
63
|
### Glass Effect
|
|
64
64
|
|
|
65
65
|
| Prop | Type | Default | Description |
|
|
66
66
|
|------|------|---------|-------------|
|
|
67
|
-
| `displacement` | `number` | `
|
|
68
|
-
| `blur` | `number` | `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `smoothness` | `number` | `1.0` | Blur on the displacement map (softens transitions) |
|
|
67
|
+
| `displacement` | `number` | `55` | How much the background refracts (feDisplacementMap scale) |
|
|
68
|
+
| `blur` | `number` | `1` | Gaussian blur applied to the background |
|
|
69
|
+
| `saturation` | `number` | `150` | Color saturation of the refracted result (feColorMatrix saturate value) |
|
|
70
|
+
| `brightness` | `number` | `1.1` | Brightness boost on the backdrop-filter (1 = normal) |
|
|
71
|
+
| `intensity` | `number` | `0.7` | Refraction intensity at the glass edge (0-1) |
|
|
72
|
+
| `edgeSize` | `number` | `30` | Thickness of the glass edge refraction zone in pixels |
|
|
73
|
+
| `specularWidth` | `number` | `0.02` | Specular rim thickness relative to the smallest dimension (0-1) |
|
|
75
74
|
|
|
76
75
|
### Hover Animation
|
|
77
76
|
|
|
78
77
|
| Prop | Type | Default | Description |
|
|
79
78
|
|------|------|---------|-------------|
|
|
80
|
-
| `hoverScale` | `number` | `1.
|
|
81
|
-
| `hoverDisplacement` | `number` | `
|
|
79
|
+
| `hoverScale` | `number` | `1.08` | Scale multiplier on hover |
|
|
80
|
+
| `hoverDisplacement` | `number` | `125` | Displacement scale on hover |
|
|
82
81
|
| `hoverBlur` | `number` | `4` | Blur amount on hover |
|
|
83
|
-
| `
|
|
84
|
-
| `hoverDuration` | `number` | `0.4` | Duration of hover animation in seconds |
|
|
82
|
+
| `hoverDuration` | `number` | `0.25` | Duration of hover animation in seconds |
|
|
85
83
|
| `disableAnimation` | `boolean` | `false` | Disable all GSAP animations |
|
|
86
84
|
|
|
87
85
|
### Standard HTML
|
|
@@ -112,17 +110,17 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
|
|
|
112
110
|
</LiquidGlassButton>
|
|
113
111
|
```
|
|
114
112
|
|
|
115
|
-
### High
|
|
113
|
+
### High Refraction
|
|
116
114
|
|
|
117
115
|
```tsx
|
|
118
116
|
<LiquidGlassButton
|
|
119
117
|
width={320}
|
|
120
118
|
height={60}
|
|
121
119
|
radius={60}
|
|
122
|
-
displacement={
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
displacement={100}
|
|
121
|
+
intensity={1}
|
|
122
|
+
edgeSize={50}
|
|
123
|
+
saturation={200}
|
|
126
124
|
>
|
|
127
125
|
Distorted
|
|
128
126
|
</LiquidGlassButton>
|
|
@@ -135,11 +133,11 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
|
|
|
135
133
|
width={320}
|
|
136
134
|
height={60}
|
|
137
135
|
radius={60}
|
|
138
|
-
displacement={
|
|
136
|
+
displacement={25}
|
|
139
137
|
blur={1}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
intensity={0.4}
|
|
139
|
+
edgeSize={15}
|
|
140
|
+
saturation={120}
|
|
143
141
|
>
|
|
144
142
|
Subtle
|
|
145
143
|
</LiquidGlassButton>
|
|
@@ -165,11 +163,10 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
|
|
|
165
163
|
width={320}
|
|
166
164
|
height={60}
|
|
167
165
|
radius={60}
|
|
168
|
-
hoverScale={1.
|
|
169
|
-
hoverDisplacement={
|
|
166
|
+
hoverScale={1.12}
|
|
167
|
+
hoverDisplacement={150}
|
|
170
168
|
hoverBlur={6}
|
|
171
|
-
|
|
172
|
-
hoverDuration={0.6}
|
|
169
|
+
hoverDuration={0.4}
|
|
173
170
|
>
|
|
174
171
|
Strong Hover
|
|
175
172
|
</LiquidGlassButton>
|
|
@@ -192,11 +189,27 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
|
|
|
192
189
|
|
|
193
190
|
The effect is built from three layers:
|
|
194
191
|
|
|
195
|
-
1. **WebGL Displacement
|
|
192
|
+
1. **WebGL Displacement + Specular Maps** - A GLSL fragment shader computes a displacement map and a specular highlight map from a signed distance field (SDF) of a rounded rectangle. 3D surface normals are derived from the SDF to create realistic light refraction at the edges. Both maps are rendered on an offscreen canvas and output as PNG data URLs. The WebGL context is cached as a singleton.
|
|
196
193
|
|
|
197
|
-
2. **SVG Filter Chain** - The displacement map feeds into an SVG `<filter>`
|
|
194
|
+
2. **SVG Filter Chain** - The displacement map feeds into an SVG `<filter>` with `feDisplacementMap` for background refraction, `feColorMatrix` for saturation control, and `feBlend` to composite the specular highlight layer on top. A `brightness()` function in the backdrop-filter adds a subtle glow.
|
|
198
195
|
|
|
199
|
-
3. **GSAP Animations** - Pointer events drive GSAP tweens that animate displacement scale, blur,
|
|
196
|
+
3. **GSAP Animations** - Pointer events drive GSAP tweens that animate displacement scale, blur, and button scale. Filter attributes are mutated directly each frame for maximum performance.
|
|
197
|
+
|
|
198
|
+
## Migration from v1.x
|
|
199
|
+
|
|
200
|
+
v2.0.0 replaced the chromatic aberration pipeline with a specular highlight pipeline for a more realistic glass effect. The following props were removed:
|
|
201
|
+
|
|
202
|
+
- `chroma` - removed (no more chromatic aberration)
|
|
203
|
+
- `distortion` - removed (shader normals are now computed from the SDF)
|
|
204
|
+
- `smoothness` - removed (displacement map blur is no longer needed)
|
|
205
|
+
- `hoverChromaMultiplier` - removed
|
|
206
|
+
|
|
207
|
+
New props added:
|
|
208
|
+
|
|
209
|
+
- `brightness` - controls backdrop brightness (default `1.1`)
|
|
210
|
+
- `specularWidth` - controls the specular rim thickness (default `0.02`)
|
|
211
|
+
|
|
212
|
+
Changed defaults: `displacement` (35 -> 55), `blur` (2 -> 1), `saturation` (1.2 -> 150), `hoverScale` (1.05 -> 1.08), `hoverDisplacement` (65 -> 125), `hoverDuration` (0.4 -> 0.25), `glassColor` ("rgba(255,255,255,0.05)" -> "transparent").
|
|
200
213
|
|
|
201
214
|
## Visibility Note
|
|
202
215
|
|
package/dist/index.cjs
CHANGED
|
@@ -81,9 +81,9 @@ var FRAG = `
|
|
|
81
81
|
precision mediump float;
|
|
82
82
|
uniform vec2 uRes;
|
|
83
83
|
uniform float uRadius;
|
|
84
|
-
uniform float
|
|
85
|
-
uniform float
|
|
86
|
-
uniform
|
|
84
|
+
uniform float uBorderSoftness;
|
|
85
|
+
uniform float uSpecularWidth;
|
|
86
|
+
uniform int uMode; // 0 = displacement, 1 = specular
|
|
87
87
|
|
|
88
88
|
float sdRoundedBox(vec2 p, vec2 b, float r){
|
|
89
89
|
r = min(r, min(b.x, b.y));
|
|
@@ -91,32 +91,36 @@ float sdRoundedBox(vec2 p, vec2 b, float r){
|
|
|
91
91
|
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
float borderSoftness = uEdgeSize * uIntensity;
|
|
103
|
-
d = max(d, -borderSoftness);
|
|
104
|
-
return smoothstep(0.0, -borderSoftness, d);
|
|
94
|
+
vec3 calcNormal(vec2 p, vec2 b, float r){
|
|
95
|
+
const float e = 1.0;
|
|
96
|
+
vec2 h = vec2(e, 0.0);
|
|
97
|
+
return normalize(vec3(
|
|
98
|
+
sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),
|
|
99
|
+
sdRoundedBox(p+h.yx, b, r) - sdRoundedBox(p-h.yx, b, r),
|
|
100
|
+
-e * 2.0
|
|
101
|
+
));
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
void main(){
|
|
108
105
|
vec2 p = gl_FragCoord.xy - uRes * 0.5;
|
|
109
|
-
|
|
106
|
+
vec2 halfSize = uRes * 0.5 - 1.0;
|
|
107
|
+
float d = sdRoundedBox(p, halfSize, uRadius);
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
const vec2 e = vec2(1.0, 0.0);
|
|
113
|
-
float hx = getHeight(p + e.xy) - getHeight(p - e.xy);
|
|
114
|
-
float hy = getHeight(p + e.yx) - getHeight(p - e.yx);
|
|
109
|
+
if(d > 0.0){ gl_FragColor = vec4(0.0); return; }
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
if(uMode == 0){
|
|
112
|
+
vec3 n = calcNormal(p, halfSize, uRadius);
|
|
113
|
+
vec3 nc = n * 0.5 + 0.5;
|
|
114
|
+
float border = smoothstep(-uBorderSoftness, 0.0, d);
|
|
115
|
+
vec3 flat_ = vec3(0.5, 0.5, 1.0);
|
|
116
|
+
gl_FragColor = vec4(mix(flat_, nc, border), 1.0);
|
|
117
|
+
} else {
|
|
118
|
+
float rim = smoothstep(-uSpecularWidth - 2.0, -uSpecularWidth, d)
|
|
119
|
+
* (1.0 - smoothstep(-2.0, 0.0, d));
|
|
120
|
+
float glow = smoothstep(-uBorderSoftness, 0.0, d) * 0.1;
|
|
121
|
+
float s = clamp(rim + glow, 0.0, 1.0);
|
|
122
|
+
gl_FragColor = vec4(vec3(s), s);
|
|
123
|
+
}
|
|
120
124
|
}
|
|
121
125
|
`;
|
|
122
126
|
function compile(gl, type, src) {
|
|
@@ -146,33 +150,42 @@ function getGL() {
|
|
|
146
150
|
_cachedProgram = { gl, program, canvas };
|
|
147
151
|
return _cachedProgram;
|
|
148
152
|
}
|
|
149
|
-
function render(width, height, radius,
|
|
153
|
+
function render(width, height, radius, borderSoftness, specularWidth, mode) {
|
|
150
154
|
const { gl, program, canvas } = getGL();
|
|
151
155
|
canvas.width = width;
|
|
152
156
|
canvas.height = height;
|
|
153
157
|
gl.viewport(0, 0, width, height);
|
|
154
|
-
gl.clearColor(0
|
|
158
|
+
gl.clearColor(0, 0, 0, 0);
|
|
155
159
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
156
160
|
gl.useProgram(program);
|
|
157
161
|
gl.uniform2f(gl.getUniformLocation(program, "uRes"), width, height);
|
|
158
162
|
gl.uniform1f(gl.getUniformLocation(program, "uRadius"), radius);
|
|
159
|
-
gl.uniform1f(gl.getUniformLocation(program, "
|
|
160
|
-
gl.uniform1f(gl.getUniformLocation(program, "
|
|
161
|
-
gl.
|
|
163
|
+
gl.uniform1f(gl.getUniformLocation(program, "uBorderSoftness"), borderSoftness);
|
|
164
|
+
gl.uniform1f(gl.getUniformLocation(program, "uSpecularWidth"), specularWidth);
|
|
165
|
+
gl.uniform1i(gl.getUniformLocation(program, "uMode"), mode);
|
|
162
166
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
163
167
|
return canvas.toDataURL("image/png");
|
|
164
168
|
}
|
|
165
169
|
function generateGlassMaps(opts) {
|
|
166
|
-
const {
|
|
170
|
+
const {
|
|
171
|
+
width,
|
|
172
|
+
height,
|
|
173
|
+
radius = 60,
|
|
174
|
+
edgeSize = 30,
|
|
175
|
+
intensity = 0.7,
|
|
176
|
+
specularWidth = 0.02
|
|
177
|
+
} = opts;
|
|
167
178
|
const r = Math.min(radius, width / 2, height / 2);
|
|
179
|
+
const borderSoftness = edgeSize * intensity;
|
|
180
|
+
const specPx = specularWidth * Math.min(width, height);
|
|
168
181
|
return {
|
|
169
|
-
displacement: render(width, height, r,
|
|
182
|
+
displacement: render(width, height, r, borderSoftness, specPx, 0),
|
|
183
|
+
specular: render(width, height, r, borderSoftness, specPx, 1)
|
|
170
184
|
};
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
// src/liquid-glass-button.tsx
|
|
174
188
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
175
|
-
var PADDING_PCT = 50;
|
|
176
189
|
var LiquidGlassButton = (0, import_react.forwardRef)(
|
|
177
190
|
function LiquidGlassButton2(_a, ref) {
|
|
178
191
|
var _b = _a, {
|
|
@@ -182,20 +195,18 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
|
|
|
182
195
|
width = 300,
|
|
183
196
|
height = 56,
|
|
184
197
|
radius = 60,
|
|
185
|
-
edgeSize,
|
|
186
|
-
intensity,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
hoverDisplacement = 65,
|
|
198
|
+
edgeSize = 30,
|
|
199
|
+
intensity = 0.7,
|
|
200
|
+
specularWidth = 0.02,
|
|
201
|
+
displacement = 55,
|
|
202
|
+
blur = 1,
|
|
203
|
+
saturation = 150,
|
|
204
|
+
brightness = 1.1,
|
|
205
|
+
glassColor = "transparent",
|
|
206
|
+
hoverScale = 1.08,
|
|
207
|
+
hoverDisplacement = 125,
|
|
196
208
|
hoverBlur = 4,
|
|
197
|
-
|
|
198
|
-
hoverDuration = 0.4,
|
|
209
|
+
hoverDuration = 0.25,
|
|
199
210
|
disableAnimation = false
|
|
200
211
|
} = _b, props = __objRest(_b, [
|
|
201
212
|
"children",
|
|
@@ -206,55 +217,55 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
|
|
|
206
217
|
"radius",
|
|
207
218
|
"edgeSize",
|
|
208
219
|
"intensity",
|
|
209
|
-
"
|
|
210
|
-
"distortion",
|
|
211
|
-
"chroma",
|
|
212
|
-
"glassColor",
|
|
220
|
+
"specularWidth",
|
|
213
221
|
"displacement",
|
|
214
222
|
"blur",
|
|
215
223
|
"saturation",
|
|
224
|
+
"brightness",
|
|
225
|
+
"glassColor",
|
|
216
226
|
"hoverScale",
|
|
217
227
|
"hoverDisplacement",
|
|
218
228
|
"hoverBlur",
|
|
219
|
-
"hoverChromaMultiplier",
|
|
220
229
|
"hoverDuration",
|
|
221
230
|
"disableAnimation"
|
|
222
231
|
]);
|
|
223
232
|
const internalRef = (0, import_react.useRef)(null);
|
|
224
233
|
const buttonRef = ref != null ? ref : internalRef;
|
|
234
|
+
const displacerRef = (0, import_react.useRef)(null);
|
|
225
235
|
const blurRef = (0, import_react.useRef)(null);
|
|
226
|
-
const displacerR = (0, import_react.useRef)(null);
|
|
227
|
-
const displacerG = (0, import_react.useRef)(null);
|
|
228
|
-
const displacerB = (0, import_react.useRef)(null);
|
|
229
236
|
const filterId = "lg" + (0, import_react.useId)().replace(/:/g, "");
|
|
230
237
|
const [maps, setMaps] = (0, import_react.useState)(null);
|
|
231
238
|
(0, import_react.useEffect)(() => {
|
|
232
239
|
let cancelled = false;
|
|
233
|
-
const m = generateGlassMaps({ width, height, radius, edgeSize, intensity,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth });
|
|
241
|
+
let loaded = 0;
|
|
242
|
+
const onLoad = () => {
|
|
243
|
+
loaded++;
|
|
244
|
+
if (loaded === 2 && !cancelled) setMaps(m);
|
|
237
245
|
};
|
|
238
|
-
|
|
246
|
+
const img1 = new Image();
|
|
247
|
+
img1.onload = onLoad;
|
|
248
|
+
img1.src = m.displacement;
|
|
249
|
+
const img2 = new Image();
|
|
250
|
+
img2.onload = onLoad;
|
|
251
|
+
img2.src = m.specular;
|
|
239
252
|
return () => {
|
|
240
253
|
cancelled = true;
|
|
241
254
|
};
|
|
242
|
-
}, [width, height, radius, edgeSize, intensity,
|
|
255
|
+
}, [width, height, radius, edgeSize, intensity, specularWidth]);
|
|
243
256
|
(0, import_react.useEffect)(() => {
|
|
244
257
|
const button = buttonRef.current;
|
|
245
|
-
|
|
258
|
+
const displacer = displacerRef.current;
|
|
259
|
+
const blurEl = blurRef.current;
|
|
260
|
+
if (!button || !displacer || !blurEl || disableAnimation) return;
|
|
246
261
|
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
247
262
|
const fx = {
|
|
248
263
|
displacement,
|
|
249
|
-
blur
|
|
250
|
-
chroma
|
|
264
|
+
blur
|
|
251
265
|
};
|
|
252
266
|
const sync = () => {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
(_b2 = displacerG.current) == null ? void 0 : _b2.setAttribute("scale", fx.displacement.toString());
|
|
256
|
-
(_c = displacerB.current) == null ? void 0 : _c.setAttribute("scale", (fx.displacement - fx.chroma).toString());
|
|
257
|
-
(_d = blurRef.current) == null ? void 0 : _d.setAttribute("stdDeviation", fx.blur.toString());
|
|
267
|
+
displacer.setAttribute("scale", fx.displacement.toString());
|
|
268
|
+
blurEl.setAttribute("stdDeviation", fx.blur.toString());
|
|
258
269
|
};
|
|
259
270
|
sync();
|
|
260
271
|
const onEnter = () => {
|
|
@@ -262,29 +273,35 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
|
|
|
262
273
|
import_gsap.default.to(fx, {
|
|
263
274
|
displacement: hoverDisplacement,
|
|
264
275
|
blur: hoverBlur,
|
|
265
|
-
chroma: chroma * hoverChromaMultiplier,
|
|
266
276
|
duration: hoverDuration,
|
|
267
|
-
ease: "
|
|
277
|
+
ease: "back.out(1.4)",
|
|
268
278
|
onUpdate: sync
|
|
269
279
|
});
|
|
270
|
-
import_gsap.default.to(button, {
|
|
280
|
+
import_gsap.default.to(button, {
|
|
281
|
+
scale: hoverScale,
|
|
282
|
+
duration: hoverDuration,
|
|
283
|
+
ease: "back.out(1.4)"
|
|
284
|
+
});
|
|
271
285
|
};
|
|
272
286
|
const onLeave = () => {
|
|
273
287
|
import_gsap.default.killTweensOf([fx, button]);
|
|
274
288
|
import_gsap.default.to(fx, {
|
|
275
289
|
displacement,
|
|
276
290
|
blur,
|
|
277
|
-
chroma,
|
|
278
291
|
duration: hoverDuration,
|
|
279
292
|
ease: "power2.out",
|
|
280
293
|
onUpdate: sync
|
|
281
294
|
});
|
|
282
|
-
import_gsap.default.to(button, {
|
|
295
|
+
import_gsap.default.to(button, {
|
|
296
|
+
scale: 1,
|
|
297
|
+
duration: hoverDuration,
|
|
298
|
+
ease: "power2.out"
|
|
299
|
+
});
|
|
283
300
|
};
|
|
284
301
|
const onClick = () => {
|
|
285
302
|
import_gsap.default.killTweensOf(button);
|
|
286
303
|
const cur = import_gsap.default.getProperty(button, "scale");
|
|
287
|
-
import_gsap.default.timeline().to(button, { scale: cur * 0.
|
|
304
|
+
import_gsap.default.timeline().to(button, { scale: cur * 0.92, duration: 0.08, ease: "power2.in" }).to(button, { scale: hoverScale, duration: 0.25, ease: "back.out(2)" });
|
|
288
305
|
};
|
|
289
306
|
button.addEventListener("pointerenter", onEnter);
|
|
290
307
|
button.addEventListener("pointerleave", onLeave);
|
|
@@ -295,48 +312,128 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
|
|
|
295
312
|
button.removeEventListener("click", onClick);
|
|
296
313
|
import_gsap.default.killTweensOf([button, fx]);
|
|
297
314
|
};
|
|
298
|
-
}, [buttonRef, maps,
|
|
315
|
+
}, [buttonRef, maps, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverDuration, disableAnimation]);
|
|
299
316
|
if (!maps) return null;
|
|
300
317
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
301
318
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
302
319
|
"button",
|
|
303
320
|
__spreadProps(__spreadValues({
|
|
304
321
|
ref: buttonRef,
|
|
305
|
-
className: cn("relative overflow-hidden shadow-
|
|
322
|
+
className: cn("relative overflow-hidden shadow-lg cursor-pointer", className),
|
|
306
323
|
style: __spreadValues({ width, height, borderRadius: radius, border: "none", background: glassColor }, style)
|
|
307
324
|
}, props), {
|
|
308
325
|
children: [
|
|
309
326
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
310
327
|
"div",
|
|
311
328
|
{
|
|
312
|
-
className: "absolute inset-0
|
|
329
|
+
className: "absolute inset-0",
|
|
313
330
|
style: {
|
|
331
|
+
backdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,
|
|
332
|
+
WebkitBackdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,
|
|
314
333
|
borderRadius: "inherit",
|
|
315
|
-
backdropFilter: `url(#${filterId})`,
|
|
316
|
-
WebkitBackdropFilter: `url(#${filterId})`,
|
|
317
334
|
willChange: "backdrop-filter",
|
|
318
335
|
transform: "translateZ(0)"
|
|
319
336
|
}
|
|
320
337
|
}
|
|
321
338
|
),
|
|
322
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
339
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
340
|
+
"div",
|
|
341
|
+
{
|
|
342
|
+
className: "absolute inset-0 inline-flex items-center justify-center font-bold text-white",
|
|
343
|
+
style: { background: "hsl(0 100% 100% / 15%)", borderRadius: "inherit" },
|
|
344
|
+
children
|
|
345
|
+
}
|
|
346
|
+
)
|
|
323
347
|
]
|
|
324
348
|
})
|
|
325
349
|
),
|
|
326
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
350
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
351
|
+
"svg",
|
|
352
|
+
{
|
|
353
|
+
colorInterpolationFilters: "sRGB",
|
|
354
|
+
style: { position: "absolute", width: 0, height: 0, overflow: "hidden" },
|
|
355
|
+
"aria-hidden": "true",
|
|
356
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("filter", { id: filterId, children: [
|
|
357
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
358
|
+
"feGaussianBlur",
|
|
359
|
+
{
|
|
360
|
+
ref: blurRef,
|
|
361
|
+
in: "SourceGraphic",
|
|
362
|
+
stdDeviation: blur,
|
|
363
|
+
result: "blurred_source"
|
|
364
|
+
}
|
|
365
|
+
),
|
|
366
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
367
|
+
"feImage",
|
|
368
|
+
{
|
|
369
|
+
href: maps.displacement,
|
|
370
|
+
x: "0",
|
|
371
|
+
y: "0",
|
|
372
|
+
width,
|
|
373
|
+
height,
|
|
374
|
+
result: "displacement_map"
|
|
375
|
+
}
|
|
376
|
+
),
|
|
377
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
378
|
+
"feDisplacementMap",
|
|
379
|
+
{
|
|
380
|
+
ref: displacerRef,
|
|
381
|
+
in: "blurred_source",
|
|
382
|
+
in2: "displacement_map",
|
|
383
|
+
scale: displacement,
|
|
384
|
+
xChannelSelector: "R",
|
|
385
|
+
yChannelSelector: "G",
|
|
386
|
+
result: "displaced"
|
|
387
|
+
}
|
|
388
|
+
),
|
|
389
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
390
|
+
"feColorMatrix",
|
|
391
|
+
{
|
|
392
|
+
in: "displaced",
|
|
393
|
+
type: "saturate",
|
|
394
|
+
result: "displaced_saturated",
|
|
395
|
+
values: saturation.toString()
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
399
|
+
"feImage",
|
|
400
|
+
{
|
|
401
|
+
href: maps.specular,
|
|
402
|
+
x: "0",
|
|
403
|
+
y: "0",
|
|
404
|
+
width,
|
|
405
|
+
height,
|
|
406
|
+
result: "specular_layer"
|
|
407
|
+
}
|
|
408
|
+
),
|
|
409
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
410
|
+
"feGaussianBlur",
|
|
411
|
+
{
|
|
412
|
+
in: "specular_layer",
|
|
413
|
+
stdDeviation: "1",
|
|
414
|
+
result: "blurred_specular_layer"
|
|
415
|
+
}
|
|
416
|
+
),
|
|
417
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
418
|
+
"feComposite",
|
|
419
|
+
{
|
|
420
|
+
in: "displaced_saturated",
|
|
421
|
+
in2: "blurred_specular_layer",
|
|
422
|
+
operator: "in",
|
|
423
|
+
result: "final_specular_layer"
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
427
|
+
"feBlend",
|
|
428
|
+
{
|
|
429
|
+
in: "final_specular_layer",
|
|
430
|
+
in2: "displaced",
|
|
431
|
+
mode: "normal"
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
] }) })
|
|
435
|
+
}
|
|
436
|
+
)
|
|
340
437
|
] });
|
|
341
438
|
}
|
|
342
439
|
);
|