@shulya/liquid-glass 1.0.4 → 1.0.5
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/dist/core.cjs +76 -23
- package/dist/core.mjs +76 -23
- package/dist/react.cjs +7 -3
- package/dist/react.mjs +7 -3
- package/package.json +1 -1
- package/types/index.d.ts +8 -0
package/dist/core.cjs
CHANGED
|
@@ -13,14 +13,16 @@ const OPTICAL_DEFAULTS = exports.OPTICAL_DEFAULTS = Object.freeze({
|
|
|
13
13
|
ior: 1.46,
|
|
14
14
|
height: 18,
|
|
15
15
|
frosted: 0,
|
|
16
|
-
fusion: true
|
|
16
|
+
fusion: true,
|
|
17
|
+
dpr: 0
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
const OPTICAL_LIMITS = exports.OPTICAL_LIMITS = Object.freeze({
|
|
20
21
|
rim: Object.freeze({ min: 4, max: 48 }),
|
|
21
22
|
ior: Object.freeze({ min: 1.05, max: 2.20 }),
|
|
22
23
|
height: Object.freeze({ min: 2, max: 50 }),
|
|
23
|
-
frosted: Object.freeze({ min: 0, max: 32 })
|
|
24
|
+
frosted: Object.freeze({ min: 0, max: 32 }),
|
|
25
|
+
dpr: Object.freeze({ min: 0.5, max: 3.0 })
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
function clampNumber(val, fallback, min, max) {
|
|
@@ -57,7 +59,11 @@ exports.normalizeOptions = function normalizeOptions(options = {}) {
|
|
|
57
59
|
OPTICAL_LIMITS.frosted.min,
|
|
58
60
|
OPTICAL_LIMITS.frosted.max
|
|
59
61
|
),
|
|
60
|
-
fusion: options.fusion !== undefined ? Boolean(options.fusion) : OPTICAL_DEFAULTS.fusion
|
|
62
|
+
fusion: options.fusion !== undefined ? Boolean(options.fusion) : OPTICAL_DEFAULTS.fusion,
|
|
63
|
+
dpr:
|
|
64
|
+
typeof options.dpr === 'number' && options.dpr > 0
|
|
65
|
+
? clampNumber(options.dpr, 1, OPTICAL_LIMITS.dpr.min, OPTICAL_LIMITS.dpr.max)
|
|
66
|
+
: 0
|
|
61
67
|
};
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -477,8 +483,17 @@ function getReadableStrokeColor(borderCol, fallback = 'rgba(148, 163, 184, 0.55)
|
|
|
477
483
|
return fallback;
|
|
478
484
|
}
|
|
479
485
|
|
|
480
|
-
function getDevicePixelRatio() {
|
|
481
|
-
|
|
486
|
+
function getDevicePixelRatio(customDpr = 0, elementWidth = 0) {
|
|
487
|
+
if (typeof customDpr === 'number' && customDpr > 0) {
|
|
488
|
+
return Math.max(0.5, Math.min(3.0, customDpr));
|
|
489
|
+
}
|
|
490
|
+
if (typeof window === 'undefined') return 1;
|
|
491
|
+
const sysDpr = window.devicePixelRatio || 1;
|
|
492
|
+
// Automatically use 1.0 for wide elements (headers, banners >= 700px) to guarantee 120 FPS
|
|
493
|
+
if (elementWidth >= 700) {
|
|
494
|
+
return 1;
|
|
495
|
+
}
|
|
496
|
+
return Math.min(sysDpr, 1.5);
|
|
482
497
|
}
|
|
483
498
|
|
|
484
499
|
function ensureStageBuffers() {
|
|
@@ -721,25 +736,50 @@ function captureViewportStage(neededBlur) {
|
|
|
721
736
|
|
|
722
737
|
stageCtx.restore();
|
|
723
738
|
|
|
739
|
+
let supportsCanvasFilterBlur = null;
|
|
740
|
+
function checkCanvasFilterBlur() {
|
|
741
|
+
if (supportsCanvasFilterBlur !== null) return supportsCanvasFilterBlur;
|
|
742
|
+
if (typeof document === 'undefined') return false;
|
|
743
|
+
try {
|
|
744
|
+
const testC = document.createElement('canvas');
|
|
745
|
+
testC.width = 4;
|
|
746
|
+
testC.height = 4;
|
|
747
|
+
const testCtx = testC.getContext('2d');
|
|
748
|
+
if (!testCtx || typeof testCtx.filter !== 'string') {
|
|
749
|
+
return (supportsCanvasFilterBlur = false);
|
|
750
|
+
}
|
|
751
|
+
testCtx.fillStyle = '#000';
|
|
752
|
+
testCtx.fillRect(0, 0, 4, 4);
|
|
753
|
+
testCtx.filter = 'blur(2px)';
|
|
754
|
+
testCtx.fillStyle = '#fff';
|
|
755
|
+
testCtx.fillRect(0, 0, 2, 2);
|
|
756
|
+
testCtx.filter = 'none';
|
|
757
|
+
const imgData = testCtx.getImageData(0, 0, 4, 4).data;
|
|
758
|
+
supportsCanvasFilterBlur = imgData[15 * 4] > 0 || imgData[14 * 4] > 0 || imgData[11 * 4] > 0;
|
|
759
|
+
return supportsCanvasFilterBlur;
|
|
760
|
+
} catch {
|
|
761
|
+
return (supportsCanvasFilterBlur = false);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
724
765
|
// 5. Blur buffer for frosted glass (Robust across all mobile WebKit & Blink engines)
|
|
725
766
|
if (neededBlur > 0) {
|
|
726
767
|
lastFrostedRadius = neededBlur;
|
|
727
768
|
blurCtx.clearRect(0, 0, bw, bh);
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const
|
|
736
|
-
const
|
|
769
|
+
|
|
770
|
+
if (checkCanvasFilterBlur()) {
|
|
771
|
+
blurCtx.filter = `blur(${Math.round(neededBlur * dpr)}px)`;
|
|
772
|
+
blurCtx.drawImage(stageCanvas, 0, 0);
|
|
773
|
+
blurCtx.filter = 'none';
|
|
774
|
+
} else {
|
|
775
|
+
// Hardware-accelerated multi-step pyramid downscale/upscale blur for iOS Safari and mobile WebKit
|
|
776
|
+
const factor = Math.max(0.04, 1.0 / (1.0 + neededBlur * 0.45));
|
|
777
|
+
const pw = Math.max(4, Math.round(bw * factor));
|
|
778
|
+
const ph = Math.max(4, Math.round(bh * factor));
|
|
737
779
|
blurCtx.imageSmoothingEnabled = true;
|
|
738
780
|
blurCtx.imageSmoothingQuality = 'high';
|
|
739
|
-
blurCtx.drawImage(stageCanvas, 0, 0,
|
|
740
|
-
blurCtx.drawImage(blurCanvas, 0, 0,
|
|
741
|
-
} else {
|
|
742
|
-
blurCtx.filter = 'none';
|
|
781
|
+
blurCtx.drawImage(stageCanvas, 0, 0, pw, ph);
|
|
782
|
+
blurCtx.drawImage(blurCanvas, 0, 0, pw, ph, 0, 0, bw, bh);
|
|
743
783
|
}
|
|
744
784
|
}
|
|
745
785
|
}
|
|
@@ -748,7 +788,7 @@ function captureViewportStage(neededBlur) {
|
|
|
748
788
|
* Fast Isolated Lens Renderer (Takes ~0.12 ms using precomputed TypedArray LUT!)
|
|
749
789
|
*/
|
|
750
790
|
function renderIsolatedController(ctrl, rect) {
|
|
751
|
-
const dpr = getDevicePixelRatio();
|
|
791
|
+
const dpr = getDevicePixelRatio(ctrl.options.dpr, rect.width);
|
|
752
792
|
const w = Math.max(16, Math.round(rect.width * dpr));
|
|
753
793
|
const h = Math.max(16, Math.round(rect.height * dpr));
|
|
754
794
|
const rad = ctrl._getRadius();
|
|
@@ -946,10 +986,12 @@ function renderAllActiveLenses() {
|
|
|
946
986
|
h: rect.height,
|
|
947
987
|
borderRadius: ctrl._getRadius(),
|
|
948
988
|
fusion: true,
|
|
949
|
-
tint: ctrl._tint
|
|
989
|
+
tint: ctrl._tint,
|
|
990
|
+
dpr: ctrl.options.dpr
|
|
950
991
|
}));
|
|
951
992
|
|
|
952
|
-
const
|
|
993
|
+
const maxClusterWidth = Math.max(...group.map(g => g.rect.width));
|
|
994
|
+
const dpr = getDevicePixelRatio(primaryCtrl.options.dpr, maxClusterWidth);
|
|
953
995
|
const box = renderFusedCluster(
|
|
954
996
|
clusterOverlayCtx,
|
|
955
997
|
sourceCtx,
|
|
@@ -964,6 +1006,17 @@ function renderAllActiveLenses() {
|
|
|
964
1006
|
if (box && clusterOverlayCanvas) {
|
|
965
1007
|
clusterOverlayCanvas.style.display = 'block';
|
|
966
1008
|
clusterOverlayCanvas.style.filter = 'none';
|
|
1009
|
+
|
|
1010
|
+
// Match z-index to highest element in the cluster so it never pops above floating menus/controls
|
|
1011
|
+
let maxZ = 20;
|
|
1012
|
+
for (const item of group) {
|
|
1013
|
+
if (item.ctrl && item.ctrl.element) {
|
|
1014
|
+
const z = parseInt(window.getComputedStyle(item.ctrl.element).zIndex, 10);
|
|
1015
|
+
if (!Number.isNaN(z) && z > maxZ) maxZ = z;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
clusterOverlayCanvas.style.zIndex = String(maxZ);
|
|
1019
|
+
|
|
967
1020
|
clusterOverlayCanvas.style.transform = `translate3d(${box.cMinX / dpr}px, ${box.cMinY / dpr}px, 0)`;
|
|
968
1021
|
clusterOverlayCanvas.style.width = `${box.boxW / dpr}px`;
|
|
969
1022
|
clusterOverlayCanvas.style.height = `${box.boxH / dpr}px`;
|
|
@@ -1136,11 +1189,11 @@ const LiquidGlassController = exports.LiquidGlassController = class LiquidGlassC
|
|
|
1136
1189
|
changed = true;
|
|
1137
1190
|
}
|
|
1138
1191
|
|
|
1139
|
-
for (const key of ['rim', 'ior', 'height', 'frosted', 'fusion']) {
|
|
1192
|
+
for (const key of ['rim', 'ior', 'height', 'frosted', 'fusion', 'dpr']) {
|
|
1140
1193
|
if (merged[key] !== this.options[key]) {
|
|
1141
1194
|
this.options[key] = merged[key];
|
|
1142
1195
|
changed = true;
|
|
1143
|
-
if (key === 'rim' || key === 'ior' || key === 'height') {
|
|
1196
|
+
if (key === 'rim' || key === 'ior' || key === 'height' || key === 'dpr') {
|
|
1144
1197
|
lutChanged = true;
|
|
1145
1198
|
}
|
|
1146
1199
|
}
|
package/dist/core.mjs
CHANGED
|
@@ -13,14 +13,16 @@ export const OPTICAL_DEFAULTS = Object.freeze({
|
|
|
13
13
|
ior: 1.46,
|
|
14
14
|
height: 18,
|
|
15
15
|
frosted: 0,
|
|
16
|
-
fusion: true
|
|
16
|
+
fusion: true,
|
|
17
|
+
dpr: 0
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
export const OPTICAL_LIMITS = Object.freeze({
|
|
20
21
|
rim: Object.freeze({ min: 4, max: 48 }),
|
|
21
22
|
ior: Object.freeze({ min: 1.05, max: 2.20 }),
|
|
22
23
|
height: Object.freeze({ min: 2, max: 50 }),
|
|
23
|
-
frosted: Object.freeze({ min: 0, max: 32 })
|
|
24
|
+
frosted: Object.freeze({ min: 0, max: 32 }),
|
|
25
|
+
dpr: Object.freeze({ min: 0.5, max: 3.0 })
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
function clampNumber(val, fallback, min, max) {
|
|
@@ -57,7 +59,11 @@ export function normalizeOptions(options = {}) {
|
|
|
57
59
|
OPTICAL_LIMITS.frosted.min,
|
|
58
60
|
OPTICAL_LIMITS.frosted.max
|
|
59
61
|
),
|
|
60
|
-
fusion: options.fusion !== undefined ? Boolean(options.fusion) : OPTICAL_DEFAULTS.fusion
|
|
62
|
+
fusion: options.fusion !== undefined ? Boolean(options.fusion) : OPTICAL_DEFAULTS.fusion,
|
|
63
|
+
dpr:
|
|
64
|
+
typeof options.dpr === 'number' && options.dpr > 0
|
|
65
|
+
? clampNumber(options.dpr, 1, OPTICAL_LIMITS.dpr.min, OPTICAL_LIMITS.dpr.max)
|
|
66
|
+
: 0
|
|
61
67
|
};
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -477,8 +483,17 @@ function getReadableStrokeColor(borderCol, fallback = 'rgba(148, 163, 184, 0.55)
|
|
|
477
483
|
return fallback;
|
|
478
484
|
}
|
|
479
485
|
|
|
480
|
-
function getDevicePixelRatio() {
|
|
481
|
-
|
|
486
|
+
function getDevicePixelRatio(customDpr = 0, elementWidth = 0) {
|
|
487
|
+
if (typeof customDpr === 'number' && customDpr > 0) {
|
|
488
|
+
return Math.max(0.5, Math.min(3.0, customDpr));
|
|
489
|
+
}
|
|
490
|
+
if (typeof window === 'undefined') return 1;
|
|
491
|
+
const sysDpr = window.devicePixelRatio || 1;
|
|
492
|
+
// Automatically use 1.0 for wide elements (headers, banners >= 700px) to guarantee 120 FPS
|
|
493
|
+
if (elementWidth >= 700) {
|
|
494
|
+
return 1;
|
|
495
|
+
}
|
|
496
|
+
return Math.min(sysDpr, 1.5);
|
|
482
497
|
}
|
|
483
498
|
|
|
484
499
|
function ensureStageBuffers() {
|
|
@@ -721,25 +736,50 @@ function captureViewportStage(neededBlur) {
|
|
|
721
736
|
|
|
722
737
|
stageCtx.restore();
|
|
723
738
|
|
|
739
|
+
let supportsCanvasFilterBlur = null;
|
|
740
|
+
function checkCanvasFilterBlur() {
|
|
741
|
+
if (supportsCanvasFilterBlur !== null) return supportsCanvasFilterBlur;
|
|
742
|
+
if (typeof document === 'undefined') return false;
|
|
743
|
+
try {
|
|
744
|
+
const testC = document.createElement('canvas');
|
|
745
|
+
testC.width = 4;
|
|
746
|
+
testC.height = 4;
|
|
747
|
+
const testCtx = testC.getContext('2d');
|
|
748
|
+
if (!testCtx || typeof testCtx.filter !== 'string') {
|
|
749
|
+
return (supportsCanvasFilterBlur = false);
|
|
750
|
+
}
|
|
751
|
+
testCtx.fillStyle = '#000';
|
|
752
|
+
testCtx.fillRect(0, 0, 4, 4);
|
|
753
|
+
testCtx.filter = 'blur(2px)';
|
|
754
|
+
testCtx.fillStyle = '#fff';
|
|
755
|
+
testCtx.fillRect(0, 0, 2, 2);
|
|
756
|
+
testCtx.filter = 'none';
|
|
757
|
+
const imgData = testCtx.getImageData(0, 0, 4, 4).data;
|
|
758
|
+
supportsCanvasFilterBlur = imgData[15 * 4] > 0 || imgData[14 * 4] > 0 || imgData[11 * 4] > 0;
|
|
759
|
+
return supportsCanvasFilterBlur;
|
|
760
|
+
} catch {
|
|
761
|
+
return (supportsCanvasFilterBlur = false);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
724
765
|
// 5. Blur buffer for frosted glass (Robust across all mobile WebKit & Blink engines)
|
|
725
766
|
if (neededBlur > 0) {
|
|
726
767
|
lastFrostedRadius = neededBlur;
|
|
727
768
|
blurCtx.clearRect(0, 0, bw, bh);
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const
|
|
736
|
-
const
|
|
769
|
+
|
|
770
|
+
if (checkCanvasFilterBlur()) {
|
|
771
|
+
blurCtx.filter = `blur(${Math.round(neededBlur * dpr)}px)`;
|
|
772
|
+
blurCtx.drawImage(stageCanvas, 0, 0);
|
|
773
|
+
blurCtx.filter = 'none';
|
|
774
|
+
} else {
|
|
775
|
+
// Hardware-accelerated multi-step pyramid downscale/upscale blur for iOS Safari and mobile WebKit
|
|
776
|
+
const factor = Math.max(0.04, 1.0 / (1.0 + neededBlur * 0.45));
|
|
777
|
+
const pw = Math.max(4, Math.round(bw * factor));
|
|
778
|
+
const ph = Math.max(4, Math.round(bh * factor));
|
|
737
779
|
blurCtx.imageSmoothingEnabled = true;
|
|
738
780
|
blurCtx.imageSmoothingQuality = 'high';
|
|
739
|
-
blurCtx.drawImage(stageCanvas, 0, 0,
|
|
740
|
-
blurCtx.drawImage(blurCanvas, 0, 0,
|
|
741
|
-
} else {
|
|
742
|
-
blurCtx.filter = 'none';
|
|
781
|
+
blurCtx.drawImage(stageCanvas, 0, 0, pw, ph);
|
|
782
|
+
blurCtx.drawImage(blurCanvas, 0, 0, pw, ph, 0, 0, bw, bh);
|
|
743
783
|
}
|
|
744
784
|
}
|
|
745
785
|
}
|
|
@@ -748,7 +788,7 @@ function captureViewportStage(neededBlur) {
|
|
|
748
788
|
* Fast Isolated Lens Renderer (Takes ~0.12 ms using precomputed TypedArray LUT!)
|
|
749
789
|
*/
|
|
750
790
|
function renderIsolatedController(ctrl, rect) {
|
|
751
|
-
const dpr = getDevicePixelRatio();
|
|
791
|
+
const dpr = getDevicePixelRatio(ctrl.options.dpr, rect.width);
|
|
752
792
|
const w = Math.max(16, Math.round(rect.width * dpr));
|
|
753
793
|
const h = Math.max(16, Math.round(rect.height * dpr));
|
|
754
794
|
const rad = ctrl._getRadius();
|
|
@@ -946,10 +986,12 @@ function renderAllActiveLenses() {
|
|
|
946
986
|
h: rect.height,
|
|
947
987
|
borderRadius: ctrl._getRadius(),
|
|
948
988
|
fusion: true,
|
|
949
|
-
tint: ctrl._tint
|
|
989
|
+
tint: ctrl._tint,
|
|
990
|
+
dpr: ctrl.options.dpr
|
|
950
991
|
}));
|
|
951
992
|
|
|
952
|
-
const
|
|
993
|
+
const maxClusterWidth = Math.max(...group.map(g => g.rect.width));
|
|
994
|
+
const dpr = getDevicePixelRatio(primaryCtrl.options.dpr, maxClusterWidth);
|
|
953
995
|
const box = renderFusedCluster(
|
|
954
996
|
clusterOverlayCtx,
|
|
955
997
|
sourceCtx,
|
|
@@ -964,6 +1006,17 @@ function renderAllActiveLenses() {
|
|
|
964
1006
|
if (box && clusterOverlayCanvas) {
|
|
965
1007
|
clusterOverlayCanvas.style.display = 'block';
|
|
966
1008
|
clusterOverlayCanvas.style.filter = 'none';
|
|
1009
|
+
|
|
1010
|
+
// Match z-index to highest element in the cluster so it never pops above floating menus/controls
|
|
1011
|
+
let maxZ = 20;
|
|
1012
|
+
for (const item of group) {
|
|
1013
|
+
if (item.ctrl && item.ctrl.element) {
|
|
1014
|
+
const z = parseInt(window.getComputedStyle(item.ctrl.element).zIndex, 10);
|
|
1015
|
+
if (!Number.isNaN(z) && z > maxZ) maxZ = z;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
clusterOverlayCanvas.style.zIndex = String(maxZ);
|
|
1019
|
+
|
|
967
1020
|
clusterOverlayCanvas.style.transform = `translate3d(${box.cMinX / dpr}px, ${box.cMinY / dpr}px, 0)`;
|
|
968
1021
|
clusterOverlayCanvas.style.width = `${box.boxW / dpr}px`;
|
|
969
1022
|
clusterOverlayCanvas.style.height = `${box.boxH / dpr}px`;
|
|
@@ -1136,11 +1189,11 @@ export class LiquidGlassController {
|
|
|
1136
1189
|
changed = true;
|
|
1137
1190
|
}
|
|
1138
1191
|
|
|
1139
|
-
for (const key of ['rim', 'ior', 'height', 'frosted', 'fusion']) {
|
|
1192
|
+
for (const key of ['rim', 'ior', 'height', 'frosted', 'fusion', 'dpr']) {
|
|
1140
1193
|
if (merged[key] !== this.options[key]) {
|
|
1141
1194
|
this.options[key] = merged[key];
|
|
1142
1195
|
changed = true;
|
|
1143
|
-
if (key === 'rim' || key === 'ior' || key === 'height') {
|
|
1196
|
+
if (key === 'rim' || key === 'ior' || key === 'height' || key === 'dpr') {
|
|
1144
1197
|
lutChanged = true;
|
|
1145
1198
|
}
|
|
1146
1199
|
}
|
package/dist/react.cjs
CHANGED
|
@@ -31,7 +31,8 @@ exports.useLiquidGlass = function useLiquidGlass(options = {}) {
|
|
|
31
31
|
height: options.height,
|
|
32
32
|
frosted: options.frosted,
|
|
33
33
|
fusion: options.fusion,
|
|
34
|
-
enabled: options.enabled
|
|
34
|
+
enabled: options.enabled,
|
|
35
|
+
dpr: options.dpr
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
}, [
|
|
@@ -40,7 +41,8 @@ exports.useLiquidGlass = function useLiquidGlass(options = {}) {
|
|
|
40
41
|
options.height,
|
|
41
42
|
options.frosted,
|
|
42
43
|
options.fusion,
|
|
43
|
-
options.enabled
|
|
44
|
+
options.enabled,
|
|
45
|
+
options.dpr
|
|
44
46
|
]);
|
|
45
47
|
|
|
46
48
|
return ref;
|
|
@@ -57,6 +59,7 @@ const LiquidGlass = exports.LiquidGlass = forwardRef(function LiquidGlass(
|
|
|
57
59
|
frosted = OPTICAL_DEFAULTS.frosted,
|
|
58
60
|
fusion = OPTICAL_DEFAULTS.fusion,
|
|
59
61
|
enabled = true,
|
|
62
|
+
dpr = OPTICAL_DEFAULTS.dpr,
|
|
60
63
|
as: Component = 'div',
|
|
61
64
|
style,
|
|
62
65
|
className,
|
|
@@ -71,7 +74,8 @@ const LiquidGlass = exports.LiquidGlass = forwardRef(function LiquidGlass(
|
|
|
71
74
|
height,
|
|
72
75
|
frosted,
|
|
73
76
|
fusion,
|
|
74
|
-
enabled
|
|
77
|
+
enabled,
|
|
78
|
+
dpr
|
|
75
79
|
});
|
|
76
80
|
|
|
77
81
|
const setRefs = useCallback(
|
package/dist/react.mjs
CHANGED
|
@@ -30,7 +30,8 @@ export function useLiquidGlass(options = {}) {
|
|
|
30
30
|
height: options.height,
|
|
31
31
|
frosted: options.frosted,
|
|
32
32
|
fusion: options.fusion,
|
|
33
|
-
enabled: options.enabled
|
|
33
|
+
enabled: options.enabled,
|
|
34
|
+
dpr: options.dpr
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
}, [
|
|
@@ -39,7 +40,8 @@ export function useLiquidGlass(options = {}) {
|
|
|
39
40
|
options.height,
|
|
40
41
|
options.frosted,
|
|
41
42
|
options.fusion,
|
|
42
|
-
options.enabled
|
|
43
|
+
options.enabled,
|
|
44
|
+
options.dpr
|
|
43
45
|
]);
|
|
44
46
|
|
|
45
47
|
return ref;
|
|
@@ -56,6 +58,7 @@ export const LiquidGlass = forwardRef(function LiquidGlass(
|
|
|
56
58
|
frosted = OPTICAL_DEFAULTS.frosted,
|
|
57
59
|
fusion = OPTICAL_DEFAULTS.fusion,
|
|
58
60
|
enabled = true,
|
|
61
|
+
dpr = OPTICAL_DEFAULTS.dpr,
|
|
59
62
|
as: Component = 'div',
|
|
60
63
|
style,
|
|
61
64
|
className,
|
|
@@ -70,7 +73,8 @@ export const LiquidGlass = forwardRef(function LiquidGlass(
|
|
|
70
73
|
height,
|
|
71
74
|
frosted,
|
|
72
75
|
fusion,
|
|
73
|
-
enabled
|
|
76
|
+
enabled,
|
|
77
|
+
dpr
|
|
74
78
|
});
|
|
75
79
|
|
|
76
80
|
const setRefs = useCallback(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shulya/liquid-glass",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "High-performance, zero-WebGL Liquid Glass optical UI engine with pre-computed empirical lookup tables for React, Vue, Svelte, Angular, SolidJS, and Vanilla JS.",
|
|
5
5
|
"author": "Shulya (https://shulya.tech)",
|
|
6
6
|
"homepage": "https://liquid.shulya.tech",
|
package/types/index.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export interface LiquidGlassOptions {
|
|
|
30
30
|
*/
|
|
31
31
|
enabled?: boolean;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Optional custom device pixel ratio override (e.g. 1.0 for full-width navbars to maximize FPS, or 1.5–2.0 for high-DPI retina sharpness).
|
|
35
|
+
* Defaults to 0 (automatic: 1.5 for compact UI elements, 1.0 for elements >= 700px wide).
|
|
36
|
+
*/
|
|
37
|
+
dpr?: number;
|
|
38
|
+
|
|
33
39
|
/**
|
|
34
40
|
* Optional border radius override in pixels
|
|
35
41
|
*/
|
|
@@ -42,6 +48,7 @@ export declare const OPTICAL_DEFAULTS: Readonly<{
|
|
|
42
48
|
height: 18;
|
|
43
49
|
frosted: 0;
|
|
44
50
|
fusion: true;
|
|
51
|
+
dpr: 0;
|
|
45
52
|
}>;
|
|
46
53
|
|
|
47
54
|
export declare const OPTICAL_LIMITS: Readonly<{
|
|
@@ -49,6 +56,7 @@ export declare const OPTICAL_LIMITS: Readonly<{
|
|
|
49
56
|
ior: Readonly<{ min: 1.05; max: 2.2 }>;
|
|
50
57
|
height: Readonly<{ min: 2; max: 50 }>;
|
|
51
58
|
frosted: Readonly<{ min: 0; max: 32 }>;
|
|
59
|
+
dpr: Readonly<{ min: 0.5; max: 3.0 }>;
|
|
52
60
|
}>;
|
|
53
61
|
|
|
54
62
|
export declare function normalizeOptions(options?: LiquidGlassOptions): Required<LiquidGlassOptions>;
|