liqvued 0.1.1 → 0.1.2

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 CHANGED
@@ -53,19 +53,21 @@ import { Liqvued } from 'liqvued'
53
53
 
54
54
  ## Props
55
55
 
56
- | Prop | Type | Default | Description |
57
- |-----------------|---------------------|------------|---------------------------------------------------------------------------------|
58
- | radius | number | 32 | Corner radius in pixels |
59
- | bezel | number | 22 | Width of the displacement bezel in pixels |
60
- | thickness | number | 42 | Maximum displacement magnitude in pixels |
61
- | refraction | number | 1 | Refraction index multiplier |
62
- | blur | number | 0.4 | CSS backdrop-filter blur amount |
63
- | surface | string | `'convex'` | Glass surface profile shape |
64
- | specularOpacity | number | 0.45 | Opacity of the specular highlight |
65
- | glareAngle | number | -60 | Light source angle in degrees for specular highlight |
66
- | glassBackground | string | | Glass panel background color (auto-derived from `asProps.color` when available) |
67
- | as | string \| Component | `'div'` | HTML tag or Vue component to render as |
68
- | asProps | object | `{}` | Props passed to the rendered element/component |
56
+ | Prop | Type | Default | Description |
57
+ |-----------------|---------------------|------------|----------------------------------------------------------------------------------------|
58
+ | radius | number | 32 | Corner radius in pixels |
59
+ | borderRadius | string | | Per-corner border-radius CSS value (e.g. `"10px 20px 10px 20px"`); overrides `radius` |
60
+ | bezel | number | 22 | Width of the displacement bezel in pixels |
61
+ | thickness | number | 42 | Maximum displacement magnitude in pixels |
62
+ | refraction | number | 1 | Refraction index multiplier |
63
+ | blur | number | 0.4 | CSS backdrop-filter blur amount |
64
+ | surface | string | `'convex'` | Glass surface profile shape |
65
+ | specularOpacity | number | 0.45 | Opacity of the specular highlight |
66
+ | glareAngle | number | -60 | Light source angle in degrees for specular highlight |
67
+ | glassBackground | string | | Glass panel background color (auto-derived from `asProps.color` when available) |
68
+ | fallbackOnly | boolean | `false` | When `true`, disables the SVG displacement effect and uses only CSS blur |
69
+ | as | string \| Component | `'div'` | HTML tag or Vue component to render as (rendered as a child of the glass root) |
70
+ | asProps | object | `{}` | Props passed to the rendered element/component |
69
71
 
70
72
  ### Surface Types
71
73
 
package/dist/liqvued.css CHANGED
@@ -1,2 +1,2 @@
1
- .liquid-glass[data-v-2c1a4b58]{isolation:isolate;position:relative;overflow:hidden}.liquid-glass__svg[data-v-2c1a4b58]{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}@supports not ((-webkit-backdrop-filter:url("#x")) or (backdrop-filter:url("#x"))){.liquid-glass[data-v-2c1a4b58]{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}}
1
+ .liquid-glass[data-v-01d40041]{isolation:isolate;position:relative;overflow:hidden}.liquid-glass__svg[data-v-01d40041]{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}@supports not ((-webkit-backdrop-filter:url("#x")) or (backdrop-filter:url("#x"))){.liquid-glass[data-v-01d40041]{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}}
2
2
  /*$vite$:1*/
