@marcosdemik/liquidglass 1.0.0
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 +188 -0
- package/dist/index.cjs +311 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +275 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# @marcosdemik/liquidglass
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@marcosdemik/liquidglass)
|
|
4
|
+
[](https://www.npmjs.com/package/@marcosdemik/liquidglass)
|
|
5
|
+
[](https://github.com/MarcosDemik/liquidglass/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
A React component that creates a **Liquid Glass** UI effect - glassmorphism with real-time refraction, chromatic aberration, and smooth GSAP animations.
|
|
8
|
+
|
|
9
|
+
Built with SVG filters and WebGL displacement maps.
|
|
10
|
+
|
|
11
|
+
[GitHub](https://github.com/MarcosDemik/liquidglass)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# npm
|
|
19
|
+
npm install @marcosdemik/liquidglass
|
|
20
|
+
|
|
21
|
+
# yarn
|
|
22
|
+
yarn add @marcosdemik/liquidglass
|
|
23
|
+
|
|
24
|
+
# pnpm
|
|
25
|
+
pnpm add @marcosdemik/liquidglass
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { LiquidGlassButton } from "@marcosdemik/liquidglass";
|
|
32
|
+
|
|
33
|
+
function App() {
|
|
34
|
+
return (
|
|
35
|
+
<LiquidGlassButton width={320} height={60} radius={60} chroma={3}>
|
|
36
|
+
Click me
|
|
37
|
+
</LiquidGlassButton>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Works With
|
|
43
|
+
|
|
44
|
+
- Next.js (App Router & Pages Router)
|
|
45
|
+
- Vite + React
|
|
46
|
+
- Remix
|
|
47
|
+
- Gatsby
|
|
48
|
+
- Any React 18+ project
|
|
49
|
+
|
|
50
|
+
The component includes a `"use client"` directive, so it works out of the box with Server Components.
|
|
51
|
+
|
|
52
|
+
## Props
|
|
53
|
+
|
|
54
|
+
| Prop | Type | Default | Description |
|
|
55
|
+
|------|------|---------|-------------|
|
|
56
|
+
| `width` | `number` | `300` | Button width in pixels |
|
|
57
|
+
| `height` | `number` | `56` | Button height in pixels |
|
|
58
|
+
| `radius` | `number` | `60` | Border radius in pixels |
|
|
59
|
+
| `edgeSize` | `number` | `40` | Size of the glass edge refraction zone |
|
|
60
|
+
| `intensity` | `number` | `1.0` | Refraction intensity multiplier |
|
|
61
|
+
| `smoothness` | `number` | `1.0` | Blur applied to the displacement map (softens edges) |
|
|
62
|
+
| `distortion` | `number` | `15.0` | Normal map distortion scale |
|
|
63
|
+
| `chroma` | `number` | `3` | Chromatic aberration strength (RGB channel offset) |
|
|
64
|
+
| `glassColor` | `string` | `"rgba(255,255,255,0.05)"` | Background tint color of the glass |
|
|
65
|
+
| `className` | `string` | | Additional CSS classes |
|
|
66
|
+
| `style` | `CSSProperties` | | Inline styles merged onto the button |
|
|
67
|
+
| `ref` | `Ref<HTMLButtonElement>` | | Forwarded ref to the underlying button |
|
|
68
|
+
|
|
69
|
+
All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, etc.) are also supported.
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
### Pill Button
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<LiquidGlassButton width={320} height={60} radius={60}>
|
|
77
|
+
Get Started
|
|
78
|
+
</LiquidGlassButton>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Square Icon Button
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<LiquidGlassButton width={60} height={60} radius={16} className="text-xl">
|
|
85
|
+
+
|
|
86
|
+
</LiquidGlassButton>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Circle Button
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<LiquidGlassButton width={80} height={80} radius={9999}>
|
|
93
|
+
Play
|
|
94
|
+
</LiquidGlassButton>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Wide Navigation Bar
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<LiquidGlassButton width={400} height={48} radius={24}>
|
|
101
|
+
Navigation
|
|
102
|
+
</LiquidGlassButton>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### High Distortion
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<LiquidGlassButton
|
|
109
|
+
width={320}
|
|
110
|
+
height={60}
|
|
111
|
+
radius={60}
|
|
112
|
+
distortion={30}
|
|
113
|
+
chroma={8}
|
|
114
|
+
intensity={2}
|
|
115
|
+
>
|
|
116
|
+
Distorted
|
|
117
|
+
</LiquidGlassButton>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Subtle Glass
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<LiquidGlassButton
|
|
124
|
+
width={320}
|
|
125
|
+
height={60}
|
|
126
|
+
radius={60}
|
|
127
|
+
distortion={5}
|
|
128
|
+
chroma={1}
|
|
129
|
+
smoothness={3}
|
|
130
|
+
>
|
|
131
|
+
Subtle
|
|
132
|
+
</LiquidGlassButton>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### With onClick Handler
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<LiquidGlassButton
|
|
139
|
+
width={200}
|
|
140
|
+
height={50}
|
|
141
|
+
radius={30}
|
|
142
|
+
onClick={() => console.log("clicked!")}
|
|
143
|
+
>
|
|
144
|
+
Click me
|
|
145
|
+
</LiquidGlassButton>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Custom Glass Color
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<LiquidGlassButton
|
|
152
|
+
width={320}
|
|
153
|
+
height={60}
|
|
154
|
+
radius={60}
|
|
155
|
+
glassColor="rgba(0, 150, 255, 0.1)"
|
|
156
|
+
>
|
|
157
|
+
Blue Glass
|
|
158
|
+
</LiquidGlassButton>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## How It Works
|
|
162
|
+
|
|
163
|
+
The effect is built from three layers:
|
|
164
|
+
|
|
165
|
+
1. **WebGL Displacement Map** - A GLSL fragment shader computes a displacement map from a signed distance field (SDF) of a rounded rectangle. The shader runs on an offscreen canvas and outputs a PNG data URL. The WebGL context is cached as a singleton for performance.
|
|
166
|
+
|
|
167
|
+
2. **SVG Filter Chain** - The displacement map feeds into an SVG `<filter>` that applies per-channel (R/G/B) `feDisplacementMap` at slightly different scales, producing chromatic aberration. Channels are recombined with `feBlend mode="screen"`.
|
|
168
|
+
|
|
169
|
+
3. **GSAP Animations** - Pointer events drive GSAP tweens that animate displacement scale, blur, chromatic separation, and button scale. Filter attributes are mutated directly each frame for maximum performance.
|
|
170
|
+
|
|
171
|
+
## Accessibility
|
|
172
|
+
|
|
173
|
+
- Respects `prefers-reduced-motion` - all animations are automatically disabled
|
|
174
|
+
- Semantic `<button>` element - fully keyboard navigable
|
|
175
|
+
- Supports all ARIA attributes via standard button props
|
|
176
|
+
|
|
177
|
+
## Requirements
|
|
178
|
+
|
|
179
|
+
- React 18+ (uses `useId` hook)
|
|
180
|
+
- Browser with WebGL support
|
|
181
|
+
|
|
182
|
+
## Acknowledgments
|
|
183
|
+
|
|
184
|
+
The core glass refraction concept is inspired by [rahuldotdev](https://github.com/rahuldotdev)'s liquid glass button implementation.
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __defProps = Object.defineProperties;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
13
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
14
|
+
var __spreadValues = (a, b) => {
|
|
15
|
+
for (var prop in b || (b = {}))
|
|
16
|
+
if (__hasOwnProp.call(b, prop))
|
|
17
|
+
__defNormalProp(a, prop, b[prop]);
|
|
18
|
+
if (__getOwnPropSymbols)
|
|
19
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
20
|
+
if (__propIsEnum.call(b, prop))
|
|
21
|
+
__defNormalProp(a, prop, b[prop]);
|
|
22
|
+
}
|
|
23
|
+
return a;
|
|
24
|
+
};
|
|
25
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
26
|
+
var __objRest = (source, exclude) => {
|
|
27
|
+
var target = {};
|
|
28
|
+
for (var prop in source)
|
|
29
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
30
|
+
target[prop] = source[prop];
|
|
31
|
+
if (source != null && __getOwnPropSymbols)
|
|
32
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
33
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
34
|
+
target[prop] = source[prop];
|
|
35
|
+
}
|
|
36
|
+
return target;
|
|
37
|
+
};
|
|
38
|
+
var __export = (target, all) => {
|
|
39
|
+
for (var name in all)
|
|
40
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
41
|
+
};
|
|
42
|
+
var __copyProps = (to, from, except, desc) => {
|
|
43
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
44
|
+
for (let key of __getOwnPropNames(from))
|
|
45
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
46
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
47
|
+
}
|
|
48
|
+
return to;
|
|
49
|
+
};
|
|
50
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
51
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
52
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
53
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
54
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
55
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
56
|
+
mod
|
|
57
|
+
));
|
|
58
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
59
|
+
|
|
60
|
+
// src/index.ts
|
|
61
|
+
var index_exports = {};
|
|
62
|
+
__export(index_exports, {
|
|
63
|
+
LiquidGlassButton: () => LiquidGlassButton,
|
|
64
|
+
cn: () => cn,
|
|
65
|
+
generateGlassMaps: () => generateGlassMaps
|
|
66
|
+
});
|
|
67
|
+
module.exports = __toCommonJS(index_exports);
|
|
68
|
+
|
|
69
|
+
// src/liquid-glass-button.tsx
|
|
70
|
+
var import_react = require("react");
|
|
71
|
+
var import_gsap = __toESM(require("gsap"), 1);
|
|
72
|
+
|
|
73
|
+
// src/utils.ts
|
|
74
|
+
function cn(...classes) {
|
|
75
|
+
return classes.filter(Boolean).join(" ");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/generate-displacement-map.ts
|
|
79
|
+
var VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;
|
|
80
|
+
var FRAG = `
|
|
81
|
+
precision mediump float;
|
|
82
|
+
uniform vec2 uRes;
|
|
83
|
+
uniform float uRadius;
|
|
84
|
+
uniform float uEdgeSize;
|
|
85
|
+
uniform float uIntensity;
|
|
86
|
+
uniform float uDistortion;
|
|
87
|
+
|
|
88
|
+
float sdRoundedBox(vec2 p, vec2 b, float r){
|
|
89
|
+
r = min(r, min(b.x, b.y));
|
|
90
|
+
vec2 q = abs(p) - b + r;
|
|
91
|
+
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
float getHeight(vec2 p) {
|
|
95
|
+
vec2 halfSize = uRes * 0.5 - 2.0;
|
|
96
|
+
|
|
97
|
+
// Aumenta o tamanho base do box ligeiramente proporcinal ao edge
|
|
98
|
+
halfSize += uEdgeSize * 0.2;
|
|
99
|
+
|
|
100
|
+
float d = sdRoundedBox(p, halfSize, uRadius);
|
|
101
|
+
|
|
102
|
+
float borderSoftness = uEdgeSize * uIntensity;
|
|
103
|
+
d = max(d, -borderSoftness);
|
|
104
|
+
return smoothstep(0.0, -borderSoftness, d);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void main(){
|
|
108
|
+
vec2 p = gl_FragCoord.xy - uRes * 0.5;
|
|
109
|
+
p.y = -p.y;
|
|
110
|
+
|
|
111
|
+
// --- Displacement Map ---
|
|
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);
|
|
115
|
+
|
|
116
|
+
vec2 normal = vec2(-hx, -hy) * uDistortion;
|
|
117
|
+
vec2 color = clamp(normal * 0.5 + 0.5, 0.0, 1.0);
|
|
118
|
+
|
|
119
|
+
gl_FragColor = vec4(color.x, color.y, 0.5, 1.0);
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
function compile(gl, type, src) {
|
|
123
|
+
const s = gl.createShader(type);
|
|
124
|
+
gl.shaderSource(s, src);
|
|
125
|
+
gl.compileShader(s);
|
|
126
|
+
return s;
|
|
127
|
+
}
|
|
128
|
+
var _cachedProgram = null;
|
|
129
|
+
function getGL() {
|
|
130
|
+
if (_cachedProgram) return _cachedProgram;
|
|
131
|
+
const canvas = document.createElement("canvas");
|
|
132
|
+
const gl = canvas.getContext("webgl", { preserveDrawingBuffer: true, premultipliedAlpha: false });
|
|
133
|
+
const vs = compile(gl, gl.VERTEX_SHADER, VERT);
|
|
134
|
+
const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);
|
|
135
|
+
const program = gl.createProgram();
|
|
136
|
+
gl.attachShader(program, vs);
|
|
137
|
+
gl.attachShader(program, fs);
|
|
138
|
+
gl.linkProgram(program);
|
|
139
|
+
gl.useProgram(program);
|
|
140
|
+
const buf = gl.createBuffer();
|
|
141
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
142
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
143
|
+
const pos = gl.getAttribLocation(program, "position");
|
|
144
|
+
gl.enableVertexAttribArray(pos);
|
|
145
|
+
gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);
|
|
146
|
+
_cachedProgram = { gl, program, canvas };
|
|
147
|
+
return _cachedProgram;
|
|
148
|
+
}
|
|
149
|
+
function render(width, height, radius, edgeSize, intensity, distortion) {
|
|
150
|
+
const { gl, program, canvas } = getGL();
|
|
151
|
+
canvas.width = width;
|
|
152
|
+
canvas.height = height;
|
|
153
|
+
gl.viewport(0, 0, width, height);
|
|
154
|
+
gl.clearColor(0.5, 0.5, 0.5, 1);
|
|
155
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
156
|
+
gl.useProgram(program);
|
|
157
|
+
gl.uniform2f(gl.getUniformLocation(program, "uRes"), width, height);
|
|
158
|
+
gl.uniform1f(gl.getUniformLocation(program, "uRadius"), radius);
|
|
159
|
+
gl.uniform1f(gl.getUniformLocation(program, "uEdgeSize"), edgeSize);
|
|
160
|
+
gl.uniform1f(gl.getUniformLocation(program, "uIntensity"), intensity);
|
|
161
|
+
gl.uniform1f(gl.getUniformLocation(program, "uDistortion"), distortion);
|
|
162
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
163
|
+
return canvas.toDataURL("image/png");
|
|
164
|
+
}
|
|
165
|
+
function generateGlassMaps(opts) {
|
|
166
|
+
const { width, height, radius = 60, edgeSize = 40, intensity = 1, distortion = 15 } = opts;
|
|
167
|
+
const r = Math.min(radius, width / 2, height / 2);
|
|
168
|
+
return {
|
|
169
|
+
displacement: render(width, height, r, edgeSize, intensity, distortion)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/liquid-glass-button.tsx
|
|
174
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
175
|
+
var CONFIG = {
|
|
176
|
+
initial: { scale: 1, displacement: 35, blur: 2, chroma: 3 },
|
|
177
|
+
hover: { scale: 1.05, displacement: 65, blur: 4, chroma: 10 },
|
|
178
|
+
click: { scaleDown: 0.95, scaleUp: 1.05 },
|
|
179
|
+
duration: { hover: 0.4, clickDown: 0.1, clickUp: 0.3 },
|
|
180
|
+
ease: {
|
|
181
|
+
hover: "power3.out",
|
|
182
|
+
hoverOut: "power2.out",
|
|
183
|
+
clickDown: "power2.in",
|
|
184
|
+
clickUp: "back.out(2)"
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
var PADDING = 60;
|
|
188
|
+
var LiquidGlassButton = (0, import_react.forwardRef)(
|
|
189
|
+
function LiquidGlassButton2(_a, ref) {
|
|
190
|
+
var _b = _a, { children, className, width = 300, height = 56, radius = 60, edgeSize, intensity, smoothness = 1, distortion, chroma = 3, glassColor = "rgba(255,255,255,0.05)", style } = _b, props = __objRest(_b, ["children", "className", "width", "height", "radius", "edgeSize", "intensity", "smoothness", "distortion", "chroma", "glassColor", "style"]);
|
|
191
|
+
const internalRef = (0, import_react.useRef)(null);
|
|
192
|
+
const buttonRef = ref != null ? ref : internalRef;
|
|
193
|
+
const blurRef = (0, import_react.useRef)(null);
|
|
194
|
+
const displacerR = (0, import_react.useRef)(null);
|
|
195
|
+
const displacerG = (0, import_react.useRef)(null);
|
|
196
|
+
const displacerB = (0, import_react.useRef)(null);
|
|
197
|
+
const filterId = "lg" + (0, import_react.useId)().replace(/:/g, "");
|
|
198
|
+
const [maps, setMaps] = (0, import_react.useState)(null);
|
|
199
|
+
(0, import_react.useEffect)(() => {
|
|
200
|
+
const result = generateGlassMaps({ width, height, radius, edgeSize, intensity, distortion });
|
|
201
|
+
setMaps(result);
|
|
202
|
+
}, [width, height, radius, edgeSize, intensity, distortion]);
|
|
203
|
+
(0, import_react.useEffect)(() => {
|
|
204
|
+
const button = buttonRef.current;
|
|
205
|
+
if (!button || !blurRef.current) return;
|
|
206
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
207
|
+
const fx = {
|
|
208
|
+
displacement: CONFIG.initial.displacement,
|
|
209
|
+
blur: CONFIG.initial.blur,
|
|
210
|
+
chroma
|
|
211
|
+
};
|
|
212
|
+
const sync = () => {
|
|
213
|
+
var _a2, _b2, _c, _d;
|
|
214
|
+
(_a2 = displacerR.current) == null ? void 0 : _a2.setAttribute("scale", (fx.displacement + fx.chroma).toString());
|
|
215
|
+
(_b2 = displacerG.current) == null ? void 0 : _b2.setAttribute("scale", fx.displacement.toString());
|
|
216
|
+
(_c = displacerB.current) == null ? void 0 : _c.setAttribute("scale", (fx.displacement - fx.chroma).toString());
|
|
217
|
+
(_d = blurRef.current) == null ? void 0 : _d.setAttribute("stdDeviation", fx.blur.toString());
|
|
218
|
+
};
|
|
219
|
+
sync();
|
|
220
|
+
const onEnter = () => {
|
|
221
|
+
import_gsap.default.killTweensOf([fx, button]);
|
|
222
|
+
import_gsap.default.to(fx, {
|
|
223
|
+
displacement: CONFIG.hover.displacement,
|
|
224
|
+
blur: CONFIG.hover.blur,
|
|
225
|
+
chroma: chroma * 2.5,
|
|
226
|
+
duration: CONFIG.duration.hover,
|
|
227
|
+
ease: CONFIG.ease.hover,
|
|
228
|
+
onUpdate: sync
|
|
229
|
+
});
|
|
230
|
+
import_gsap.default.to(button, { scale: CONFIG.hover.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hover });
|
|
231
|
+
};
|
|
232
|
+
const onLeave = () => {
|
|
233
|
+
import_gsap.default.killTweensOf([fx, button]);
|
|
234
|
+
import_gsap.default.to(fx, {
|
|
235
|
+
displacement: CONFIG.initial.displacement,
|
|
236
|
+
blur: CONFIG.initial.blur,
|
|
237
|
+
chroma,
|
|
238
|
+
duration: CONFIG.duration.hover,
|
|
239
|
+
ease: CONFIG.ease.hoverOut,
|
|
240
|
+
onUpdate: sync
|
|
241
|
+
});
|
|
242
|
+
import_gsap.default.to(button, { scale: CONFIG.initial.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hoverOut });
|
|
243
|
+
};
|
|
244
|
+
const onClick = () => {
|
|
245
|
+
import_gsap.default.killTweensOf(button);
|
|
246
|
+
const cur = import_gsap.default.getProperty(button, "scale");
|
|
247
|
+
import_gsap.default.timeline().to(button, { scale: cur * CONFIG.click.scaleDown, duration: CONFIG.duration.clickDown, ease: CONFIG.ease.clickDown }).to(button, { scale: CONFIG.click.scaleUp, duration: CONFIG.duration.clickUp, ease: CONFIG.ease.clickUp });
|
|
248
|
+
};
|
|
249
|
+
button.addEventListener("pointerenter", onEnter);
|
|
250
|
+
button.addEventListener("pointerleave", onLeave);
|
|
251
|
+
button.addEventListener("click", onClick);
|
|
252
|
+
return () => {
|
|
253
|
+
button.removeEventListener("pointerenter", onEnter);
|
|
254
|
+
button.removeEventListener("pointerleave", onLeave);
|
|
255
|
+
button.removeEventListener("click", onClick);
|
|
256
|
+
import_gsap.default.killTweensOf([button, fx]);
|
|
257
|
+
};
|
|
258
|
+
}, [buttonRef, maps, chroma]);
|
|
259
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
261
|
+
"button",
|
|
262
|
+
__spreadProps(__spreadValues({
|
|
263
|
+
ref: buttonRef,
|
|
264
|
+
className: cn("relative overflow-hidden shadow-2xl shadow-black/20 cursor-pointer", className),
|
|
265
|
+
style: __spreadValues({ width, height, borderRadius: radius, border: "none", background: glassColor }, style)
|
|
266
|
+
}, props), {
|
|
267
|
+
children: [
|
|
268
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
269
|
+
"div",
|
|
270
|
+
{
|
|
271
|
+
className: "absolute z-0",
|
|
272
|
+
style: {
|
|
273
|
+
top: -PADDING,
|
|
274
|
+
left: -PADDING,
|
|
275
|
+
right: -PADDING,
|
|
276
|
+
bottom: -PADDING,
|
|
277
|
+
backdropFilter: `url(#${filterId})`,
|
|
278
|
+
WebkitBackdropFilter: `url(#${filterId})`
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
),
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 z-10 flex items-center justify-center font-bold text-white shadow-[inset_0_1px_1px_rgba(255,255,255,0.4)]", style: { background: "linear-gradient(180deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.0) 100%)", borderRadius: "inherit" }, children })
|
|
283
|
+
]
|
|
284
|
+
})
|
|
285
|
+
),
|
|
286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { style: { position: "absolute", width: 0, height: 0, pointerEvents: "none" }, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("filter", { id: filterId, x: "0", y: "0", width: "100%", height: "100%", colorInterpolationFilters: "sRGB", children: [
|
|
287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feGaussianBlur", { ref: blurRef, in: "SourceGraphic", stdDeviation: CONFIG.initial.blur, result: "blurred_bg", edgeMode: "duplicate" }),
|
|
288
|
+
maps && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feImage", { href: maps.displacement, result: "disp_map", x: PADDING, y: PADDING, width, height, preserveAspectRatio: "none" }),
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feGaussianBlur", { in: "disp_map", stdDeviation: smoothness, result: "disp_blurred", edgeMode: "duplicate" }),
|
|
291
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feDisplacementMap", { ref: displacerR, in: "blurred_bg", in2: "disp_blurred", scale: CONFIG.initial.displacement + chroma, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_r" }),
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "displaced_r", type: "matrix", values: "1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "red_channel" }),
|
|
293
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feDisplacementMap", { ref: displacerG, in: "blurred_bg", in2: "disp_blurred", scale: CONFIG.initial.displacement, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_g" }),
|
|
294
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "displaced_g", type: "matrix", values: "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "green_channel" }),
|
|
295
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feDisplacementMap", { ref: displacerB, in: "blurred_bg", in2: "disp_blurred", scale: CONFIG.initial.displacement - chroma, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_b" }),
|
|
296
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "displaced_b", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0", result: "blue_channel" }),
|
|
297
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feBlend", { in: "red_channel", in2: "green_channel", mode: "screen", result: "rg_channels" }),
|
|
298
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feBlend", { in: "rg_channels", in2: "blue_channel", mode: "screen", result: "rgb_channels" }),
|
|
299
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "rgb_channels", type: "saturate", values: "1.2", result: "final" })
|
|
300
|
+
] })
|
|
301
|
+
] }) }) })
|
|
302
|
+
] });
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
306
|
+
0 && (module.exports = {
|
|
307
|
+
LiquidGlassButton,
|
|
308
|
+
cn,
|
|
309
|
+
generateGlassMaps
|
|
310
|
+
});
|
|
311
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/liquid-glass-button.tsx","../src/utils.ts","../src/generate-displacement-map.ts"],"sourcesContent":["export { LiquidGlassButton } from \"./liquid-glass-button\";\nexport type { LiquidGlassButtonProps } from \"./liquid-glass-button\";\nexport { generateGlassMaps } from \"./generate-displacement-map\";\nexport type { MapOptions } from \"./generate-displacement-map\";\nexport { cn } from \"./utils\";\n","import React, { useRef, useEffect, useState, useId, forwardRef } from \"react\";\nimport gsap from \"gsap\";\nimport { cn } from \"./utils\";\nimport { generateGlassMaps } from \"./generate-displacement-map\";\n\nconst CONFIG = {\n initial: { scale: 1, displacement: 35, blur: 2, chroma: 3 },\n hover: { scale: 1.05, displacement: 65, blur: 4, chroma: 10 },\n click: { scaleDown: 0.95, scaleUp: 1.05 },\n duration: { hover: 0.4, clickDown: 0.1, clickUp: 0.3 },\n ease: {\n hover: \"power3.out\",\n hoverOut: \"power2.out\",\n clickDown: \"power2.in\",\n clickUp: \"back.out(2)\",\n },\n} as const;\n\nconst PADDING = 60;\n\nexport interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n width?: number;\n height?: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n smoothness?: number;\n distortion?: number;\n chroma?: number;\n glassColor?: string;\n}\n\nexport const LiquidGlassButton = forwardRef<HTMLButtonElement, LiquidGlassButtonProps>(\n function LiquidGlassButton({ children, className, width = 300, height = 56, radius = 60, edgeSize, intensity, smoothness = 1, distortion, chroma = 3, glassColor = \"rgba(255,255,255,0.05)\", style, ...props }, ref) {\n const internalRef = useRef<HTMLButtonElement>(null);\n const buttonRef = (ref as React.RefObject<HTMLButtonElement>) ?? internalRef;\n\n const blurRef = useRef<SVGFEGaussianBlurElement>(null);\n const displacerR = useRef<SVGFEDisplacementMapElement>(null);\n const displacerG = useRef<SVGFEDisplacementMapElement>(null);\n const displacerB = useRef<SVGFEDisplacementMapElement>(null);\n\n const filterId = \"lg\" + useId().replace(/:/g, \"\");\n\n const [maps, setMaps] = useState<{ displacement: string } | null>(null);\n\n useEffect(() => {\n const result = generateGlassMaps({ width, height, radius, edgeSize, intensity, distortion });\n setMaps(result);\n }, [width, height, radius, edgeSize, intensity, distortion]);\n\n useEffect(() => {\n const button = buttonRef.current;\n if (!button || !blurRef.current) return;\n\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const fx = {\n displacement: CONFIG.initial.displacement,\n blur: CONFIG.initial.blur,\n chroma: chroma\n };\n\n const sync = () => {\n displacerR.current?.setAttribute(\"scale\", (fx.displacement + fx.chroma).toString());\n displacerG.current?.setAttribute(\"scale\", fx.displacement.toString());\n displacerB.current?.setAttribute(\"scale\", (fx.displacement - fx.chroma).toString());\n blurRef.current?.setAttribute(\"stdDeviation\", fx.blur.toString());\n };\n\n sync();\n\n const onEnter = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: CONFIG.hover.displacement,\n blur: CONFIG.hover.blur,\n chroma: chroma * 2.5,\n duration: CONFIG.duration.hover,\n ease: CONFIG.ease.hover,\n onUpdate: sync\n });\n gsap.to(button, { scale: CONFIG.hover.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hover });\n };\n\n const onLeave = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: CONFIG.initial.displacement,\n blur: CONFIG.initial.blur,\n chroma: chroma,\n duration: CONFIG.duration.hover,\n ease: CONFIG.ease.hoverOut,\n onUpdate: sync\n });\n gsap.to(button, { scale: CONFIG.initial.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hoverOut });\n };\n\n const onClick = () => {\n gsap.killTweensOf(button);\n const cur = gsap.getProperty(button, \"scale\") as number;\n gsap.timeline()\n .to(button, { scale: cur * CONFIG.click.scaleDown, duration: CONFIG.duration.clickDown, ease: CONFIG.ease.clickDown })\n .to(button, { scale: CONFIG.click.scaleUp, duration: CONFIG.duration.clickUp, ease: CONFIG.ease.clickUp });\n };\n\n button.addEventListener(\"pointerenter\", onEnter);\n button.addEventListener(\"pointerleave\", onLeave);\n button.addEventListener(\"click\", onClick);\n\n return () => {\n button.removeEventListener(\"pointerenter\", onEnter);\n button.removeEventListener(\"pointerleave\", onLeave);\n button.removeEventListener(\"click\", onClick);\n gsap.killTweensOf([button, fx]);\n };\n }, [buttonRef, maps, chroma]);\n\n return (\n <>\n <button\n ref={buttonRef}\n className={cn(\"relative overflow-hidden shadow-2xl shadow-black/20 cursor-pointer\", className)}\n style={{ width, height, borderRadius: radius, border: \"none\", background: glassColor, ...style }}\n {...props}\n >\n <div\n className=\"absolute z-0\"\n style={{\n top: -PADDING, left: -PADDING, right: -PADDING, bottom: -PADDING,\n backdropFilter: `url(#${filterId})`,\n WebkitBackdropFilter: `url(#${filterId})`\n }}\n />\n <div className=\"absolute inset-0 z-10 flex items-center justify-center font-bold text-white shadow-[inset_0_1px_1px_rgba(255,255,255,0.4)]\" style={{ background: \"linear-gradient(180deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.0) 100%)\", borderRadius: \"inherit\" }}>\n {children}\n </div>\n </button>\n\n <svg style={{ position: \"absolute\", width: 0, height: 0, pointerEvents: \"none\" }} aria-hidden=\"true\">\n <defs>\n <filter id={filterId} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" colorInterpolationFilters=\"sRGB\">\n <feGaussianBlur ref={blurRef} in=\"SourceGraphic\" stdDeviation={CONFIG.initial.blur} result=\"blurred_bg\" edgeMode=\"duplicate\" />\n\n {maps && (\n <>\n <feImage href={maps.displacement} result=\"disp_map\" x={PADDING} y={PADDING} width={width} height={height} preserveAspectRatio=\"none\" />\n\n <feGaussianBlur in=\"disp_map\" stdDeviation={smoothness} result=\"disp_blurred\" edgeMode=\"duplicate\" />\n\n <feDisplacementMap ref={displacerR} in=\"blurred_bg\" in2=\"disp_blurred\" scale={CONFIG.initial.displacement + chroma} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"displaced_r\" />\n <feColorMatrix in=\"displaced_r\" type=\"matrix\" values=\"1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"red_channel\" />\n\n <feDisplacementMap ref={displacerG} in=\"blurred_bg\" in2=\"disp_blurred\" scale={CONFIG.initial.displacement} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"displaced_g\" />\n <feColorMatrix in=\"displaced_g\" type=\"matrix\" values=\"0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"green_channel\" />\n\n <feDisplacementMap ref={displacerB} in=\"blurred_bg\" in2=\"disp_blurred\" scale={CONFIG.initial.displacement - chroma} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"displaced_b\" />\n <feColorMatrix in=\"displaced_b\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0\" result=\"blue_channel\" />\n\n <feBlend in=\"red_channel\" in2=\"green_channel\" mode=\"screen\" result=\"rg_channels\" />\n <feBlend in=\"rg_channels\" in2=\"blue_channel\" mode=\"screen\" result=\"rgb_channels\" />\n\n <feColorMatrix in=\"rgb_channels\" type=\"saturate\" values=\"1.2\" result=\"final\" />\n </>\n )}\n </filter>\n </defs>\n </svg>\n </>\n );\n }\n);\n","export function cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(\" \");\n}\n","export interface MapOptions {\n width: number;\n height: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n distortion?: number;\n}\n\nconst VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;\n\nconst FRAG = `\nprecision mediump float;\nuniform vec2 uRes;\nuniform float uRadius;\nuniform float uEdgeSize;\nuniform float uIntensity;\nuniform float uDistortion;\n\nfloat sdRoundedBox(vec2 p, vec2 b, float r){\n r = min(r, min(b.x, b.y));\n vec2 q = abs(p) - b + r;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n}\n\nfloat getHeight(vec2 p) {\n vec2 halfSize = uRes * 0.5 - 2.0;\n\n // Aumenta o tamanho base do box ligeiramente proporcinal ao edge\n halfSize += uEdgeSize * 0.2;\n\n float d = sdRoundedBox(p, halfSize, uRadius);\n\n float borderSoftness = uEdgeSize * uIntensity;\n d = max(d, -borderSoftness);\n return smoothstep(0.0, -borderSoftness, d);\n}\n\nvoid main(){\n vec2 p = gl_FragCoord.xy - uRes * 0.5;\n p.y = -p.y;\n\n // --- Displacement Map ---\n const vec2 e = vec2(1.0, 0.0);\n float hx = getHeight(p + e.xy) - getHeight(p - e.xy);\n float hy = getHeight(p + e.yx) - getHeight(p - e.yx);\n\n vec2 normal = vec2(-hx, -hy) * uDistortion;\n vec2 color = clamp(normal * 0.5 + 0.5, 0.0, 1.0);\n\n gl_FragColor = vec4(color.x, color.y, 0.5, 1.0);\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string) {\n const s = gl.createShader(type)!;\n gl.shaderSource(s, src);\n gl.compileShader(s);\n return s;\n}\n\nlet _cachedProgram: { gl: WebGLRenderingContext; program: WebGLProgram; canvas: HTMLCanvasElement } | null = null;\n\nfunction getGL() {\n if (_cachedProgram) return _cachedProgram;\n\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl\", { preserveDrawingBuffer: true, premultipliedAlpha: false })!;\n\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const program = gl.createProgram()!;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(program, \"position\");\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n\n _cachedProgram = { gl, program, canvas };\n return _cachedProgram;\n}\n\nfunction render(width: number, height: number, radius: number, edgeSize: number, intensity: number, distortion: number): string {\n const { gl, program, canvas } = getGL();\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n gl.clearColor(0.5, 0.5, 0.5, 1.0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n\n gl.uniform2f(gl.getUniformLocation(program, \"uRes\"), width, height);\n gl.uniform1f(gl.getUniformLocation(program, \"uRadius\"), radius);\n gl.uniform1f(gl.getUniformLocation(program, \"uEdgeSize\"), edgeSize);\n gl.uniform1f(gl.getUniformLocation(program, \"uIntensity\"), intensity);\n gl.uniform1f(gl.getUniformLocation(program, \"uDistortion\"), distortion);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n return canvas.toDataURL(\"image/png\");\n}\n\nexport function generateGlassMaps(opts: MapOptions): { displacement: string } {\n const { width, height, radius = 60, edgeSize = 40, intensity = 1.0, distortion = 15.0 } = opts;\n const r = Math.min(radius, width / 2, height / 2);\n\n return {\n displacement: render(width, height, r, edgeSize, intensity, distortion),\n };\n}\n"],"mappings":"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","names":["LiquidGlassButton","_a","_b","gsap"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
radius?: number;
|
|
7
|
+
edgeSize?: number;
|
|
8
|
+
intensity?: number;
|
|
9
|
+
smoothness?: number;
|
|
10
|
+
distortion?: number;
|
|
11
|
+
chroma?: number;
|
|
12
|
+
glassColor?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const LiquidGlassButton: React.ForwardRefExoticComponent<LiquidGlassButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
15
|
+
|
|
16
|
+
interface MapOptions {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
radius?: number;
|
|
20
|
+
edgeSize?: number;
|
|
21
|
+
intensity?: number;
|
|
22
|
+
distortion?: number;
|
|
23
|
+
}
|
|
24
|
+
declare function generateGlassMaps(opts: MapOptions): {
|
|
25
|
+
displacement: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
declare function cn(...classes: (string | undefined | null | false)[]): string;
|
|
29
|
+
|
|
30
|
+
export { LiquidGlassButton, type LiquidGlassButtonProps, type MapOptions, cn, generateGlassMaps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
radius?: number;
|
|
7
|
+
edgeSize?: number;
|
|
8
|
+
intensity?: number;
|
|
9
|
+
smoothness?: number;
|
|
10
|
+
distortion?: number;
|
|
11
|
+
chroma?: number;
|
|
12
|
+
glassColor?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const LiquidGlassButton: React.ForwardRefExoticComponent<LiquidGlassButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
15
|
+
|
|
16
|
+
interface MapOptions {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
radius?: number;
|
|
20
|
+
edgeSize?: number;
|
|
21
|
+
intensity?: number;
|
|
22
|
+
distortion?: number;
|
|
23
|
+
}
|
|
24
|
+
declare function generateGlassMaps(opts: MapOptions): {
|
|
25
|
+
displacement: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
declare function cn(...classes: (string | undefined | null | false)[]): string;
|
|
29
|
+
|
|
30
|
+
export { LiquidGlassButton, type LiquidGlassButtonProps, type MapOptions, cn, generateGlassMaps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
+
var __objRest = (source, exclude) => {
|
|
22
|
+
var target = {};
|
|
23
|
+
for (var prop in source)
|
|
24
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
25
|
+
target[prop] = source[prop];
|
|
26
|
+
if (source != null && __getOwnPropSymbols)
|
|
27
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
28
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
29
|
+
target[prop] = source[prop];
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/liquid-glass-button.tsx
|
|
35
|
+
import { useRef, useEffect, useState, useId, forwardRef } from "react";
|
|
36
|
+
import gsap from "gsap";
|
|
37
|
+
|
|
38
|
+
// src/utils.ts
|
|
39
|
+
function cn(...classes) {
|
|
40
|
+
return classes.filter(Boolean).join(" ");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/generate-displacement-map.ts
|
|
44
|
+
var VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;
|
|
45
|
+
var FRAG = `
|
|
46
|
+
precision mediump float;
|
|
47
|
+
uniform vec2 uRes;
|
|
48
|
+
uniform float uRadius;
|
|
49
|
+
uniform float uEdgeSize;
|
|
50
|
+
uniform float uIntensity;
|
|
51
|
+
uniform float uDistortion;
|
|
52
|
+
|
|
53
|
+
float sdRoundedBox(vec2 p, vec2 b, float r){
|
|
54
|
+
r = min(r, min(b.x, b.y));
|
|
55
|
+
vec2 q = abs(p) - b + r;
|
|
56
|
+
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
float getHeight(vec2 p) {
|
|
60
|
+
vec2 halfSize = uRes * 0.5 - 2.0;
|
|
61
|
+
|
|
62
|
+
// Aumenta o tamanho base do box ligeiramente proporcinal ao edge
|
|
63
|
+
halfSize += uEdgeSize * 0.2;
|
|
64
|
+
|
|
65
|
+
float d = sdRoundedBox(p, halfSize, uRadius);
|
|
66
|
+
|
|
67
|
+
float borderSoftness = uEdgeSize * uIntensity;
|
|
68
|
+
d = max(d, -borderSoftness);
|
|
69
|
+
return smoothstep(0.0, -borderSoftness, d);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void main(){
|
|
73
|
+
vec2 p = gl_FragCoord.xy - uRes * 0.5;
|
|
74
|
+
p.y = -p.y;
|
|
75
|
+
|
|
76
|
+
// --- Displacement Map ---
|
|
77
|
+
const vec2 e = vec2(1.0, 0.0);
|
|
78
|
+
float hx = getHeight(p + e.xy) - getHeight(p - e.xy);
|
|
79
|
+
float hy = getHeight(p + e.yx) - getHeight(p - e.yx);
|
|
80
|
+
|
|
81
|
+
vec2 normal = vec2(-hx, -hy) * uDistortion;
|
|
82
|
+
vec2 color = clamp(normal * 0.5 + 0.5, 0.0, 1.0);
|
|
83
|
+
|
|
84
|
+
gl_FragColor = vec4(color.x, color.y, 0.5, 1.0);
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
function compile(gl, type, src) {
|
|
88
|
+
const s = gl.createShader(type);
|
|
89
|
+
gl.shaderSource(s, src);
|
|
90
|
+
gl.compileShader(s);
|
|
91
|
+
return s;
|
|
92
|
+
}
|
|
93
|
+
var _cachedProgram = null;
|
|
94
|
+
function getGL() {
|
|
95
|
+
if (_cachedProgram) return _cachedProgram;
|
|
96
|
+
const canvas = document.createElement("canvas");
|
|
97
|
+
const gl = canvas.getContext("webgl", { preserveDrawingBuffer: true, premultipliedAlpha: false });
|
|
98
|
+
const vs = compile(gl, gl.VERTEX_SHADER, VERT);
|
|
99
|
+
const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);
|
|
100
|
+
const program = gl.createProgram();
|
|
101
|
+
gl.attachShader(program, vs);
|
|
102
|
+
gl.attachShader(program, fs);
|
|
103
|
+
gl.linkProgram(program);
|
|
104
|
+
gl.useProgram(program);
|
|
105
|
+
const buf = gl.createBuffer();
|
|
106
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
107
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
108
|
+
const pos = gl.getAttribLocation(program, "position");
|
|
109
|
+
gl.enableVertexAttribArray(pos);
|
|
110
|
+
gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);
|
|
111
|
+
_cachedProgram = { gl, program, canvas };
|
|
112
|
+
return _cachedProgram;
|
|
113
|
+
}
|
|
114
|
+
function render(width, height, radius, edgeSize, intensity, distortion) {
|
|
115
|
+
const { gl, program, canvas } = getGL();
|
|
116
|
+
canvas.width = width;
|
|
117
|
+
canvas.height = height;
|
|
118
|
+
gl.viewport(0, 0, width, height);
|
|
119
|
+
gl.clearColor(0.5, 0.5, 0.5, 1);
|
|
120
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
121
|
+
gl.useProgram(program);
|
|
122
|
+
gl.uniform2f(gl.getUniformLocation(program, "uRes"), width, height);
|
|
123
|
+
gl.uniform1f(gl.getUniformLocation(program, "uRadius"), radius);
|
|
124
|
+
gl.uniform1f(gl.getUniformLocation(program, "uEdgeSize"), edgeSize);
|
|
125
|
+
gl.uniform1f(gl.getUniformLocation(program, "uIntensity"), intensity);
|
|
126
|
+
gl.uniform1f(gl.getUniformLocation(program, "uDistortion"), distortion);
|
|
127
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
128
|
+
return canvas.toDataURL("image/png");
|
|
129
|
+
}
|
|
130
|
+
function generateGlassMaps(opts) {
|
|
131
|
+
const { width, height, radius = 60, edgeSize = 40, intensity = 1, distortion = 15 } = opts;
|
|
132
|
+
const r = Math.min(radius, width / 2, height / 2);
|
|
133
|
+
return {
|
|
134
|
+
displacement: render(width, height, r, edgeSize, intensity, distortion)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/liquid-glass-button.tsx
|
|
139
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
140
|
+
var CONFIG = {
|
|
141
|
+
initial: { scale: 1, displacement: 35, blur: 2, chroma: 3 },
|
|
142
|
+
hover: { scale: 1.05, displacement: 65, blur: 4, chroma: 10 },
|
|
143
|
+
click: { scaleDown: 0.95, scaleUp: 1.05 },
|
|
144
|
+
duration: { hover: 0.4, clickDown: 0.1, clickUp: 0.3 },
|
|
145
|
+
ease: {
|
|
146
|
+
hover: "power3.out",
|
|
147
|
+
hoverOut: "power2.out",
|
|
148
|
+
clickDown: "power2.in",
|
|
149
|
+
clickUp: "back.out(2)"
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var PADDING = 60;
|
|
153
|
+
var LiquidGlassButton = forwardRef(
|
|
154
|
+
function LiquidGlassButton2(_a, ref) {
|
|
155
|
+
var _b = _a, { children, className, width = 300, height = 56, radius = 60, edgeSize, intensity, smoothness = 1, distortion, chroma = 3, glassColor = "rgba(255,255,255,0.05)", style } = _b, props = __objRest(_b, ["children", "className", "width", "height", "radius", "edgeSize", "intensity", "smoothness", "distortion", "chroma", "glassColor", "style"]);
|
|
156
|
+
const internalRef = useRef(null);
|
|
157
|
+
const buttonRef = ref != null ? ref : internalRef;
|
|
158
|
+
const blurRef = useRef(null);
|
|
159
|
+
const displacerR = useRef(null);
|
|
160
|
+
const displacerG = useRef(null);
|
|
161
|
+
const displacerB = useRef(null);
|
|
162
|
+
const filterId = "lg" + useId().replace(/:/g, "");
|
|
163
|
+
const [maps, setMaps] = useState(null);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
const result = generateGlassMaps({ width, height, radius, edgeSize, intensity, distortion });
|
|
166
|
+
setMaps(result);
|
|
167
|
+
}, [width, height, radius, edgeSize, intensity, distortion]);
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
const button = buttonRef.current;
|
|
170
|
+
if (!button || !blurRef.current) return;
|
|
171
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
172
|
+
const fx = {
|
|
173
|
+
displacement: CONFIG.initial.displacement,
|
|
174
|
+
blur: CONFIG.initial.blur,
|
|
175
|
+
chroma
|
|
176
|
+
};
|
|
177
|
+
const sync = () => {
|
|
178
|
+
var _a2, _b2, _c, _d;
|
|
179
|
+
(_a2 = displacerR.current) == null ? void 0 : _a2.setAttribute("scale", (fx.displacement + fx.chroma).toString());
|
|
180
|
+
(_b2 = displacerG.current) == null ? void 0 : _b2.setAttribute("scale", fx.displacement.toString());
|
|
181
|
+
(_c = displacerB.current) == null ? void 0 : _c.setAttribute("scale", (fx.displacement - fx.chroma).toString());
|
|
182
|
+
(_d = blurRef.current) == null ? void 0 : _d.setAttribute("stdDeviation", fx.blur.toString());
|
|
183
|
+
};
|
|
184
|
+
sync();
|
|
185
|
+
const onEnter = () => {
|
|
186
|
+
gsap.killTweensOf([fx, button]);
|
|
187
|
+
gsap.to(fx, {
|
|
188
|
+
displacement: CONFIG.hover.displacement,
|
|
189
|
+
blur: CONFIG.hover.blur,
|
|
190
|
+
chroma: chroma * 2.5,
|
|
191
|
+
duration: CONFIG.duration.hover,
|
|
192
|
+
ease: CONFIG.ease.hover,
|
|
193
|
+
onUpdate: sync
|
|
194
|
+
});
|
|
195
|
+
gsap.to(button, { scale: CONFIG.hover.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hover });
|
|
196
|
+
};
|
|
197
|
+
const onLeave = () => {
|
|
198
|
+
gsap.killTweensOf([fx, button]);
|
|
199
|
+
gsap.to(fx, {
|
|
200
|
+
displacement: CONFIG.initial.displacement,
|
|
201
|
+
blur: CONFIG.initial.blur,
|
|
202
|
+
chroma,
|
|
203
|
+
duration: CONFIG.duration.hover,
|
|
204
|
+
ease: CONFIG.ease.hoverOut,
|
|
205
|
+
onUpdate: sync
|
|
206
|
+
});
|
|
207
|
+
gsap.to(button, { scale: CONFIG.initial.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hoverOut });
|
|
208
|
+
};
|
|
209
|
+
const onClick = () => {
|
|
210
|
+
gsap.killTweensOf(button);
|
|
211
|
+
const cur = gsap.getProperty(button, "scale");
|
|
212
|
+
gsap.timeline().to(button, { scale: cur * CONFIG.click.scaleDown, duration: CONFIG.duration.clickDown, ease: CONFIG.ease.clickDown }).to(button, { scale: CONFIG.click.scaleUp, duration: CONFIG.duration.clickUp, ease: CONFIG.ease.clickUp });
|
|
213
|
+
};
|
|
214
|
+
button.addEventListener("pointerenter", onEnter);
|
|
215
|
+
button.addEventListener("pointerleave", onLeave);
|
|
216
|
+
button.addEventListener("click", onClick);
|
|
217
|
+
return () => {
|
|
218
|
+
button.removeEventListener("pointerenter", onEnter);
|
|
219
|
+
button.removeEventListener("pointerleave", onLeave);
|
|
220
|
+
button.removeEventListener("click", onClick);
|
|
221
|
+
gsap.killTweensOf([button, fx]);
|
|
222
|
+
};
|
|
223
|
+
}, [buttonRef, maps, chroma]);
|
|
224
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
225
|
+
/* @__PURE__ */ jsxs(
|
|
226
|
+
"button",
|
|
227
|
+
__spreadProps(__spreadValues({
|
|
228
|
+
ref: buttonRef,
|
|
229
|
+
className: cn("relative overflow-hidden shadow-2xl shadow-black/20 cursor-pointer", className),
|
|
230
|
+
style: __spreadValues({ width, height, borderRadius: radius, border: "none", background: glassColor }, style)
|
|
231
|
+
}, props), {
|
|
232
|
+
children: [
|
|
233
|
+
/* @__PURE__ */ jsx(
|
|
234
|
+
"div",
|
|
235
|
+
{
|
|
236
|
+
className: "absolute z-0",
|
|
237
|
+
style: {
|
|
238
|
+
top: -PADDING,
|
|
239
|
+
left: -PADDING,
|
|
240
|
+
right: -PADDING,
|
|
241
|
+
bottom: -PADDING,
|
|
242
|
+
backdropFilter: `url(#${filterId})`,
|
|
243
|
+
WebkitBackdropFilter: `url(#${filterId})`
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
),
|
|
247
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center font-bold text-white shadow-[inset_0_1px_1px_rgba(255,255,255,0.4)]", style: { background: "linear-gradient(180deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.0) 100%)", borderRadius: "inherit" }, children })
|
|
248
|
+
]
|
|
249
|
+
})
|
|
250
|
+
),
|
|
251
|
+
/* @__PURE__ */ jsx("svg", { style: { position: "absolute", width: 0, height: 0, pointerEvents: "none" }, "aria-hidden": "true", children: /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("filter", { id: filterId, x: "0", y: "0", width: "100%", height: "100%", colorInterpolationFilters: "sRGB", children: [
|
|
252
|
+
/* @__PURE__ */ jsx("feGaussianBlur", { ref: blurRef, in: "SourceGraphic", stdDeviation: CONFIG.initial.blur, result: "blurred_bg", edgeMode: "duplicate" }),
|
|
253
|
+
maps && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
254
|
+
/* @__PURE__ */ jsx("feImage", { href: maps.displacement, result: "disp_map", x: PADDING, y: PADDING, width, height, preserveAspectRatio: "none" }),
|
|
255
|
+
/* @__PURE__ */ jsx("feGaussianBlur", { in: "disp_map", stdDeviation: smoothness, result: "disp_blurred", edgeMode: "duplicate" }),
|
|
256
|
+
/* @__PURE__ */ jsx("feDisplacementMap", { ref: displacerR, in: "blurred_bg", in2: "disp_blurred", scale: CONFIG.initial.displacement + chroma, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_r" }),
|
|
257
|
+
/* @__PURE__ */ jsx("feColorMatrix", { in: "displaced_r", type: "matrix", values: "1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "red_channel" }),
|
|
258
|
+
/* @__PURE__ */ jsx("feDisplacementMap", { ref: displacerG, in: "blurred_bg", in2: "disp_blurred", scale: CONFIG.initial.displacement, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_g" }),
|
|
259
|
+
/* @__PURE__ */ jsx("feColorMatrix", { in: "displaced_g", type: "matrix", values: "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "green_channel" }),
|
|
260
|
+
/* @__PURE__ */ jsx("feDisplacementMap", { ref: displacerB, in: "blurred_bg", in2: "disp_blurred", scale: CONFIG.initial.displacement - chroma, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_b" }),
|
|
261
|
+
/* @__PURE__ */ jsx("feColorMatrix", { in: "displaced_b", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0", result: "blue_channel" }),
|
|
262
|
+
/* @__PURE__ */ jsx("feBlend", { in: "red_channel", in2: "green_channel", mode: "screen", result: "rg_channels" }),
|
|
263
|
+
/* @__PURE__ */ jsx("feBlend", { in: "rg_channels", in2: "blue_channel", mode: "screen", result: "rgb_channels" }),
|
|
264
|
+
/* @__PURE__ */ jsx("feColorMatrix", { in: "rgb_channels", type: "saturate", values: "1.2", result: "final" })
|
|
265
|
+
] })
|
|
266
|
+
] }) }) })
|
|
267
|
+
] });
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
export {
|
|
271
|
+
LiquidGlassButton,
|
|
272
|
+
cn,
|
|
273
|
+
generateGlassMaps
|
|
274
|
+
};
|
|
275
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/liquid-glass-button.tsx","../src/utils.ts","../src/generate-displacement-map.ts"],"sourcesContent":["import React, { useRef, useEffect, useState, useId, forwardRef } from \"react\";\nimport gsap from \"gsap\";\nimport { cn } from \"./utils\";\nimport { generateGlassMaps } from \"./generate-displacement-map\";\n\nconst CONFIG = {\n initial: { scale: 1, displacement: 35, blur: 2, chroma: 3 },\n hover: { scale: 1.05, displacement: 65, blur: 4, chroma: 10 },\n click: { scaleDown: 0.95, scaleUp: 1.05 },\n duration: { hover: 0.4, clickDown: 0.1, clickUp: 0.3 },\n ease: {\n hover: \"power3.out\",\n hoverOut: \"power2.out\",\n clickDown: \"power2.in\",\n clickUp: \"back.out(2)\",\n },\n} as const;\n\nconst PADDING = 60;\n\nexport interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n width?: number;\n height?: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n smoothness?: number;\n distortion?: number;\n chroma?: number;\n glassColor?: string;\n}\n\nexport const LiquidGlassButton = forwardRef<HTMLButtonElement, LiquidGlassButtonProps>(\n function LiquidGlassButton({ children, className, width = 300, height = 56, radius = 60, edgeSize, intensity, smoothness = 1, distortion, chroma = 3, glassColor = \"rgba(255,255,255,0.05)\", style, ...props }, ref) {\n const internalRef = useRef<HTMLButtonElement>(null);\n const buttonRef = (ref as React.RefObject<HTMLButtonElement>) ?? internalRef;\n\n const blurRef = useRef<SVGFEGaussianBlurElement>(null);\n const displacerR = useRef<SVGFEDisplacementMapElement>(null);\n const displacerG = useRef<SVGFEDisplacementMapElement>(null);\n const displacerB = useRef<SVGFEDisplacementMapElement>(null);\n\n const filterId = \"lg\" + useId().replace(/:/g, \"\");\n\n const [maps, setMaps] = useState<{ displacement: string } | null>(null);\n\n useEffect(() => {\n const result = generateGlassMaps({ width, height, radius, edgeSize, intensity, distortion });\n setMaps(result);\n }, [width, height, radius, edgeSize, intensity, distortion]);\n\n useEffect(() => {\n const button = buttonRef.current;\n if (!button || !blurRef.current) return;\n\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const fx = {\n displacement: CONFIG.initial.displacement,\n blur: CONFIG.initial.blur,\n chroma: chroma\n };\n\n const sync = () => {\n displacerR.current?.setAttribute(\"scale\", (fx.displacement + fx.chroma).toString());\n displacerG.current?.setAttribute(\"scale\", fx.displacement.toString());\n displacerB.current?.setAttribute(\"scale\", (fx.displacement - fx.chroma).toString());\n blurRef.current?.setAttribute(\"stdDeviation\", fx.blur.toString());\n };\n\n sync();\n\n const onEnter = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: CONFIG.hover.displacement,\n blur: CONFIG.hover.blur,\n chroma: chroma * 2.5,\n duration: CONFIG.duration.hover,\n ease: CONFIG.ease.hover,\n onUpdate: sync\n });\n gsap.to(button, { scale: CONFIG.hover.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hover });\n };\n\n const onLeave = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: CONFIG.initial.displacement,\n blur: CONFIG.initial.blur,\n chroma: chroma,\n duration: CONFIG.duration.hover,\n ease: CONFIG.ease.hoverOut,\n onUpdate: sync\n });\n gsap.to(button, { scale: CONFIG.initial.scale, duration: CONFIG.duration.hover, ease: CONFIG.ease.hoverOut });\n };\n\n const onClick = () => {\n gsap.killTweensOf(button);\n const cur = gsap.getProperty(button, \"scale\") as number;\n gsap.timeline()\n .to(button, { scale: cur * CONFIG.click.scaleDown, duration: CONFIG.duration.clickDown, ease: CONFIG.ease.clickDown })\n .to(button, { scale: CONFIG.click.scaleUp, duration: CONFIG.duration.clickUp, ease: CONFIG.ease.clickUp });\n };\n\n button.addEventListener(\"pointerenter\", onEnter);\n button.addEventListener(\"pointerleave\", onLeave);\n button.addEventListener(\"click\", onClick);\n\n return () => {\n button.removeEventListener(\"pointerenter\", onEnter);\n button.removeEventListener(\"pointerleave\", onLeave);\n button.removeEventListener(\"click\", onClick);\n gsap.killTweensOf([button, fx]);\n };\n }, [buttonRef, maps, chroma]);\n\n return (\n <>\n <button\n ref={buttonRef}\n className={cn(\"relative overflow-hidden shadow-2xl shadow-black/20 cursor-pointer\", className)}\n style={{ width, height, borderRadius: radius, border: \"none\", background: glassColor, ...style }}\n {...props}\n >\n <div\n className=\"absolute z-0\"\n style={{\n top: -PADDING, left: -PADDING, right: -PADDING, bottom: -PADDING,\n backdropFilter: `url(#${filterId})`,\n WebkitBackdropFilter: `url(#${filterId})`\n }}\n />\n <div className=\"absolute inset-0 z-10 flex items-center justify-center font-bold text-white shadow-[inset_0_1px_1px_rgba(255,255,255,0.4)]\" style={{ background: \"linear-gradient(180deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.0) 100%)\", borderRadius: \"inherit\" }}>\n {children}\n </div>\n </button>\n\n <svg style={{ position: \"absolute\", width: 0, height: 0, pointerEvents: \"none\" }} aria-hidden=\"true\">\n <defs>\n <filter id={filterId} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" colorInterpolationFilters=\"sRGB\">\n <feGaussianBlur ref={blurRef} in=\"SourceGraphic\" stdDeviation={CONFIG.initial.blur} result=\"blurred_bg\" edgeMode=\"duplicate\" />\n\n {maps && (\n <>\n <feImage href={maps.displacement} result=\"disp_map\" x={PADDING} y={PADDING} width={width} height={height} preserveAspectRatio=\"none\" />\n\n <feGaussianBlur in=\"disp_map\" stdDeviation={smoothness} result=\"disp_blurred\" edgeMode=\"duplicate\" />\n\n <feDisplacementMap ref={displacerR} in=\"blurred_bg\" in2=\"disp_blurred\" scale={CONFIG.initial.displacement + chroma} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"displaced_r\" />\n <feColorMatrix in=\"displaced_r\" type=\"matrix\" values=\"1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"red_channel\" />\n\n <feDisplacementMap ref={displacerG} in=\"blurred_bg\" in2=\"disp_blurred\" scale={CONFIG.initial.displacement} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"displaced_g\" />\n <feColorMatrix in=\"displaced_g\" type=\"matrix\" values=\"0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0\" result=\"green_channel\" />\n\n <feDisplacementMap ref={displacerB} in=\"blurred_bg\" in2=\"disp_blurred\" scale={CONFIG.initial.displacement - chroma} xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"displaced_b\" />\n <feColorMatrix in=\"displaced_b\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0\" result=\"blue_channel\" />\n\n <feBlend in=\"red_channel\" in2=\"green_channel\" mode=\"screen\" result=\"rg_channels\" />\n <feBlend in=\"rg_channels\" in2=\"blue_channel\" mode=\"screen\" result=\"rgb_channels\" />\n\n <feColorMatrix in=\"rgb_channels\" type=\"saturate\" values=\"1.2\" result=\"final\" />\n </>\n )}\n </filter>\n </defs>\n </svg>\n </>\n );\n }\n);\n","export function cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(\" \");\n}\n","export interface MapOptions {\n width: number;\n height: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n distortion?: number;\n}\n\nconst VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;\n\nconst FRAG = `\nprecision mediump float;\nuniform vec2 uRes;\nuniform float uRadius;\nuniform float uEdgeSize;\nuniform float uIntensity;\nuniform float uDistortion;\n\nfloat sdRoundedBox(vec2 p, vec2 b, float r){\n r = min(r, min(b.x, b.y));\n vec2 q = abs(p) - b + r;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n}\n\nfloat getHeight(vec2 p) {\n vec2 halfSize = uRes * 0.5 - 2.0;\n\n // Aumenta o tamanho base do box ligeiramente proporcinal ao edge\n halfSize += uEdgeSize * 0.2;\n\n float d = sdRoundedBox(p, halfSize, uRadius);\n\n float borderSoftness = uEdgeSize * uIntensity;\n d = max(d, -borderSoftness);\n return smoothstep(0.0, -borderSoftness, d);\n}\n\nvoid main(){\n vec2 p = gl_FragCoord.xy - uRes * 0.5;\n p.y = -p.y;\n\n // --- Displacement Map ---\n const vec2 e = vec2(1.0, 0.0);\n float hx = getHeight(p + e.xy) - getHeight(p - e.xy);\n float hy = getHeight(p + e.yx) - getHeight(p - e.yx);\n\n vec2 normal = vec2(-hx, -hy) * uDistortion;\n vec2 color = clamp(normal * 0.5 + 0.5, 0.0, 1.0);\n\n gl_FragColor = vec4(color.x, color.y, 0.5, 1.0);\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string) {\n const s = gl.createShader(type)!;\n gl.shaderSource(s, src);\n gl.compileShader(s);\n return s;\n}\n\nlet _cachedProgram: { gl: WebGLRenderingContext; program: WebGLProgram; canvas: HTMLCanvasElement } | null = null;\n\nfunction getGL() {\n if (_cachedProgram) return _cachedProgram;\n\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl\", { preserveDrawingBuffer: true, premultipliedAlpha: false })!;\n\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const program = gl.createProgram()!;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(program, \"position\");\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n\n _cachedProgram = { gl, program, canvas };\n return _cachedProgram;\n}\n\nfunction render(width: number, height: number, radius: number, edgeSize: number, intensity: number, distortion: number): string {\n const { gl, program, canvas } = getGL();\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n gl.clearColor(0.5, 0.5, 0.5, 1.0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n\n gl.uniform2f(gl.getUniformLocation(program, \"uRes\"), width, height);\n gl.uniform1f(gl.getUniformLocation(program, \"uRadius\"), radius);\n gl.uniform1f(gl.getUniformLocation(program, \"uEdgeSize\"), edgeSize);\n gl.uniform1f(gl.getUniformLocation(program, \"uIntensity\"), intensity);\n gl.uniform1f(gl.getUniformLocation(program, \"uDistortion\"), distortion);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n return canvas.toDataURL(\"image/png\");\n}\n\nexport function generateGlassMaps(opts: MapOptions): { displacement: string } {\n const { width, height, radius = 60, edgeSize = 40, intensity = 1.0, distortion = 15.0 } = opts;\n const r = Math.min(radius, width / 2, height / 2);\n\n return {\n displacement: render(width, height, r, edgeSize, intensity, distortion),\n };\n}\n"],"mappings":"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","names":["LiquidGlassButton","_a","_b"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@marcosdemik/liquidglass",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Liquid Glass UI effect for React - glassmorphism refraction with SVG filters, WebGL and GSAP animations",
|
|
5
|
+
"author": "Marcos Demik <marcosdemik>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"dev": "tsup --watch"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"gsap": "^3.12.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"react": ">=18.0.0",
|
|
37
|
+
"react-dom": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/react": "^19",
|
|
41
|
+
"@types/react-dom": "^19",
|
|
42
|
+
"tsup": "^8",
|
|
43
|
+
"typescript": "^5"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"liquid-glass",
|
|
47
|
+
"liquidglass",
|
|
48
|
+
"glassmorphism",
|
|
49
|
+
"glass",
|
|
50
|
+
"glass-effect",
|
|
51
|
+
"refraction",
|
|
52
|
+
"svg-filter",
|
|
53
|
+
"webgl",
|
|
54
|
+
"react",
|
|
55
|
+
"react-component",
|
|
56
|
+
"ui",
|
|
57
|
+
"button",
|
|
58
|
+
"apple",
|
|
59
|
+
"ios",
|
|
60
|
+
"macos",
|
|
61
|
+
"chromatic-aberration",
|
|
62
|
+
"gsap",
|
|
63
|
+
"animation"
|
|
64
|
+
],
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "https://github.com/MarcosDemik/liquidglass"
|
|
68
|
+
},
|
|
69
|
+
"homepage": "https://github.com/MarcosDemik/liquidglass#readme",
|
|
70
|
+
"bugs": {
|
|
71
|
+
"url": "https://github.com/MarcosDemik/liquidglass/issues"
|
|
72
|
+
}
|
|
73
|
+
}
|