package/dist/liqvued.mjs CHANGED
@@ -1,22 +1,23 @@
1
- import { computed as R, createBlock as le, createCommentVNode as ue, createElementBlock as ce, createElementVNode as w, defineComponent as ie, inject as de, mergeProps as fe, nextTick as K, onBeforeUnmount as he, onMounted as pe, openBlock as Q, provide as ve, ref as M, renderSlot as ge, resolveDynamicComponent as me, unref as be, watch as Me, withCtx as _e } from "vue";
2
- var ye = {
1
+ import { computed as R, createBlock as ie, createCommentVNode as ce, createElementBlock as Q, createElementVNode as v, defineComponent as de, guardReactiveProps as he, inject as fe, nextTick as X, normalizeProps as pe, normalizeStyle as ve, onBeforeUnmount as ge, onMounted as me, openBlock as L, provide as Me, ref as g, renderSlot as be, resolveDynamicComponent as ye, unref as _e, watch as xe, withCtx as ke } from "vue";
2
+ var we = {
3
3
  key: 0,
4
4
  class: "liquid-glass__svg",
5
5
  "aria-hidden": "true"
6
- }, xe = [
6
+ }, Re = [
7
7
  "href",
8
8
  "width",
9
9
  "height"
10
- ], ke = ["scale"], we = [
10
+ ], Be = ["scale"], Ie = [
11
11
  "href",
12
12
  "width",
13
13
  "height"
14
- ], Ie = /* @__PURE__ */ ie({
14
+ ], Se = /* @__PURE__ */ de({
15
15
  __name: "Liqvued",
16
16
  props: {
17
17
  as: { default: "div" },
18
18
  asProps: { default: () => ({}) },
19
19
  radius: { default: 32 },
20
+ borderRadius: {},
20
21
  bezel: { default: 22 },
21
22
  thickness: { default: 42 },
22
23
  refraction: { default: 1 },
@@ -24,12 +25,16 @@ var ye = {
24
25
  surface: { default: "convex" },
25
26
  specularOpacity: { default: 0.45 },
26
27
  glareAngle: { default: -60 },
27
- glassBackground: { default: void 0 }
28
+ glassBackground: { default: void 0 },
29
+ fallbackOnly: {
30
+ type: Boolean,
31
+ default: !1
32
+ }
28
33
  },
29
- setup(p) {
30
- const a = p, v = M(null), _ = M(1), y = M(1), F = M(""), U = M(""), z = M(1), x = CSS.supports?.("backdrop-filter", "url(#x)") && /Chrome/.test(navigator.userAgent), O = `${`lg-${Math.random().toString(36).slice(2)}`}-filter`, T = de("_liqvued", !1);
31
- ve("_liqvued", !0);
32
- const k = M(!1), A = {
34
+ setup(d) {
35
+ const a = d, x = g(null), m = g(1), M = g(1), E = g(""), F = g(""), U = g(1), b = CSS.supports?.("backdrop-filter", "url(#x)") && /Chrome/.test(navigator.userAgent), T = `${`lg-${Math.random().toString(36).slice(2)}`}-filter`, A = fe("_liqvued", !1);
36
+ Me("_liqvued", !0);
37
+ const y = g(!1), G = {
33
38
  0: 0,
34
39
  sm: 4,
35
40
  md: 8,
@@ -40,90 +45,91 @@ var ye = {
40
45
  shaped: 16
41
46
  };
42
47
  function W() {
43
- return v.value ? v.value instanceof Element ? v.value : v.value.$el ?? null : null;
48
+ return x.value;
44
49
  }
45
- function f(e, t = 0, n = 1) {
50
+ function k(e, t = 0, n = 1) {
46
51
  return Math.max(t, Math.min(n, e));
47
52
  }
48
- function X(e) {
49
- return e = f(e), e * e * e * (e * (e * 6 - 15) + 10);
53
+ function Y(e) {
54
+ return e = k(e), e * e * e * (e * (e * 6 - 15) + 10);
50
55
  }
51
- function I(e) {
52
- return Math.pow(1 - Math.pow(1 - f(e), 4), 1 / 4);
56
+ function B(e) {
57
+ return Math.pow(1 - Math.pow(1 - k(e), 4), 1 / 4);
53
58
  }
54
- function G(e) {
55
- if (a.surface === "concave") return 1 - I(e);
59
+ function N(e) {
60
+ if (a.surface === "concave") return 1 - B(e);
56
61
  if (a.surface === "lip") {
57
- const t = X(e);
58
- return I(e) * (1 - t) + (1 - I(e)) * t;
62
+ const t = Y(e);
63
+ return B(e) * (1 - t) + (1 - B(e)) * t;
59
64
  }
60
- return I(e);
61
- }
62
- function Y(e) {
63
- return (G(e + 1e-3) - G(e - 1e-3)) / (2 * 1e-3);
65
+ return B(e);
64
66
  }
65
67
  function Z(e) {
66
- const s = Math.atan(e), r = 1 / 1.5 * Math.sin(s), g = Math.asin(f(r, -1, 1));
67
- return Math.tan(s - g);
68
+ return (N(e + 1e-3) - N(e - 1e-3)) / (2 * 1e-3);
68
69
  }
69
- function ee(e, t, n, s, r) {
70
- const g = f(e, r, n - r), h = f(t, r, s - r), o = e - g, l = t - h, c = Math.hypot(o, l), m = Math.min(e, t, n - e, s - t);
71
- return o !== 0 && l !== 0 ? r - c : m;
70
+ function ee(e) {
71
+ const r = Math.atan(e), c = 1 / 1.5 * Math.sin(r), l = Math.asin(k(c, -1, 1));
72
+ return Math.tan(r - l);
72
73
  }
73
- function te(e, t, n, s, r) {
74
- const g = f(e, r, n - r), h = f(t, r, s - r);
75
- let o = e - g, l = t - h;
76
- if (o === 0 && l === 0) {
77
- const m = e, i = n - e, d = t, b = s - t, u = Math.min(m, i, d, b);
78
- u === m ? o = -1 : u === i ? o = 1 : u === d ? l = -1 : l = 1;
79
- }
80
- const c = Math.hypot(o, l) || 1;
81
- return {
82
- x: o / c,
83
- y: l / c
74
+ function te(e, t, n, r, c) {
75
+ const l = Math.min(c, Math.min(n, r) / 2), s = n / 2 - l, u = r / 2 - l, i = Math.abs(e - n / 2) - s, o = Math.abs(t - r / 2) - u;
76
+ return -(Math.min(Math.max(i, o), 0) + Math.hypot(Math.max(i, 0), Math.max(o, 0)) - l);
77
+ }
78
+ function ae(e, t, n, r) {
79
+ const c = Math.min(e, n - 1 - e), l = Math.min(t, r - 1 - t), s = 4, u = Math.pow(c, s) + Math.pow(l, s);
80
+ let i, o;
81
+ u > 1e-10 ? (i = (e > n / 2 ? 1 : -1) * (Math.pow(l, s) / u), o = (t > r / 2 ? 1 : -1) * (Math.pow(c, s) / u)) : (i = e > n / 2 ? 1 : -1, o = t > r / 2 ? 1 : -1);
82
+ const _ = Math.hypot(i, o);
83
+ return _ < 1e-10 ? {
84
+ x: 0,
85
+ y: -1
86
+ } : {
87
+ x: i / _,
88
+ y: o / _
84
89
  };
85
90
  }
86
- function N(e) {
91
+ function V(e) {
87
92
  return e.toDataURL("image/png");
88
93
  }
89
- function q() {
90
- const e = Math.max(1, Math.round(_.value)), t = Math.max(1, Math.round(y.value)), n = document.createElement("canvas");
94
+ function C() {
95
+ if (a.fallbackOnly) return;
96
+ const e = Math.max(1, Math.round(m.value)), t = Math.max(1, Math.round(M.value)), n = document.createElement("canvas");
91
97
  n.width = e, n.height = t;
92
- const s = document.createElement("canvas");
93
- s.width = e, s.height = t;
94
- const r = n.getContext("2d"), g = s.getContext("2d"), h = r.createImageData(e, t), o = g.createImageData(e, t), l = [];
95
- let c = 1;
96
- const m = {
98
+ const r = document.createElement("canvas");
99
+ r.width = e, r.height = t;
100
+ const c = n.getContext("2d"), l = r.getContext("2d"), s = c.createImageData(e, t), u = l.createImageData(e, t), i = [];
101
+ let o = 1;
102
+ const _ = {
97
103
  x: Math.cos(a.glareAngle * Math.PI / 180),
98
104
  y: Math.sin(a.glareAngle * Math.PI / 180)
99
105
  };
100
- for (let i = 0; i < t; i++) for (let d = 0; d < e; d++) {
101
- const b = ee(d, i, e, t, S.value), u = f(b / a.bezel);
102
- let L = 0, P = 0, H = 0;
103
- if (b >= 0 && b < a.bezel) {
104
- const C = te(d, i, e, t, S.value), J = Z(Y(u)) * a.thickness * (1 - u) * a.refraction;
105
- L = -C.x * J, P = -C.y * J, c = Math.max(c, Math.abs(L), Math.abs(P));
106
- const se = f(C.x * m.x + C.y * m.y, 0, 1);
107
- H = Math.pow(se, 18) * a.specularOpacity;
106
+ for (let h = 0; h < t; h++) for (let f = 0; f < e; f++) {
107
+ const w = te(f, h, e, t, q.value), p = k(w / a.bezel);
108
+ let z = 0, D = 0, J = 0;
109
+ if (w >= 0 && w < a.bezel) {
110
+ const $ = ae(f, h, e, t), K = ee(Z(p)) * a.thickness * (1 - p) * a.refraction;
111
+ z = -$.x * K, D = -$.y * K, o = Math.max(o, Math.abs(z), Math.abs(D));
112
+ const ue = k($.x * _.x + $.y * _.y, 0, 1);
113
+ J = Math.pow(ue, 18) * a.specularOpacity;
108
114
  }
109
- l.push([L, P]);
110
- const B = (i * e + d) * 4, oe = Math.round(H * 255);
111
- o.data[B] = 255, o.data[B + 1] = 255, o.data[B + 2] = 255, o.data[B + 3] = oe;
115
+ i.push([z, D]);
116
+ const S = (h * e + f) * 4, le = Math.round(J * 255);
117
+ u.data[S] = 255, u.data[S + 1] = 255, u.data[S + 2] = 255, u.data[S + 3] = le;
112
118
  }
113
- l.forEach(([i, d], b) => {
114
- const u = b * 4;
115
- h.data[u] = Math.round(128 + i / c * 127), h.data[u + 1] = Math.round(128 + d / c * 127), h.data[u + 2] = 128, h.data[u + 3] = 255;
116
- }), r.putImageData(h, 0, 0), g.putImageData(o, 0, 0), F.value = N(n), U.value = N(s), z.value = c;
119
+ i.forEach(([h, f], w) => {
120
+ const p = w * 4;
121
+ s.data[p] = Math.round(128 + h / o * 127), s.data[p + 1] = Math.round(128 + f / o * 127), s.data[p + 2] = 128, s.data[p + 3] = 255;
122
+ }), c.putImageData(s, 0, 0), l.putImageData(u, 0, 0), E.value = V(n), F.value = V(r), U.value = o;
117
123
  }
118
- function V() {
124
+ function j() {
119
125
  const e = W();
120
- e && (_.value = Math.max(1, e.offsetWidth), y.value = Math.max(1, e.offsetHeight));
126
+ e && (m.value = Math.max(1, e.offsetWidth), M.value = Math.max(1, e.offsetHeight));
121
127
  }
122
- const j = x ? "rgba(255, 255, 255, 0.08)" : "rgba(255, 255, 255, 0.22)", S = R(() => {
128
+ const H = b ? "rgba(255, 255, 255, 0.08)" : "rgba(255, 255, 255, 0.22)", q = R(() => {
123
129
  if (a.radius !== 32) return a.radius;
124
130
  const e = a.asProps;
125
- return typeof e.rounded == "string" && e.rounded in A ? A[e.rounded] : a.radius;
126
- }), ae = /* @__PURE__ */ new Set([
131
+ return typeof e.rounded == "string" && e.rounded in G ? G[e.rounded] : a.radius;
132
+ }), ne = R(() => a.borderRadius || `${q.value}px`), re = /* @__PURE__ */ new Set([
127
133
  "primary",
128
134
  "secondary",
129
135
  "accent",
@@ -134,107 +140,114 @@ var ye = {
134
140
  "surface",
135
141
  "background"
136
142
  ]);
137
- function ne(e, t) {
138
- if (ae.has(e)) return `rgba(var(--v-theme-${e}), ${t})`;
143
+ function oe(e, t) {
144
+ if (re.has(e)) return `rgba(var(--v-theme-${e}), ${t})`;
139
145
  if (e.startsWith("#")) {
140
146
  const n = e.replace("#", "");
141
147
  return `rgba(${parseInt(n.slice(0, 2), 16)}, ${parseInt(n.slice(2, 4), 16)}, ${parseInt(n.slice(4, 6), 16)}, ${t})`;
142
148
  }
143
- return e.startsWith("rgb") ? e.replace("rgb(", "rgba(").replace(")", `, ${t})`) : j;
149
+ return e.startsWith("rgb") ? e.replace("rgb(", "rgba(").replace(")", `, ${t})`) : H;
144
150
  }
145
- const D = R(() => {
151
+ const O = R(() => {
146
152
  if (a.glassBackground !== void 0) return a.glassBackground;
147
153
  const e = a.asProps;
148
- return typeof e.color == "string" ? ne(e.color, 0.6) : j;
149
- }), $ = R(() => x ? `url(#${O}) blur(${a.blur}px)` : "blur(12px)"), re = R(() => ({
150
- borderRadius: `${S.value}px`,
151
- ...D.value !== void 0 ? { backgroundColor: D.value } : {},
152
- ...D.value !== void 0 ? { boxShadow: ["inset 0 0 0 1px rgba(255, 255, 255, 0.28)", "0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ") } : {},
153
- ...k.value && !T ? {
154
- backdropFilter: $.value,
155
- WebkitBackdropFilter: $.value
154
+ return typeof e.color == "string" ? oe(e.color, 0.6) : H;
155
+ }), I = R(() => a.fallbackOnly || !b ? "blur(12px)" : `url(#${T}) blur(${a.blur}px)`), se = R(() => ({
156
+ borderRadius: `${ne.value}`,
157
+ ...O.value !== void 0 ? { backgroundColor: O.value } : {},
158
+ ...O.value !== void 0 ? { boxShadow: ["inset 0 0 0 1px rgba(255, 255, 255, 0.28)", "0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ") } : {},
159
+ ...y.value && !A ? {
160
+ backdropFilter: I.value,
161
+ WebkitBackdropFilter: I.value
156
162
  } : {},
157
- ...k.value && T && !x ? {
158
- backdropFilter: $.value,
159
- WebkitBackdropFilter: $.value
163
+ ...y.value && A && !b ? {
164
+ backdropFilter: I.value,
165
+ WebkitBackdropFilter: I.value
160
166
  } : {}
161
167
  }));
162
- let E = null;
163
- return pe(async () => {
164
- if (await K(), x) {
165
- V(), q(), await K(), k.value = !0, E = new ResizeObserver(() => {
166
- V(), q(), k.value = !0;
168
+ let P = null;
169
+ return me(async () => {
170
+ if (await X(), b) {
171
+ j(), C(), await X(), y.value = !0, P = new ResizeObserver(() => {
172
+ j(), C(), y.value = !0;
167
173
  });
168
174
  const e = W();
169
- e && E.observe(e);
170
- } else k.value = !0;
171
- }), Me(() => [
172
- S.value,
175
+ e && P.observe(e);
176
+ } else y.value = !0;
177
+ }), xe(() => [
178
+ q.value,
173
179
  a.bezel,
174
180
  a.thickness,
175
181
  a.refraction,
176
182
  a.surface,
177
183
  a.specularOpacity,
178
- a.glareAngle
184
+ a.glareAngle,
185
+ a.fallbackOnly
179
186
  ], () => {
180
- x && q(), k.value = !0;
181
- }), he(() => {
182
- E?.disconnect();
183
- }), (e, t) => (Q(), le(me(p.as), fe({
187
+ b && C(), y.value = !0;
188
+ }), ge(() => {
189
+ P?.disconnect();
190
+ }), (e, t) => (L(), Q("div", {
184
191
  ref_key: "root",
185
- ref: v,
186
- class: "liquid-glass"
187
- }, p.asProps, { style: re.value }), {
188
- default: _e(() => [be(x) ? (Q(), ce("svg", ye, [w("defs", null, [w("filter", {
189
- id: O,
190
- "color-interpolation-filters": "sRGB",
191
- filterUnits: "userSpaceOnUse",
192
+ ref: x,
193
+ class: "liquid-glass",
194
+ style: ve(se.value)
195
+ }, [_e(b) ? (L(), Q("svg", we, [v("defs", null, [v("filter", {
196
+ id: T,
197
+ "color-interpolation-filters": "sRGB",
198
+ filterUnits: "userSpaceOnUse",
199
+ x: "0",
200
+ y: "0",
201
+ width: "10000",
202
+ height: "10000"
203
+ }, [
204
+ v("feImage", {
205
+ href: E.value,
206
+ x: "0",
207
+ y: "0",
208
+ width: m.value,
209
+ height: M.value,
210
+ result: "displacement_map"
211
+ }, null, 8, Re),
212
+ t[0] || (t[0] = v("feGaussianBlur", {
213
+ in: "displacement_map",
214
+ stdDeviation: "2",
215
+ result: "smooth_disp"
216
+ }, null, -1)),
217
+ v("feDisplacementMap", {
218
+ in: "SourceGraphic",
219
+ in2: "smooth_disp",
220
+ scale: U.value,
221
+ xChannelSelector: "R",
222
+ yChannelSelector: "G",
223
+ result: "refracted"
224
+ }, null, 8, Be),
225
+ v("feImage", {
226
+ href: F.value,
192
227
  x: "0",
193
228
  y: "0",
194
- width: "10000",
195
- height: "10000"
196
- }, [
197
- w("feImage", {
198
- href: F.value,
199
- x: "0",
200
- y: "0",
201
- width: _.value,
202
- height: y.value,
203
- result: "displacement_map"
204
- }, null, 8, xe),
205
- w("feDisplacementMap", {
206
- in: "SourceGraphic",
207
- in2: "displacement_map",
208
- scale: z.value,
209
- xChannelSelector: "R",
210
- yChannelSelector: "G",
211
- result: "refracted"
212
- }, null, 8, ke),
213
- w("feImage", {
214
- href: U.value,
215
- x: "0",
216
- y: "0",
217
- width: _.value,
218
- height: y.value,
219
- result: "specular"
220
- }, null, 8, we),
221
- t[0] || (t[0] = w("feBlend", {
222
- in: "refracted",
223
- in2: "specular",
224
- mode: "screen"
225
- }, null, -1))
226
- ])])])) : ue("", !0), ge(e.$slots, "default", {}, void 0, !0)]),
229
+ width: m.value,
230
+ height: M.value,
231
+ result: "specular"
232
+ }, null, 8, Ie),
233
+ t[1] || (t[1] = v("feBlend", {
234
+ in: "refracted",
235
+ in2: "specular",
236
+ mode: "screen"
237
+ }, null, -1))
238
+ ])])])) : ce("", !0), (L(), ie(ye(d.as), pe(he(d.asProps)), {
239
+ default: ke(() => [be(e.$slots, "default", {}, void 0, !0)]),
227
240
  _: 3
228
- }, 16, ["style"]));
241
+ }, 16))], 4));
229
242
  }
230
- }), Se = (p, a) => {
231
- const v = p.__vccOpts || p;
232
- for (const [_, y] of a) v[_] = y;
233
- return v;
234
- }, $e = /* @__PURE__ */ Se(Ie, [["__scopeId", "data-v-2c1a4b58"]]), Ce = { install(p) {
235
- p.component("Liqvued", $e);
243
+ }), $e = (d, a) => {
244
+ const x = d.__vccOpts || d;
245
+ for (const [m, M] of a) x[m] = M;
246
+ return x;
247
+ }, Ce = /* @__PURE__ */ $e(Se, [["__scopeId", "data-v-01d40041"]]), Oe = { install(d) {
248
+ d.component("Liqvued", Ce);
236
249
  } };
237
250
  export {
238
- $e as Liqvued,
239
- Ce as default
251
+ Ce as Liqvued,
252
+ Oe as default
240
253
  };
@@ -1 +1 @@
1
- (function(g,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],t):(g=typeof globalThis<"u"?globalThis:g||self,t(g.Liqvued={},g.Vue))})(this,function(g,t){Object.defineProperties(g,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var K={key:0,class:"liquid-glass__svg","aria-hidden":"true"},Q=["href","width","height"],X=["scale"],Y=["href","width","height"],Z=(0,t.defineComponent)({__name:"Liqvued",props:{as:{default:"div"},asProps:{default:()=>({})},radius:{default:32},bezel:{default:22},thickness:{default:42},refraction:{default:1},blur:{default:.4},surface:{default:"convex"},specularOpacity:{default:.45},glareAngle:{default:-60},glassBackground:{default:void 0}},setup(m){const n=m,v=(0,t.ref)(null),y=(0,t.ref)(1),k=(0,t.ref)(1),P=(0,t.ref)(""),V=(0,t.ref)(""),N=(0,t.ref)(1),x=CSS.supports?.("backdrop-filter","url(#x)")&&/Chrome/.test(navigator.userAgent),F=`${`lg-${Math.random().toString(36).slice(2)}`}-filter`,O=(0,t.inject)("_liqvued",!1);(0,t.provide)("_liqvued",!0);const w=(0,t.ref)(!1),U={0:0,sm:4,md:8,lg:12,xl:16,pill:9999,circle:9999,shaped:16};function z(){return v.value?v.value instanceof Element?v.value:v.value.$el??null:null}function h(e,a=0,r=1){return Math.max(a,Math.min(r,e))}function ae(e){return e=h(e),e*e*e*(e*(e*6-15)+10)}function S(e){return Math.pow(1-Math.pow(1-h(e),4),1/4)}function A(e){if(n.surface==="concave")return 1-S(e);if(n.surface==="lip"){const a=ae(e);return S(e)*(1-a)+(1-S(e))*a}return S(e)}function ne(e){return(A(e+.001)-A(e-.001))/(2*.001)}function re(e){const s=Math.atan(e),o=1/1.5*Math.sin(s),b=Math.asin(h(o,-1,1));return Math.tan(s-b)}function oe(e,a,r,s,o){const b=h(e,o,r-o),p=h(a,o,s-o),l=e-b,c=a-p,u=Math.hypot(l,c),M=Math.min(e,a,r-e,s-a);return l!==0&&c!==0?o-u:M}function le(e,a,r,s,o){const b=h(e,o,r-o),p=h(a,o,s-o);let l=e-b,c=a-p;if(l===0&&c===0){const M=e,d=r-e,f=a,_=s-a,i=Math.min(M,d,f,_);i===M?l=-1:i===d?l=1:i===f?c=-1:c=1}const u=Math.hypot(l,c)||1;return{x:l/u,y:c/u}}function W(e){return e.toDataURL("image/png")}function E(){const e=Math.max(1,Math.round(y.value)),a=Math.max(1,Math.round(k.value)),r=document.createElement("canvas");r.width=e,r.height=a;const s=document.createElement("canvas");s.width=e,s.height=a;const o=r.getContext("2d"),b=s.getContext("2d"),p=o.createImageData(e,a),l=b.createImageData(e,a),c=[];let u=1;const M={x:Math.cos(n.glareAngle*Math.PI/180),y:Math.sin(n.glareAngle*Math.PI/180)};for(let d=0;d<a;d++)for(let f=0;f<e;f++){const _=oe(f,d,e,a,I.value),i=h(_/n.bezel);let T=0,D=0,H=0;if(_>=0&&_<n.bezel){const q=le(f,d,e,a,I.value),J=re(ne(i))*n.thickness*(1-i)*n.refraction;T=-q.x*J,D=-q.y*J,u=Math.max(u,Math.abs(T),Math.abs(D));const de=h(q.x*M.x+q.y*M.y,0,1);H=Math.pow(de,18)*n.specularOpacity}c.push([T,D]);const $=(d*e+f)*4,ue=Math.round(H*255);l.data[$]=255,l.data[$+1]=255,l.data[$+2]=255,l.data[$+3]=ue}c.forEach(([d,f],_)=>{const i=_*4;p.data[i]=Math.round(128+d/u*127),p.data[i+1]=Math.round(128+f/u*127),p.data[i+2]=128,p.data[i+3]=255}),o.putImageData(p,0,0),b.putImageData(l,0,0),P.value=W(r),V.value=W(s),N.value=u}function j(){const e=z();e&&(y.value=Math.max(1,e.offsetWidth),k.value=Math.max(1,e.offsetHeight))}const G=x?"rgba(255, 255, 255, 0.08)":"rgba(255, 255, 255, 0.22)",I=(0,t.computed)(()=>{if(n.radius!==32)return n.radius;const e=n.asProps;return typeof e.rounded=="string"&&e.rounded in U?U[e.rounded]:n.radius}),se=new Set(["primary","secondary","accent","info","warning","error","success","surface","background"]);function ce(e,a){if(se.has(e))return`rgba(var(--v-theme-${e}), ${a})`;if(e.startsWith("#")){const r=e.replace("#","");return`rgba(${parseInt(r.slice(0,2),16)}, ${parseInt(r.slice(2,4),16)}, ${parseInt(r.slice(4,6),16)}, ${a})`}return e.startsWith("rgb")?e.replace("rgb(","rgba(").replace(")",`, ${a})`):G}const C=(0,t.computed)(()=>{if(n.glassBackground!==void 0)return n.glassBackground;const e=n.asProps;return typeof e.color=="string"?ce(e.color,.6):G}),B=(0,t.computed)(()=>x?`url(#${F}) blur(${n.blur}px)`:"blur(12px)"),ie=(0,t.computed)(()=>({borderRadius:`${I.value}px`,...C.value!==void 0?{backgroundColor:C.value}:{},...C.value!==void 0?{boxShadow:["inset 0 0 0 1px rgba(255, 255, 255, 0.28)","0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ")}:{},...w.value&&!O?{backdropFilter:B.value,WebkitBackdropFilter:B.value}:{},...w.value&&O&&!x?{backdropFilter:B.value,WebkitBackdropFilter:B.value}:{}}));let R=null;return(0,t.onMounted)(async()=>{if(await(0,t.nextTick)(),x){j(),E(),await(0,t.nextTick)(),w.value=!0,R=new ResizeObserver(()=>{j(),E(),w.value=!0});const e=z();e&&R.observe(e)}else w.value=!0}),(0,t.watch)(()=>[I.value,n.bezel,n.thickness,n.refraction,n.surface,n.specularOpacity,n.glareAngle],()=>{x&&E(),w.value=!0}),(0,t.onBeforeUnmount)(()=>{R?.disconnect()}),(e,a)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(m.as),(0,t.mergeProps)({ref_key:"root",ref:v,class:"liquid-glass"},m.asProps,{style:ie.value}),{default:(0,t.withCtx)(()=>[(0,t.unref)(x)?((0,t.openBlock)(),(0,t.createElementBlock)("svg",K,[(0,t.createElementVNode)("defs",null,[(0,t.createElementVNode)("filter",{id:F,"color-interpolation-filters":"sRGB",filterUnits:"userSpaceOnUse",x:"0",y:"0",width:"10000",height:"10000"},[(0,t.createElementVNode)("feImage",{href:P.value,x:"0",y:"0",width:y.value,height:k.value,result:"displacement_map"},null,8,Q),(0,t.createElementVNode)("feDisplacementMap",{in:"SourceGraphic",in2:"displacement_map",scale:N.value,xChannelSelector:"R",yChannelSelector:"G",result:"refracted"},null,8,X),(0,t.createElementVNode)("feImage",{href:V.value,x:"0",y:"0",width:y.value,height:k.value,result:"specular"},null,8,Y),a[0]||(a[0]=(0,t.createElementVNode)("feBlend",{in:"refracted",in2:"specular",mode:"screen"},null,-1))])])])):(0,t.createCommentVNode)("",!0),(0,t.renderSlot)(e.$slots,"default",{},void 0,!0)]),_:3},16,["style"]))}}),ee=(m,n)=>{const v=m.__vccOpts||m;for(const[y,k]of n)v[y]=k;return v},L=ee(Z,[["__scopeId","data-v-2c1a4b58"]]),te={install(m){m.component("Liqvued",L)}};g.Liqvued=L,g.default=te});
1
+ (function(f,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],t):(f=typeof globalThis<"u"?globalThis:f||self,t(f.Liqvued={},f.Vue))})(this,function(f,t){Object.defineProperties(f,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var K={key:0,class:"liquid-glass__svg","aria-hidden":"true"},Q=["href","width","height"],X=["scale"],Y=["href","width","height"],Z=(0,t.defineComponent)({__name:"Liqvued",props:{as:{default:"div"},asProps:{default:()=>({})},radius:{default:32},borderRadius:{},bezel:{default:22},thickness:{default:42},refraction:{default:1},blur:{default:.4},surface:{default:"convex"},specularOpacity:{default:.45},glareAngle:{default:-60},glassBackground:{default:void 0},fallbackOnly:{type:Boolean,default:!1}},setup(h){const n=h,k=(0,t.ref)(null),v=(0,t.ref)(1),M=(0,t.ref)(1),V=(0,t.ref)(""),L=(0,t.ref)(""),N=(0,t.ref)(1),b=CSS.supports?.("backdrop-filter","url(#x)")&&/Chrome/.test(navigator.userAgent),z=`${`lg-${Math.random().toString(36).slice(2)}`}-filter`,D=(0,t.inject)("_liqvued",!1);(0,t.provide)("_liqvued",!0);const y=(0,t.ref)(!1),F={0:0,sm:4,md:8,lg:12,xl:16,pill:9999,circle:9999,shaped:16};function U(){return k.value}function x(e,a=0,r=1){return Math.max(a,Math.min(r,e))}function ae(e){return e=x(e),e*e*e*(e*(e*6-15)+10)}function B(e){return Math.pow(1-Math.pow(1-x(e),4),1/4)}function A(e){if(n.surface==="concave")return 1-B(e);if(n.surface==="lip"){const a=ae(e);return B(e)*(1-a)+(1-B(e))*a}return B(e)}function ne(e){return(A(e+.001)-A(e-.001))/(2*.001)}function re(e){const o=Math.atan(e),d=1/1.5*Math.sin(o),i=Math.asin(x(d,-1,1));return Math.tan(o-i)}function oe(e,a,r,o,d){const i=Math.min(d,Math.min(r,o)/2),s=r/2-i,c=o/2-i,u=Math.abs(e-r/2)-s,l=Math.abs(a-o/2)-c;return-(Math.min(Math.max(u,l),0)+Math.hypot(Math.max(u,0),Math.max(l,0))-i)}function le(e,a,r,o){const d=Math.min(e,r-1-e),i=Math.min(a,o-1-a),s=4,c=Math.pow(d,s)+Math.pow(i,s);let u,l;c>1e-10?(u=(e>r/2?1:-1)*(Math.pow(i,s)/c),l=(a>o/2?1:-1)*(Math.pow(d,s)/c)):(u=e>r/2?1:-1,l=a>o/2?1:-1);const _=Math.hypot(u,l);return _<1e-10?{x:0,y:-1}:{x:u/_,y:l/_}}function G(e){return e.toDataURL("image/png")}function E(){if(n.fallbackOnly)return;const e=Math.max(1,Math.round(v.value)),a=Math.max(1,Math.round(M.value)),r=document.createElement("canvas");r.width=e,r.height=a;const o=document.createElement("canvas");o.width=e,o.height=a;const d=r.getContext("2d"),i=o.getContext("2d"),s=d.createImageData(e,a),c=i.createImageData(e,a),u=[];let l=1;const _={x:Math.cos(n.glareAngle*Math.PI/180),y:Math.sin(n.glareAngle*Math.PI/180)};for(let p=0;p<a;p++)for(let m=0;m<e;m++){const w=oe(m,p,e,a,$.value),g=x(w/n.bezel);let O=0,P=0,H=0;if(w>=0&&w<n.bezel){const I=le(m,p,e,a),J=re(ne(g))*n.thickness*(1-g)*n.refraction;O=-I.x*J,P=-I.y*J,l=Math.max(l,Math.abs(O),Math.abs(P));const fe=x(I.x*_.x+I.y*_.y,0,1);H=Math.pow(fe,18)*n.specularOpacity}u.push([O,P]);const R=(p*e+m)*4,de=Math.round(H*255);c.data[R]=255,c.data[R+1]=255,c.data[R+2]=255,c.data[R+3]=de}u.forEach(([p,m],w)=>{const g=w*4;s.data[g]=Math.round(128+p/l*127),s.data[g+1]=Math.round(128+m/l*127),s.data[g+2]=128,s.data[g+3]=255}),d.putImageData(s,0,0),i.putImageData(c,0,0),V.value=G(r),L.value=G(o),N.value=l}function W(){const e=U();e&&(v.value=Math.max(1,e.offsetWidth),M.value=Math.max(1,e.offsetHeight))}const j=b?"rgba(255, 255, 255, 0.08)":"rgba(255, 255, 255, 0.22)",$=(0,t.computed)(()=>{if(n.radius!==32)return n.radius;const e=n.asProps;return typeof e.rounded=="string"&&e.rounded in F?F[e.rounded]:n.radius}),se=(0,t.computed)(()=>n.borderRadius||`${$.value}px`),ie=new Set(["primary","secondary","accent","info","warning","error","success","surface","background"]);function ce(e,a){if(ie.has(e))return`rgba(var(--v-theme-${e}), ${a})`;if(e.startsWith("#")){const r=e.replace("#","");return`rgba(${parseInt(r.slice(0,2),16)}, ${parseInt(r.slice(2,4),16)}, ${parseInt(r.slice(4,6),16)}, ${a})`}return e.startsWith("rgb")?e.replace("rgb(","rgba(").replace(")",`, ${a})`):j}const q=(0,t.computed)(()=>{if(n.glassBackground!==void 0)return n.glassBackground;const e=n.asProps;return typeof e.color=="string"?ce(e.color,.6):j}),S=(0,t.computed)(()=>n.fallbackOnly||!b?"blur(12px)":`url(#${z}) blur(${n.blur}px)`),ue=(0,t.computed)(()=>({borderRadius:`${se.value}`,...q.value!==void 0?{backgroundColor:q.value}:{},...q.value!==void 0?{boxShadow:["inset 0 0 0 1px rgba(255, 255, 255, 0.28)","0 18px 48px rgba(0, 0, 0, 0.18)"].join(", ")}:{},...y.value&&!D?{backdropFilter:S.value,WebkitBackdropFilter:S.value}:{},...y.value&&D&&!b?{backdropFilter:S.value,WebkitBackdropFilter:S.value}:{}}));let C=null;return(0,t.onMounted)(async()=>{if(await(0,t.nextTick)(),b){W(),E(),await(0,t.nextTick)(),y.value=!0,C=new ResizeObserver(()=>{W(),E(),y.value=!0});const e=U();e&&C.observe(e)}else y.value=!0}),(0,t.watch)(()=>[$.value,n.bezel,n.thickness,n.refraction,n.surface,n.specularOpacity,n.glareAngle,n.fallbackOnly],()=>{b&&E(),y.value=!0}),(0,t.onBeforeUnmount)(()=>{C?.disconnect()}),(e,a)=>((0,t.openBlock)(),(0,t.createElementBlock)("div",{ref_key:"root",ref:k,class:"liquid-glass",style:(0,t.normalizeStyle)(ue.value)},[(0,t.unref)(b)?((0,t.openBlock)(),(0,t.createElementBlock)("svg",K,[(0,t.createElementVNode)("defs",null,[(0,t.createElementVNode)("filter",{id:z,"color-interpolation-filters":"sRGB",filterUnits:"userSpaceOnUse",x:"0",y:"0",width:"10000",height:"10000"},[(0,t.createElementVNode)("feImage",{href:V.value,x:"0",y:"0",width:v.value,height:M.value,result:"displacement_map"},null,8,Q),a[0]||(a[0]=(0,t.createElementVNode)("feGaussianBlur",{in:"displacement_map",stdDeviation:"2",result:"smooth_disp"},null,-1)),(0,t.createElementVNode)("feDisplacementMap",{in:"SourceGraphic",in2:"smooth_disp",scale:N.value,xChannelSelector:"R",yChannelSelector:"G",result:"refracted"},null,8,X),(0,t.createElementVNode)("feImage",{href:L.value,x:"0",y:"0",width:v.value,height:M.value,result:"specular"},null,8,Y),a[1]||(a[1]=(0,t.createElementVNode)("feBlend",{in:"refracted",in2:"specular",mode:"screen"},null,-1))])])])):(0,t.createCommentVNode)("",!0),((0,t.openBlock)(),(0,t.createBlock)((0,t.resolveDynamicComponent)(h.as),(0,t.normalizeProps)((0,t.guardReactiveProps)(h.asProps)),{default:(0,t.withCtx)(()=>[(0,t.renderSlot)(e.$slots,"default",{},void 0,!0)]),_:3},16))],4))}}),ee=(h,n)=>{const k=h.__vccOpts||h;for(const[v,M]of n)k[v]=M;return k},T=ee(Z,[["__scopeId","data-v-01d40041"]]),te={install(h){h.component("Liqvued",T)}};f.Liqvued=T,f.default=te});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "liqvued",
3
3
  "types": "./index.d.ts",
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "description": "Liquid-glass for Vue",
6
6
  "exports": {
7
7
  ".": {
package/src/Liqvued.vue CHANGED
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import {
3
3
  type Component,
4
- type ComponentPublicInstance,
5
4
  computed,
6
5
  inject,
7
6
  nextTick,
@@ -17,6 +16,7 @@ const props = withDefaults(defineProps<{
17
16
  as?: string | Component
18
17
  asProps?: object
19
18
  radius?: number
19
+ borderRadius?: string
20
20
  bezel?: number
21
21
  thickness?: number
22
22
  refraction?: number
@@ -25,9 +25,11 @@ const props = withDefaults(defineProps<{
25
25
  specularOpacity?: number
26
26
  glareAngle?: number
27
27
  glassBackground?: string
28
+ fallbackOnly?: boolean
28
29
  }>(), {
29
30
  as: 'div',
30
31
  asProps: () => ({}),
32
+ fallbackOnly: false,
31
33
  radius: 32,
32
34
  bezel: 22,
33
35
  thickness: 42,
@@ -39,7 +41,7 @@ const props = withDefaults(defineProps<{
39
41
  glassBackground: undefined,
40
42
  })
41
43
 
42
- const root = ref<HTMLElement | ComponentPublicInstance | null>(null)
44
+ const root = ref<HTMLElement | null>(null)
43
45
 
44
46
  const width = ref(1)
45
47
  const height = ref(1)
@@ -54,6 +56,11 @@ const id = `lg-${Math.random().toString(36).slice(2)}`
54
56
  const filterId = `${id}-filter`
55
57
  const isNested = inject('_liqvued', false)
56
58
  provide('_liqvued', true)
59
+
60
+ if (import.meta.env.DEV && typeof props.as !== 'string') {
61
+ console.warn('[Liqvued] `as` prop should be a string (HTML tag name). For Vue components, use Liqvued as a wrapper instead of `as`.')
62
+ }
63
+
57
64
  const glassReady = ref(false)
58
65
 
59
66
  const roundedMap: Record<string, number> = {
@@ -68,9 +75,7 @@ const roundedMap: Record<string, number> = {
68
75
  }
69
76
 
70
77
  function getRootEl(): HTMLElement | null {
71
- if (!root.value) return null
72
- if (root.value instanceof Element) return root.value as HTMLElement
73
- return (root.value as ComponentPublicInstance).$el ?? null
78
+ return root.value
74
79
  }
75
80
 
76
81
  function clamp(v: number, min = 0, max = 1) {
@@ -120,20 +125,16 @@ function distanceToRoundedRectEdge(
120
125
  h: number,
121
126
  r: number,
122
127
  ) {
123
- const cx = clamp(x, r, w - r)
124
- const cy = clamp(y, r, h - r)
128
+ const hr = Math.min(r, Math.min(w, h) / 2)
129
+ const hw = w / 2 - hr
130
+ const hh = h / 2 - hr
131
+ const ax = Math.abs(x - w / 2) - hw
132
+ const ay = Math.abs(y - h / 2) - hh
125
133
 
126
- const dx = x - cx
127
- const dy = y - cy
128
-
129
- const cornerDistance = Math.hypot(dx, dy)
130
- const edgeDistance = Math.min(x, y, w - x, h - y)
131
-
132
- if (dx !== 0 && dy !== 0) {
133
- return r - cornerDistance
134
- }
134
+ const outsidePart = Math.hypot(Math.max(ax, 0), Math.max(ay, 0))
135
+ const insidePart = Math.min(Math.max(ax, ay), 0)
135
136
 
136
- return edgeDistance
137
+ return -(insidePart + outsidePart - hr)
137
138
  }
138
139
 
139
140
  function normalFromRoundedRect(
@@ -141,33 +142,28 @@ function normalFromRoundedRect(
141
142
  y: number,
142
143
  w: number,
143
144
  h: number,
144
- r: number,
145
145
  ) {
146
- const cx = clamp(x, r, w - r)
147
- const cy = clamp(y, r, h - r)
148
-
149
- let nx = x - cx
150
- let ny = y - cy
151
-
152
- if (nx === 0 && ny === 0) {
153
- const left = x
154
- const right = w - x
155
- const top = y
156
- const bottom = h - y
157
- const m = Math.min(left, right, top, bottom)
158
-
159
- if (m === left) nx = -1
160
- else if (m === right) nx = 1
161
- else if (m === top) ny = -1
162
- else ny = 1
163
- }
146
+ const dx = Math.min(x, w - 1 - x)
147
+ const dy = Math.min(y, h - 1 - y)
148
+
149
+ const p = 4
150
+ const wsum = Math.pow(dx, p) + Math.pow(dy, p)
164
151
 
165
- const len = Math.hypot(nx, ny) || 1
152
+ let nx: number
153
+ let ny: number
166
154
 
167
- return {
168
- x: nx / len,
169
- y: ny / len,
155
+ if (wsum > 1e-10) {
156
+ nx = (x > w / 2 ? 1 : -1) * (Math.pow(dy, p) / wsum)
157
+ ny = (y > h / 2 ? 1 : -1) * (Math.pow(dx, p) / wsum)
158
+ } else {
159
+ nx = x > w / 2 ? 1 : -1
160
+ ny = y > h / 2 ? 1 : -1
170
161
  }
162
+
163
+ const len = Math.hypot(nx, ny)
164
+ if (len < 1e-10) return { x: 0, y: -1 }
165
+
166
+ return { x: nx / len, y: ny / len }
171
167
  }
172
168
 
173
169
  function canvasToUrl(canvas: HTMLCanvasElement) {
@@ -175,6 +171,10 @@ function canvasToUrl(canvas: HTMLCanvasElement) {
175
171
  }
176
172
 
177
173
  function buildMaps() {
174
+ if (props.fallbackOnly) {
175
+ return
176
+ }
177
+
178
178
  const w = Math.max(1, Math.round(width.value))
179
179
  const h = Math.max(1, Math.round(height.value))
180
180
 
@@ -210,7 +210,7 @@ function buildMaps() {
210
210
  let spec = 0
211
211
 
212
212
  if (dist >= 0 && dist < props.bezel) {
213
- const n = normalFromRoundedRect(x, y, w, h, resolvedRadius.value)
213
+ const n = normalFromRoundedRect(x, y, w, h)
214
214
  const slope = derivative(t)
215
215
  const bend = refractSlope(slope)
216
216
 
@@ -276,6 +276,10 @@ const resolvedRadius = computed(() => {
276
276
  return props.radius
277
277
  })
278
278
 
279
+ const borderRadios = computed(() => {
280
+ return props.borderRadius || `${resolvedRadius.value}px`
281
+ })
282
+
279
283
  const vuetifyThemeColors = new Set([
280
284
  'primary', 'secondary', 'accent', 'info', 'warning', 'error', 'success',
281
285
  'surface', 'background',
@@ -308,14 +312,14 @@ const glassBgValue = computed(() => {
308
312
  })
309
313
 
310
314
  const backdropFilter = computed(() => {
311
- if (supportsLiquidGlass) {
312
- return `url(#${filterId}) blur(${props.blur}px)`
315
+ if (props.fallbackOnly || !supportsLiquidGlass) {
316
+ return 'blur(12px)'
313
317
  }
314
- return 'blur(12px)'
318
+ return `url(#${filterId}) blur(${props.blur}px)`
315
319
  })
316
320
 
317
321
  const rootStyle = computed(() => ({
318
- borderRadius: `${resolvedRadius.value}px`,
322
+ borderRadius: `${borderRadios.value}`,
319
323
  ...(glassBgValue.value !== undefined
320
324
  ? { backgroundColor: glassBgValue.value }
321
325
  : {}),
@@ -374,6 +378,7 @@ watch(
374
378
  props.surface,
375
379
  props.specularOpacity,
376
380
  props.glareAngle,
381
+ props.fallbackOnly,
377
382
  ],
378
383
  () => {
379
384
  if (supportsLiquidGlass) {
@@ -386,15 +391,12 @@ watch(
386
391
  onBeforeUnmount(() => {
387
392
  ro?.disconnect()
388
393
  })
389
-
390
394
  </script>
391
395
 
392
396
  <template>
393
- <component
394
- :is="as"
397
+ <div
395
398
  ref="root"
396
399
  class="liquid-glass"
397
- v-bind="asProps"
398
400
  :style="rootStyle"
399
401
  >
400
402
  <svg
@@ -421,9 +423,15 @@ onBeforeUnmount(() => {
421
423
  result="displacement_map"
422
424
  />
423
425
 
426
+ <feGaussianBlur
427
+ in="displacement_map"
428
+ stdDeviation="2"
429
+ result="smooth_disp"
430
+ />
431
+
424
432
  <feDisplacementMap
425
433
  in="SourceGraphic"
426
- in2="displacement_map"
434
+ in2="smooth_disp"
427
435
  :scale="scale"
428
436
  xChannelSelector="R"
429
437
  yChannelSelector="G"
@@ -444,8 +452,13 @@ onBeforeUnmount(() => {
444
452
  </defs>
445
453
  </svg>
446
454
 
447
- <slot />
448
- </component>
455
+ <component
456
+ :is="as"
457
+ v-bind="asProps"
458
+ >
459
+ <slot />
460
+ </component>
461
+ </div>
449
462
  </template>
450
463
 
451
464
  <style scoped>