liquid-gl 1.0.6 → 2.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 +117 -40
- package/liquidGL.js +519 -136
- package/package.json +3 -10
package/liquidGL.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Author: NaughtyDuk© – https://liquidgl.naughtyduk.com
|
|
6
6
|
* Licence: MIT
|
|
7
|
+
* Version: v2.0.0
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import html2canvas from "html2canvas";
|
|
@@ -11,6 +12,8 @@ import html2canvas from "html2canvas";
|
|
|
11
12
|
const liquidGL = (() => {
|
|
12
13
|
"use strict";
|
|
13
14
|
|
|
15
|
+
const RECAPTURE_INTERVAL_MS = 250;
|
|
16
|
+
|
|
14
17
|
/* --------------------------------------------------
|
|
15
18
|
* Utilities
|
|
16
19
|
* ------------------------------------------------*/
|
|
@@ -138,7 +141,7 @@ const liquidGL = (() => {
|
|
|
138
141
|
this._resizeCanvas();
|
|
139
142
|
this.lenses.forEach((l) => l.updateMetrics());
|
|
140
143
|
this.captureSnapshot();
|
|
141
|
-
},
|
|
144
|
+
}, RECAPTURE_INTERVAL_MS);
|
|
142
145
|
window.addEventListener("resize", onResize, { passive: true });
|
|
143
146
|
|
|
144
147
|
if ("ResizeObserver" in window) {
|
|
@@ -157,27 +160,30 @@ const liquidGL = (() => {
|
|
|
157
160
|
document.head.appendChild(styleEl);
|
|
158
161
|
this._dynamicStyleSheet = styleEl.sheet;
|
|
159
162
|
|
|
163
|
+
this._snapshotResolution = Math.max(
|
|
164
|
+
0.1,
|
|
165
|
+
Math.min(3.0, snapshotResolution),
|
|
166
|
+
);
|
|
167
|
+
this._pendingReveal = [];
|
|
168
|
+
|
|
160
169
|
this._resizeCanvas();
|
|
161
170
|
this.captureSnapshot();
|
|
162
171
|
|
|
163
|
-
this._pendingReveal = [];
|
|
164
|
-
|
|
165
172
|
/* --------------------------------------------------
|
|
166
173
|
* Dynamic media (video) support
|
|
167
174
|
* ------------------------------------------------*/
|
|
168
175
|
this._videoNodes = Array.from(
|
|
169
|
-
this.snapshotTarget.querySelectorAll("video")
|
|
176
|
+
this.snapshotTarget.querySelectorAll("video"),
|
|
170
177
|
);
|
|
171
178
|
this._videoNodes = this._videoNodes.filter((v) => !this._isIgnored(v));
|
|
172
179
|
this._tmpCanvas = document.createElement("canvas");
|
|
173
180
|
this._tmpCtx = this._tmpCanvas.getContext("2d");
|
|
174
181
|
|
|
175
|
-
this.
|
|
182
|
+
this._videoFrameState = new WeakMap();
|
|
176
183
|
|
|
177
|
-
this.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
);
|
|
184
|
+
this._videoAlphaState = new WeakMap();
|
|
185
|
+
|
|
186
|
+
this.canvas.style.opacity = "0";
|
|
181
187
|
|
|
182
188
|
this.useExternalTicker = false;
|
|
183
189
|
|
|
@@ -227,7 +233,7 @@ const liquidGL = (() => {
|
|
|
227
233
|
y,
|
|
228
234
|
gl.RGBA,
|
|
229
235
|
gl.UNSIGNED_BYTE,
|
|
230
|
-
bmp
|
|
236
|
+
bmp,
|
|
231
237
|
);
|
|
232
238
|
};
|
|
233
239
|
}
|
|
@@ -244,13 +250,18 @@ const liquidGL = (() => {
|
|
|
244
250
|
}`;
|
|
245
251
|
|
|
246
252
|
const fsSource = `
|
|
253
|
+
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
|
254
|
+
precision highp float;
|
|
255
|
+
#else
|
|
247
256
|
precision mediump float;
|
|
257
|
+
#endif
|
|
248
258
|
varying vec2 v_uv;
|
|
249
259
|
uniform sampler2D u_tex;
|
|
250
260
|
uniform vec2 u_resolution;
|
|
251
261
|
uniform vec2 u_textureResolution;
|
|
252
262
|
uniform vec4 u_bounds;
|
|
253
263
|
uniform float u_refraction;
|
|
264
|
+
uniform float u_aberration;
|
|
254
265
|
uniform float u_bevelDepth;
|
|
255
266
|
uniform float u_bevelWidth;
|
|
256
267
|
uniform float u_frost;
|
|
@@ -267,6 +278,17 @@ const liquidGL = (() => {
|
|
|
267
278
|
return length(max(abs(p)-b+r,0.0))-r;
|
|
268
279
|
}
|
|
269
280
|
|
|
281
|
+
vec2 cornerNormal( vec2 p, vec2 b, float r, vec2 fallback ) {
|
|
282
|
+
vec2 q = abs(p) - b + r;
|
|
283
|
+
vec2 m = max(q, 0.0);
|
|
284
|
+
float l = length(m);
|
|
285
|
+
if (l <= 0.0) return fallback;
|
|
286
|
+
float w = smoothstep(0.0, max(r * 0.5, 1.0), min(m.x, m.y));
|
|
287
|
+
if (w <= 0.0) return fallback;
|
|
288
|
+
vec2 s = vec2(p.x < 0.0 ? -1.0 : 1.0, p.y < 0.0 ? -1.0 : 1.0);
|
|
289
|
+
return normalize(mix(fallback, s * (m / l), w));
|
|
290
|
+
}
|
|
291
|
+
|
|
270
292
|
float random(vec2 st) {
|
|
271
293
|
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
|
|
272
294
|
}
|
|
@@ -286,7 +308,13 @@ const liquidGL = (() => {
|
|
|
286
308
|
float min_dimension = min(u_resolution.x, u_resolution.y);
|
|
287
309
|
float offsetAmt = (edge * u_refraction + pow(edge, 10.0) * u_bevelDepth);
|
|
288
310
|
float centreBlend = smoothstep(0.15, 0.45, length(p));
|
|
289
|
-
vec2
|
|
311
|
+
vec2 refractDir = cornerNormal(
|
|
312
|
+
(v_uv - 0.5) * u_resolution,
|
|
313
|
+
0.5 * u_resolution,
|
|
314
|
+
u_radius,
|
|
315
|
+
normalize(p)
|
|
316
|
+
);
|
|
317
|
+
vec2 offset = refractDir * offsetAmt * centreBlend;
|
|
290
318
|
|
|
291
319
|
float tiltRefractionScale = 0.05;
|
|
292
320
|
vec2 tiltOffset = vec2(tan(radians(u_tiltY)), -tan(radians(u_tiltX))) * tiltRefractionScale;
|
|
@@ -326,6 +354,12 @@ const liquidGL = (() => {
|
|
|
326
354
|
refrCol /= 5.0;
|
|
327
355
|
}
|
|
328
356
|
|
|
357
|
+
if (u_aberration > 0.0) {
|
|
358
|
+
vec2 chroma = offset * u_aberration;
|
|
359
|
+
refrCol.r = texture2D(u_tex, sampleUV - chroma).r;
|
|
360
|
+
refrCol.b = texture2D(u_tex, sampleUV + chroma).b;
|
|
361
|
+
}
|
|
362
|
+
|
|
329
363
|
if (refrCol.a < 0.1) {
|
|
330
364
|
refrCol = baseCol;
|
|
331
365
|
}
|
|
@@ -339,7 +373,7 @@ const liquidGL = (() => {
|
|
|
339
373
|
vec2 p_px = (v_uv - 0.5) * u_resolution;
|
|
340
374
|
vec2 b_px = 0.5 * u_resolution;
|
|
341
375
|
float dmask = udRoundBox(p_px, b_px, u_radius);
|
|
342
|
-
float inShape =
|
|
376
|
+
float inShape = clamp(0.5 - dmask, 0.0, 1.0);
|
|
343
377
|
|
|
344
378
|
if (u_specular) {
|
|
345
379
|
vec2 lp1 = vec2(sin(u_time*0.2), cos(u_time*0.3))*0.6 + 0.5;
|
|
@@ -370,22 +404,26 @@ const liquidGL = (() => {
|
|
|
370
404
|
gl.bufferData(
|
|
371
405
|
gl.ARRAY_BUFFER,
|
|
372
406
|
new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
|
|
373
|
-
gl.STATIC_DRAW
|
|
407
|
+
gl.STATIC_DRAW,
|
|
374
408
|
);
|
|
375
409
|
|
|
376
410
|
const posLoc = gl.getAttribLocation(this.program, "a_position");
|
|
377
411
|
gl.enableVertexAttribArray(posLoc);
|
|
378
412
|
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
379
413
|
|
|
414
|
+
this._posBuf = posBuf;
|
|
415
|
+
this._posLoc = posLoc;
|
|
416
|
+
|
|
380
417
|
this.u = {
|
|
381
418
|
tex: gl.getUniformLocation(this.program, "u_tex"),
|
|
382
419
|
res: gl.getUniformLocation(this.program, "u_resolution"),
|
|
383
420
|
textureResolution: gl.getUniformLocation(
|
|
384
421
|
this.program,
|
|
385
|
-
"u_textureResolution"
|
|
422
|
+
"u_textureResolution",
|
|
386
423
|
),
|
|
387
424
|
bounds: gl.getUniformLocation(this.program, "u_bounds"),
|
|
388
425
|
refraction: gl.getUniformLocation(this.program, "u_refraction"),
|
|
426
|
+
aberration: gl.getUniformLocation(this.program, "u_aberration"),
|
|
389
427
|
bevelDepth: gl.getUniformLocation(this.program, "u_bevelDepth"),
|
|
390
428
|
bevelWidth: gl.getUniformLocation(this.program, "u_bevelWidth"),
|
|
391
429
|
frost: gl.getUniformLocation(this.program, "u_frost"),
|
|
@@ -420,7 +458,7 @@ const liquidGL = (() => {
|
|
|
420
458
|
const attemptCapture = async (
|
|
421
459
|
attempt = 1,
|
|
422
460
|
maxAttempts = 3,
|
|
423
|
-
delayMs = 500
|
|
461
|
+
delayMs = 500,
|
|
424
462
|
) => {
|
|
425
463
|
try {
|
|
426
464
|
const fullW = this.snapshotTarget.scrollWidth;
|
|
@@ -432,13 +470,24 @@ const liquidGL = (() => {
|
|
|
432
470
|
let scale = Math.min(
|
|
433
471
|
this._snapshotResolution,
|
|
434
472
|
maxTex / fullW,
|
|
435
|
-
maxTex / fullH
|
|
473
|
+
maxTex / fullH,
|
|
436
474
|
);
|
|
437
475
|
|
|
438
476
|
if (isMobileSafari) {
|
|
439
477
|
const over = (Math.max(fullW, fullH) * scale) / MAX_MOBILE_DIM;
|
|
440
478
|
if (over > 1) scale = scale / over;
|
|
441
479
|
}
|
|
480
|
+
|
|
481
|
+
const maxArea = isMobileSafari ? 4096 * 4096 : 16384 * 16384;
|
|
482
|
+
if (fullW * fullH * scale * scale > maxArea) {
|
|
483
|
+
scale = Math.sqrt(maxArea / (fullW * fullH));
|
|
484
|
+
console.warn(
|
|
485
|
+
`liquidGL: snapshot area capped, resolution reduced to ${scale.toFixed(
|
|
486
|
+
3,
|
|
487
|
+
)} for a ${fullW}x${fullH} document.`,
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
442
491
|
this.scaleFactor = Math.max(0.1, scale);
|
|
443
492
|
|
|
444
493
|
this.canvas.style.visibility = "hidden";
|
|
@@ -448,9 +497,16 @@ const liquidGL = (() => {
|
|
|
448
497
|
.flatMap((lens) => [lens.el, lens._shadowEl])
|
|
449
498
|
.filter(Boolean);
|
|
450
499
|
|
|
500
|
+
lensElements.forEach((el) => {
|
|
501
|
+
el.setAttribute("data-liquidgl-hide", "");
|
|
502
|
+
undos.push(() => {
|
|
503
|
+
el.removeAttribute("data-liquidgl-hide");
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
451
507
|
const ignoreElementsFunc = (element) => {
|
|
452
508
|
if (!element || !element.hasAttribute) return false;
|
|
453
|
-
if (element === this.canvas
|
|
509
|
+
if (element === this.canvas) {
|
|
454
510
|
return true;
|
|
455
511
|
}
|
|
456
512
|
const style = window.getComputedStyle(element);
|
|
@@ -474,15 +530,24 @@ const liquidGL = (() => {
|
|
|
474
530
|
scrollY: 0,
|
|
475
531
|
scale: scale,
|
|
476
532
|
ignoreElements: ignoreElementsFunc,
|
|
533
|
+
onclone: (clonedDoc) => {
|
|
534
|
+
clonedDoc
|
|
535
|
+
.querySelectorAll("[data-liquidgl-hide]")
|
|
536
|
+
.forEach((el) => {
|
|
537
|
+
el.style.visibility = "hidden";
|
|
538
|
+
});
|
|
539
|
+
},
|
|
477
540
|
});
|
|
478
541
|
|
|
479
|
-
this._uploadTexture(snapCanvas)
|
|
542
|
+
if (!this._uploadTexture(snapCanvas)) {
|
|
543
|
+
throw new Error("liquidGL: snapshot could not be uploaded.");
|
|
544
|
+
}
|
|
480
545
|
return true;
|
|
481
546
|
} catch (e) {
|
|
482
547
|
console.error("liquidGL snapshot failed on attempt " + attempt, e);
|
|
483
548
|
if (attempt < maxAttempts) {
|
|
484
549
|
console.log(
|
|
485
|
-
`Retrying snapshot capture (${attempt + 1}/${maxAttempts})
|
|
550
|
+
`Retrying snapshot capture (${attempt + 1}/${maxAttempts})...`,
|
|
486
551
|
);
|
|
487
552
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
488
553
|
return await attemptCapture(attempt + 1, maxAttempts, delayMs);
|
|
@@ -503,27 +568,39 @@ const liquidGL = (() => {
|
|
|
503
568
|
|
|
504
569
|
/* ----------------------------- */
|
|
505
570
|
_uploadTexture(srcCanvas) {
|
|
506
|
-
if (!srcCanvas)
|
|
571
|
+
if (!srcCanvas) {
|
|
572
|
+
console.error("liquidGL: snapshot produced no canvas.");
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
507
575
|
|
|
508
576
|
if (!(srcCanvas instanceof HTMLCanvasElement)) {
|
|
509
577
|
const tmp = document.createElement("canvas");
|
|
510
578
|
tmp.width = srcCanvas.width || 0;
|
|
511
579
|
tmp.height = srcCanvas.height || 0;
|
|
512
|
-
if (tmp.width === 0 || tmp.height === 0)
|
|
580
|
+
if (tmp.width === 0 || tmp.height === 0) {
|
|
581
|
+
console.error("liquidGL: snapshot canvas has zero dimensions.");
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
513
584
|
try {
|
|
514
585
|
const ctx = tmp.getContext("2d");
|
|
515
586
|
ctx.drawImage(srcCanvas, 0, 0);
|
|
516
587
|
srcCanvas = tmp;
|
|
517
588
|
} catch (e) {
|
|
518
|
-
console.
|
|
589
|
+
console.error(
|
|
519
590
|
"liquidGL: Unable to convert OffscreenCanvas for upload",
|
|
520
|
-
e
|
|
591
|
+
e,
|
|
521
592
|
);
|
|
522
|
-
return;
|
|
593
|
+
return false;
|
|
523
594
|
}
|
|
524
595
|
}
|
|
525
596
|
|
|
526
|
-
if (srcCanvas.width === 0 || srcCanvas.height === 0)
|
|
597
|
+
if (srcCanvas.width === 0 || srcCanvas.height === 0) {
|
|
598
|
+
console.error(
|
|
599
|
+
"liquidGL: snapshot canvas has zero dimensions, requested area " +
|
|
600
|
+
`${srcCanvas.width}x${srcCanvas.height} exceeds a browser limit.`,
|
|
601
|
+
);
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
527
604
|
this.staticSnapshotCanvas = srcCanvas;
|
|
528
605
|
const gl = this.gl;
|
|
529
606
|
if (!this.texture) this.texture = gl.createTexture();
|
|
@@ -535,7 +612,7 @@ const liquidGL = (() => {
|
|
|
535
612
|
gl.RGBA,
|
|
536
613
|
gl.RGBA,
|
|
537
614
|
gl.UNSIGNED_BYTE,
|
|
538
|
-
srcCanvas
|
|
615
|
+
srcCanvas,
|
|
539
616
|
);
|
|
540
617
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
541
618
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
@@ -545,12 +622,18 @@ const liquidGL = (() => {
|
|
|
545
622
|
this.textureWidth = srcCanvas.width;
|
|
546
623
|
this.textureHeight = srcCanvas.height;
|
|
547
624
|
|
|
625
|
+
if (this._videoFrameState) this._videoFrameState = new WeakMap();
|
|
626
|
+
|
|
627
|
+
this._vFboTexture = null;
|
|
628
|
+
|
|
548
629
|
this.render();
|
|
549
630
|
|
|
550
631
|
if (this._pendingReveal.length) {
|
|
551
632
|
this._pendingReveal.forEach((ln) => ln._reveal());
|
|
552
633
|
this._pendingReveal.length = 0;
|
|
553
634
|
}
|
|
635
|
+
|
|
636
|
+
return true;
|
|
554
637
|
}
|
|
555
638
|
|
|
556
639
|
/* ----------------------------- */
|
|
@@ -594,6 +677,8 @@ const liquidGL = (() => {
|
|
|
594
677
|
|
|
595
678
|
this._updateDynamicNodes();
|
|
596
679
|
|
|
680
|
+
this._frameSnapRect = this.snapshotTarget.getBoundingClientRect();
|
|
681
|
+
|
|
597
682
|
this.lenses.forEach((lens) => {
|
|
598
683
|
lens.updateMetrics();
|
|
599
684
|
if (lens._mirrorActive && lens._mirrorClipUpdater) {
|
|
@@ -624,15 +709,15 @@ const liquidGL = (() => {
|
|
|
624
709
|
const x = Math.max(0, Math.round(left * dpr) - expand);
|
|
625
710
|
const y = Math.max(
|
|
626
711
|
0,
|
|
627
|
-
Math.round(this.canvas.height - (top + height) * dpr) - expand
|
|
712
|
+
Math.round(this.canvas.height - (top + height) * dpr) - expand,
|
|
628
713
|
);
|
|
629
714
|
const w = Math.min(
|
|
630
715
|
this.canvas.width - x,
|
|
631
|
-
Math.round(width * dpr) + expand * 2
|
|
716
|
+
Math.round(width * dpr) + expand * 2,
|
|
632
717
|
);
|
|
633
718
|
const h = Math.min(
|
|
634
719
|
this.canvas.height - y,
|
|
635
|
-
Math.round(height * dpr) + expand * 2
|
|
720
|
+
Math.round(height * dpr) + expand * 2,
|
|
636
721
|
);
|
|
637
722
|
if (w > 0 && h > 0) {
|
|
638
723
|
gl.enable(gl.SCISSOR_TEST);
|
|
@@ -656,22 +741,27 @@ const liquidGL = (() => {
|
|
|
656
741
|
let overscrollY = 0;
|
|
657
742
|
let overscrollX = 0;
|
|
658
743
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
744
|
+
const vv = window.visualViewport;
|
|
745
|
+
if (vv && Math.abs(vv.scale - 1) < 0.01) {
|
|
746
|
+
overscrollX = vv.offsetLeft;
|
|
747
|
+
overscrollY = vv.offsetTop;
|
|
662
748
|
}
|
|
663
749
|
|
|
664
|
-
const
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
const
|
|
668
|
-
const
|
|
750
|
+
const leftPx = (rect.left + overscrollX) * dpr;
|
|
751
|
+
const topPx = (rect.top + overscrollY) * dpr;
|
|
752
|
+
const x = Math.round(leftPx);
|
|
753
|
+
const yTop = Math.round(topPx);
|
|
754
|
+
const w = Math.round(leftPx + rect.width * dpr) - x;
|
|
755
|
+
const h = Math.round(topPx + rect.height * dpr) - yTop;
|
|
756
|
+
const y = this.canvas.height - (yTop + h);
|
|
669
757
|
|
|
670
758
|
gl.viewport(x, y, w, h);
|
|
671
759
|
gl.uniform2f(this.u.res, w, h);
|
|
672
760
|
|
|
673
|
-
const
|
|
674
|
-
|
|
761
|
+
const snapRect =
|
|
762
|
+
this._frameSnapRect || this.snapshotTarget.getBoundingClientRect();
|
|
763
|
+
const docX = rect.left - snapRect.left;
|
|
764
|
+
const docY = rect.top - snapRect.top;
|
|
675
765
|
const leftUV = (docX * this.scaleFactor) / this.textureWidth;
|
|
676
766
|
const topUV = (docY * this.scaleFactor) / this.textureHeight;
|
|
677
767
|
const wUV = (rect.width * this.scaleFactor) / this.textureWidth;
|
|
@@ -681,9 +771,10 @@ const liquidGL = (() => {
|
|
|
681
771
|
gl.uniform2f(
|
|
682
772
|
this.u.textureResolution,
|
|
683
773
|
this.textureWidth,
|
|
684
|
-
this.textureHeight
|
|
774
|
+
this.textureHeight,
|
|
685
775
|
);
|
|
686
776
|
gl.uniform1f(this.u.refraction, lens.options.refraction);
|
|
777
|
+
gl.uniform1f(this.u.aberration, lens.options.aberration || 0);
|
|
687
778
|
gl.uniform1f(this.u.bevelDepth, lens.options.bevelDepth);
|
|
688
779
|
gl.uniform1f(this.u.bevelWidth, lens.options.bevelWidth);
|
|
689
780
|
gl.uniform1f(this.u.frost, lens.options.frost);
|
|
@@ -696,8 +787,8 @@ const liquidGL = (() => {
|
|
|
696
787
|
0.001,
|
|
697
788
|
Math.min(
|
|
698
789
|
3.0,
|
|
699
|
-
lens.options.magnify !== undefined ? lens.options.magnify : 1.0
|
|
700
|
-
)
|
|
790
|
+
lens.options.magnify !== undefined ? lens.options.magnify : 1.0,
|
|
791
|
+
),
|
|
701
792
|
);
|
|
702
793
|
gl.uniform1f(this.u.magnify, mag);
|
|
703
794
|
|
|
@@ -722,6 +813,166 @@ const liquidGL = (() => {
|
|
|
722
813
|
ctx.closePath();
|
|
723
814
|
}
|
|
724
815
|
|
|
816
|
+
_initVideoBlit() {
|
|
817
|
+
if (this._vBlitReady !== undefined) return this._vBlitReady;
|
|
818
|
+
|
|
819
|
+
const gl = this.gl;
|
|
820
|
+
|
|
821
|
+
const vs = `
|
|
822
|
+
attribute vec2 a_position;
|
|
823
|
+
varying vec2 v_uv;
|
|
824
|
+
void main(){
|
|
825
|
+
v_uv = (a_position + 1.0) * 0.5;
|
|
826
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
827
|
+
}`;
|
|
828
|
+
|
|
829
|
+
const fs = `
|
|
830
|
+
precision mediump float;
|
|
831
|
+
varying vec2 v_uv;
|
|
832
|
+
uniform sampler2D u_src;
|
|
833
|
+
uniform vec4 u_srcRect;
|
|
834
|
+
void main(){
|
|
835
|
+
gl_FragColor = texture2D(u_src, u_srcRect.xy + v_uv * u_srcRect.zw);
|
|
836
|
+
}`;
|
|
837
|
+
|
|
838
|
+
const prog = createProgram(gl, vs, fs);
|
|
839
|
+
if (!prog) {
|
|
840
|
+
this._vBlitReady = false;
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
this._vProg = prog;
|
|
845
|
+
this._vPosLoc = gl.getAttribLocation(prog, "a_position");
|
|
846
|
+
this._vU = {
|
|
847
|
+
src: gl.getUniformLocation(prog, "u_src"),
|
|
848
|
+
srcRect: gl.getUniformLocation(prog, "u_srcRect"),
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
this._vTex = gl.createTexture();
|
|
852
|
+
gl.bindTexture(gl.TEXTURE_2D, this._vTex);
|
|
853
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
854
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
855
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
856
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
857
|
+
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
858
|
+
|
|
859
|
+
this._vFbo = gl.createFramebuffer();
|
|
860
|
+
this._vFboTexture = null;
|
|
861
|
+
|
|
862
|
+
this._vBlitReady = true;
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
_videoIsOpaque(vid) {
|
|
867
|
+
if (!vid.videoWidth || !vid.videoHeight) return false;
|
|
868
|
+
|
|
869
|
+
const key = vid.videoWidth + "x" + vid.videoHeight;
|
|
870
|
+
const cached = this._videoAlphaState.get(vid);
|
|
871
|
+
if (cached && cached.key === key) return cached.opaque;
|
|
872
|
+
|
|
873
|
+
let opaque = false;
|
|
874
|
+
try {
|
|
875
|
+
const probe =
|
|
876
|
+
this._alphaProbeCanvas ||
|
|
877
|
+
(this._alphaProbeCanvas = document.createElement("canvas"));
|
|
878
|
+
const size = 32;
|
|
879
|
+
probe.width = size;
|
|
880
|
+
probe.height = size;
|
|
881
|
+
const ctx = probe.getContext("2d", { willReadFrequently: true });
|
|
882
|
+
ctx.clearRect(0, 0, size, size);
|
|
883
|
+
ctx.drawImage(vid, 0, 0, size, size);
|
|
884
|
+
const data = ctx.getImageData(0, 0, size, size).data;
|
|
885
|
+
opaque = true;
|
|
886
|
+
for (let i = 3; i < data.length; i += 4) {
|
|
887
|
+
if (data[i] !== 255) {
|
|
888
|
+
opaque = false;
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
} catch (e) {
|
|
893
|
+
opaque = false;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
this._videoAlphaState.set(vid, { key, opaque });
|
|
897
|
+
return opaque;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
_blitVideoToTexture(vid, dstX, dstY, dstW, dstH, srcRect) {
|
|
901
|
+
const gl = this.gl;
|
|
902
|
+
|
|
903
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this._vFbo);
|
|
904
|
+
|
|
905
|
+
if (this._vFboTexture !== this.texture) {
|
|
906
|
+
gl.framebufferTexture2D(
|
|
907
|
+
gl.FRAMEBUFFER,
|
|
908
|
+
gl.COLOR_ATTACHMENT0,
|
|
909
|
+
gl.TEXTURE_2D,
|
|
910
|
+
this.texture,
|
|
911
|
+
0,
|
|
912
|
+
);
|
|
913
|
+
if (
|
|
914
|
+
gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE
|
|
915
|
+
) {
|
|
916
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
917
|
+
this._vBlitReady = false;
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
this._vFboTexture = this.texture;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
924
|
+
gl.bindTexture(gl.TEXTURE_2D, this._vTex);
|
|
925
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
926
|
+
|
|
927
|
+
try {
|
|
928
|
+
gl.texImage2D(
|
|
929
|
+
gl.TEXTURE_2D,
|
|
930
|
+
0,
|
|
931
|
+
gl.RGBA,
|
|
932
|
+
gl.RGBA,
|
|
933
|
+
gl.UNSIGNED_BYTE,
|
|
934
|
+
vid,
|
|
935
|
+
);
|
|
936
|
+
} catch (e) {
|
|
937
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
938
|
+
this._restoreLensProgramState();
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
gl.useProgram(this._vProg);
|
|
943
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._posBuf);
|
|
944
|
+
gl.enableVertexAttribArray(this._vPosLoc);
|
|
945
|
+
gl.vertexAttribPointer(this._vPosLoc, 2, gl.FLOAT, false, 0, 0);
|
|
946
|
+
|
|
947
|
+
gl.uniform1i(this._vU.src, 0);
|
|
948
|
+
gl.uniform4f(
|
|
949
|
+
this._vU.srcRect,
|
|
950
|
+
srcRect.u,
|
|
951
|
+
srcRect.v,
|
|
952
|
+
srcRect.uw,
|
|
953
|
+
srcRect.vh,
|
|
954
|
+
);
|
|
955
|
+
|
|
956
|
+
gl.viewport(dstX, dstY, dstW, dstH);
|
|
957
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
958
|
+
|
|
959
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
960
|
+
this._restoreLensProgramState();
|
|
961
|
+
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
_restoreLensProgramState() {
|
|
966
|
+
const gl = this.gl;
|
|
967
|
+
gl.useProgram(this.program);
|
|
968
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._posBuf);
|
|
969
|
+
gl.enableVertexAttribArray(this._posLoc);
|
|
970
|
+
gl.vertexAttribPointer(this._posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
971
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
972
|
+
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
973
|
+
gl.uniform1i(this.u.tex, 0);
|
|
974
|
+
}
|
|
975
|
+
|
|
725
976
|
/* ----------------------------- */
|
|
726
977
|
_updateDynamicVideos() {
|
|
727
978
|
if (this._isScrolling && this._scrollUpdateCounter % 2 !== 0) return;
|
|
@@ -755,6 +1006,76 @@ const liquidGL = (() => {
|
|
|
755
1006
|
|
|
756
1007
|
if (drawW <= 0 || drawH <= 0) return;
|
|
757
1008
|
|
|
1009
|
+
const geomKey =
|
|
1010
|
+
Math.round(texX) + "," + Math.round(texY) + "," + drawW + "," + drawH;
|
|
1011
|
+
const prevState = this._videoFrameState.get(vid);
|
|
1012
|
+
if (
|
|
1013
|
+
prevState &&
|
|
1014
|
+
prevState.time === vid.currentTime &&
|
|
1015
|
+
prevState.geom === geomKey
|
|
1016
|
+
) {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
this._videoFrameState.set(vid, {
|
|
1020
|
+
time: vid.currentTime,
|
|
1021
|
+
geom: geomKey,
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
const drawX = Math.round(texX);
|
|
1025
|
+
const drawY = Math.round(texY);
|
|
1026
|
+
|
|
1027
|
+
const maxW = this.textureWidth;
|
|
1028
|
+
const maxH = this.textureHeight;
|
|
1029
|
+
let dstX = drawX;
|
|
1030
|
+
let dstY = drawY;
|
|
1031
|
+
let srcX = 0,
|
|
1032
|
+
srcY = 0,
|
|
1033
|
+
updW = drawW,
|
|
1034
|
+
updH = drawH;
|
|
1035
|
+
|
|
1036
|
+
if (dstX < 0) {
|
|
1037
|
+
srcX = -dstX;
|
|
1038
|
+
updW += dstX;
|
|
1039
|
+
dstX = 0;
|
|
1040
|
+
}
|
|
1041
|
+
if (dstY < 0) {
|
|
1042
|
+
srcY = -dstY;
|
|
1043
|
+
updH += dstY;
|
|
1044
|
+
dstY = 0;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if (dstX + updW > maxW) {
|
|
1048
|
+
updW = maxW - dstX;
|
|
1049
|
+
}
|
|
1050
|
+
if (dstY + updH > maxH) {
|
|
1051
|
+
updH = maxH - dstY;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
if (updW <= 0 || updH <= 0) return;
|
|
1055
|
+
|
|
1056
|
+
const style = window.getComputedStyle(vid);
|
|
1057
|
+
const scaledRadii = {
|
|
1058
|
+
tl: parseFloat(style.borderTopLeftRadius) * this.scaleFactor,
|
|
1059
|
+
tr: parseFloat(style.borderTopRightRadius) * this.scaleFactor,
|
|
1060
|
+
br: parseFloat(style.borderBottomRightRadius) * this.scaleFactor,
|
|
1061
|
+
bl: parseFloat(style.borderBottomLeftRadius) * this.scaleFactor,
|
|
1062
|
+
};
|
|
1063
|
+
const isRounded = Object.values(scaledRadii).some((r) => r > 0);
|
|
1064
|
+
|
|
1065
|
+
if (
|
|
1066
|
+
!isRounded &&
|
|
1067
|
+
this._initVideoBlit() &&
|
|
1068
|
+
this._videoIsOpaque(vid) &&
|
|
1069
|
+
this._blitVideoToTexture(vid, dstX, dstY, updW, updH, {
|
|
1070
|
+
u: srcX / drawW,
|
|
1071
|
+
v: srcY / drawH,
|
|
1072
|
+
uw: updW / drawW,
|
|
1073
|
+
vh: updH / drawH,
|
|
1074
|
+
})
|
|
1075
|
+
) {
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
758
1079
|
if (
|
|
759
1080
|
this._tmpCanvas.width !== drawW ||
|
|
760
1081
|
this._tmpCanvas.height !== drawH
|
|
@@ -767,20 +1088,12 @@ const liquidGL = (() => {
|
|
|
767
1088
|
this._tmpCtx.save();
|
|
768
1089
|
this._tmpCtx.clearRect(0, 0, drawW, drawH);
|
|
769
1090
|
|
|
770
|
-
|
|
771
|
-
const scaledRadii = {
|
|
772
|
-
tl: parseFloat(style.borderTopLeftRadius) * this.scaleFactor,
|
|
773
|
-
tr: parseFloat(style.borderTopRightRadius) * this.scaleFactor,
|
|
774
|
-
br: parseFloat(style.borderBottomRightRadius) * this.scaleFactor,
|
|
775
|
-
bl: parseFloat(style.borderBottomLeftRadius) * this.scaleFactor,
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
if (Object.values(scaledRadii).some((r) => r > 0)) {
|
|
1091
|
+
if (isRounded) {
|
|
779
1092
|
this._createRoundedRectPath(
|
|
780
1093
|
this._tmpCtx,
|
|
781
1094
|
drawW,
|
|
782
1095
|
drawH,
|
|
783
|
-
scaledRadii
|
|
1096
|
+
scaledRadii,
|
|
784
1097
|
);
|
|
785
1098
|
this._tmpCtx.clip();
|
|
786
1099
|
}
|
|
@@ -794,7 +1107,7 @@ const liquidGL = (() => {
|
|
|
794
1107
|
0,
|
|
795
1108
|
0,
|
|
796
1109
|
drawW,
|
|
797
|
-
drawH
|
|
1110
|
+
drawH,
|
|
798
1111
|
);
|
|
799
1112
|
|
|
800
1113
|
this._tmpCtx.drawImage(vid, 0, 0, drawW, drawH);
|
|
@@ -804,40 +1117,6 @@ const liquidGL = (() => {
|
|
|
804
1117
|
return;
|
|
805
1118
|
}
|
|
806
1119
|
|
|
807
|
-
const drawX = Math.round(texX);
|
|
808
|
-
const drawY = Math.round(texY);
|
|
809
|
-
|
|
810
|
-
if (drawW <= 0 || drawH <= 0) return;
|
|
811
|
-
|
|
812
|
-
const maxW = this.textureWidth;
|
|
813
|
-
const maxH = this.textureHeight;
|
|
814
|
-
let dstX = drawX;
|
|
815
|
-
let dstY = drawY;
|
|
816
|
-
let srcX = 0,
|
|
817
|
-
srcY = 0,
|
|
818
|
-
updW = drawW,
|
|
819
|
-
updH = drawH;
|
|
820
|
-
|
|
821
|
-
if (dstX < 0) {
|
|
822
|
-
srcX = -dstX;
|
|
823
|
-
updW += dstX;
|
|
824
|
-
dstX = 0;
|
|
825
|
-
}
|
|
826
|
-
if (dstY < 0) {
|
|
827
|
-
srcY = -dstY;
|
|
828
|
-
updH += dstY;
|
|
829
|
-
dstY = 0;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
if (dstX + updW > maxW) {
|
|
833
|
-
updW = maxW - dstX;
|
|
834
|
-
}
|
|
835
|
-
if (dstY + updH > maxH) {
|
|
836
|
-
updH = maxH - dstY;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (updW <= 0 || updH <= 0) return;
|
|
840
|
-
|
|
841
1120
|
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
842
1121
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
843
1122
|
gl.texSubImage2D(
|
|
@@ -847,7 +1126,7 @@ const liquidGL = (() => {
|
|
|
847
1126
|
dstY,
|
|
848
1127
|
gl.RGBA,
|
|
849
1128
|
gl.UNSIGNED_BYTE,
|
|
850
|
-
this._tmpCanvas
|
|
1129
|
+
this._tmpCanvas,
|
|
851
1130
|
);
|
|
852
1131
|
});
|
|
853
1132
|
}
|
|
@@ -894,7 +1173,7 @@ const liquidGL = (() => {
|
|
|
894
1173
|
xInComposite,
|
|
895
1174
|
yInComposite,
|
|
896
1175
|
wInComposite,
|
|
897
|
-
hInComposite
|
|
1176
|
+
hInComposite,
|
|
898
1177
|
);
|
|
899
1178
|
}
|
|
900
1179
|
});
|
|
@@ -949,7 +1228,7 @@ const liquidGL = (() => {
|
|
|
949
1228
|
0,
|
|
950
1229
|
0,
|
|
951
1230
|
w,
|
|
952
|
-
h
|
|
1231
|
+
h,
|
|
953
1232
|
);
|
|
954
1233
|
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
955
1234
|
gl.texSubImage2D(
|
|
@@ -959,7 +1238,7 @@ const liquidGL = (() => {
|
|
|
959
1238
|
y,
|
|
960
1239
|
gl.RGBA,
|
|
961
1240
|
gl.UNSIGNED_BYTE,
|
|
962
|
-
eraseCanvas
|
|
1241
|
+
eraseCanvas,
|
|
963
1242
|
);
|
|
964
1243
|
}
|
|
965
1244
|
}
|
|
@@ -1037,7 +1316,7 @@ const liquidGL = (() => {
|
|
|
1037
1316
|
0,
|
|
1038
1317
|
0,
|
|
1039
1318
|
drawW,
|
|
1040
|
-
drawH
|
|
1319
|
+
drawH,
|
|
1041
1320
|
);
|
|
1042
1321
|
compositeVideos(this._compositeCtx, rect);
|
|
1043
1322
|
|
|
@@ -1046,7 +1325,7 @@ const liquidGL = (() => {
|
|
|
1046
1325
|
this._compositeCtx.translate(drawW / 2, drawH / 2);
|
|
1047
1326
|
if (style.transform !== "none") {
|
|
1048
1327
|
this._compositeCtx.transform(
|
|
1049
|
-
...this._parseTransform(style.transform)
|
|
1328
|
+
...this._parseTransform(style.transform),
|
|
1050
1329
|
);
|
|
1051
1330
|
}
|
|
1052
1331
|
this._compositeCtx.translate(-drawW / 2, -drawH / 2);
|
|
@@ -1062,7 +1341,7 @@ const liquidGL = (() => {
|
|
|
1062
1341
|
dstY,
|
|
1063
1342
|
gl.RGBA,
|
|
1064
1343
|
gl.UNSIGNED_BYTE,
|
|
1065
|
-
compositeCanvas
|
|
1344
|
+
compositeCanvas,
|
|
1066
1345
|
);
|
|
1067
1346
|
|
|
1068
1347
|
if (this._workerEnabled && meta._heavyAnim) {
|
|
@@ -1080,7 +1359,7 @@ const liquidGL = (() => {
|
|
|
1080
1359
|
dstX,
|
|
1081
1360
|
dstY,
|
|
1082
1361
|
updW,
|
|
1083
|
-
updH
|
|
1362
|
+
updH,
|
|
1084
1363
|
),
|
|
1085
1364
|
createImageBitmap(meta.lastCapture),
|
|
1086
1365
|
]).then(([snapBmp, dynBmp]) => {
|
|
@@ -1092,7 +1371,7 @@ const liquidGL = (() => {
|
|
|
1092
1371
|
snap: snapBmp,
|
|
1093
1372
|
dyn: dynBmp,
|
|
1094
1373
|
},
|
|
1095
|
-
[snapBmp, dynBmp]
|
|
1374
|
+
[snapBmp, dynBmp],
|
|
1096
1375
|
);
|
|
1097
1376
|
});
|
|
1098
1377
|
meta.prevDrawRect = { x: dstX, y: dstY, w: updW, h: updH };
|
|
@@ -1217,7 +1496,7 @@ const liquidGL = (() => {
|
|
|
1217
1496
|
try {
|
|
1218
1497
|
this._dynamicStyleSheet.insertRule(
|
|
1219
1498
|
rule,
|
|
1220
|
-
this._dynamicStyleSheet.cssRules.length
|
|
1499
|
+
this._dynamicStyleSheet.cssRules.length,
|
|
1221
1500
|
);
|
|
1222
1501
|
m.hoverClassName = className;
|
|
1223
1502
|
el.classList.add(className);
|
|
@@ -1227,7 +1506,7 @@ const liquidGL = (() => {
|
|
|
1227
1506
|
}
|
|
1228
1507
|
setDirty();
|
|
1229
1508
|
},
|
|
1230
|
-
{ passive: true }
|
|
1509
|
+
{ passive: true },
|
|
1231
1510
|
);
|
|
1232
1511
|
|
|
1233
1512
|
el.addEventListener("mouseleave", handleLeave, { passive: true });
|
|
@@ -1247,7 +1526,7 @@ const liquidGL = (() => {
|
|
|
1247
1526
|
if (
|
|
1248
1527
|
meta._heavyAnim &&
|
|
1249
1528
|
!meta._capturing &&
|
|
1250
|
-
ts - meta._lastCaptureTs >
|
|
1529
|
+
ts - meta._lastCaptureTs > RECAPTURE_INTERVAL_MS
|
|
1251
1530
|
) {
|
|
1252
1531
|
meta._lastCaptureTs = ts;
|
|
1253
1532
|
meta.needsRecapture = true;
|
|
@@ -1293,7 +1572,7 @@ const liquidGL = (() => {
|
|
|
1293
1572
|
if (m) m._heavyAnim = true;
|
|
1294
1573
|
startRealtime();
|
|
1295
1574
|
},
|
|
1296
|
-
{ passive: true }
|
|
1575
|
+
{ passive: true },
|
|
1297
1576
|
);
|
|
1298
1577
|
|
|
1299
1578
|
el.addEventListener(
|
|
@@ -1305,7 +1584,7 @@ const liquidGL = (() => {
|
|
|
1305
1584
|
if (!m._animating) startRealtime();
|
|
1306
1585
|
}
|
|
1307
1586
|
},
|
|
1308
|
-
{ passive: true }
|
|
1587
|
+
{ passive: true },
|
|
1309
1588
|
);
|
|
1310
1589
|
|
|
1311
1590
|
const stopRealtime = () => {
|
|
@@ -1454,9 +1733,12 @@ const liquidGL = (() => {
|
|
|
1454
1733
|
let overscrollY = 0;
|
|
1455
1734
|
let overscrollX = 0;
|
|
1456
1735
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1736
|
+
const vv = window.visualViewport;
|
|
1737
|
+
if (vv) {
|
|
1738
|
+
if (Math.abs(vv.scale - 1) < 0.01) {
|
|
1739
|
+
overscrollX = -vv.offsetLeft;
|
|
1740
|
+
overscrollY = -vv.offsetTop;
|
|
1741
|
+
}
|
|
1460
1742
|
} else {
|
|
1461
1743
|
const bodyStyle = window.getComputedStyle(document.body);
|
|
1462
1744
|
const htmlStyle = window.getComputedStyle(document.documentElement);
|
|
@@ -1641,23 +1923,47 @@ const liquidGL = (() => {
|
|
|
1641
1923
|
const getMaxTilt = () =>
|
|
1642
1924
|
Number.isFinite(this.options.tiltFactor) ? this.options.tiltFactor : 5;
|
|
1643
1925
|
|
|
1926
|
+
const getTiltEase = () =>
|
|
1927
|
+
Number.isFinite(this.options.tiltEase)
|
|
1928
|
+
? Math.max(0, this.options.tiltEase)
|
|
1929
|
+
: 400;
|
|
1930
|
+
|
|
1931
|
+
const TILT_TRACK_MS = 120;
|
|
1932
|
+
|
|
1933
|
+
const setTiltTransition = (ms) => {
|
|
1934
|
+
const value = `transform ${ms}ms cubic-bezier(0.33,1,0.68,1)`;
|
|
1935
|
+
this.el.style.transition = value;
|
|
1936
|
+
if (this._mirror) this._mirror.style.transition = value;
|
|
1937
|
+
if (this._shadowEl) this._shadowEl.style.transition = value;
|
|
1938
|
+
};
|
|
1939
|
+
|
|
1940
|
+
this._clearTiltEnterTimer = () => {
|
|
1941
|
+
if (this._tiltEnterTimer) {
|
|
1942
|
+
clearTimeout(this._tiltEnterTimer);
|
|
1943
|
+
this._tiltEnterTimer = null;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
|
|
1644
1947
|
this._applyTilt = (clientX, clientY) => {
|
|
1645
1948
|
if (!this._tiltInteracting) {
|
|
1646
1949
|
this._tiltInteracting = true;
|
|
1647
|
-
this.el.style.transition =
|
|
1648
|
-
"transform 0.12s cubic-bezier(0.33,1,0.68,1)";
|
|
1649
1950
|
this._createMirrorCanvas();
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1951
|
+
|
|
1952
|
+
const enterMs = getTiltEase();
|
|
1953
|
+
setTiltTransition(enterMs);
|
|
1954
|
+
|
|
1955
|
+
this._tiltEntering = enterMs > 0;
|
|
1956
|
+
this._clearTiltEnterTimer();
|
|
1957
|
+
if (this._tiltEntering) {
|
|
1958
|
+
this._tiltEnterTimer = setTimeout(() => {
|
|
1959
|
+
this._tiltEntering = false;
|
|
1960
|
+
this._tiltEnterTimer = null;
|
|
1961
|
+
setTiltTransition(TILT_TRACK_MS);
|
|
1962
|
+
}, enterMs);
|
|
1657
1963
|
}
|
|
1658
1964
|
}
|
|
1659
1965
|
|
|
1660
|
-
const r = this._baseRect || this.
|
|
1966
|
+
const r = this._baseRect || this._measureBaseRect();
|
|
1661
1967
|
const cx = r.left + r.width / 2;
|
|
1662
1968
|
const cy = r.top + r.height / 2;
|
|
1663
1969
|
|
|
@@ -1686,8 +1992,13 @@ const liquidGL = (() => {
|
|
|
1686
1992
|
|
|
1687
1993
|
const transformStr = `${overscrollCompensation}${baseTransform}perspective(800px) rotateX(${rotX}deg) rotateY(${rotY}deg)`;
|
|
1688
1994
|
|
|
1689
|
-
this.
|
|
1690
|
-
|
|
1995
|
+
if (this._tiltEntering) {
|
|
1996
|
+
this._easeTiltTo(rotX, rotY, getTiltEase());
|
|
1997
|
+
} else {
|
|
1998
|
+
this._cancelTiltEase();
|
|
1999
|
+
this.tiltX = rotX;
|
|
2000
|
+
this.tiltY = rotY;
|
|
2001
|
+
}
|
|
1691
2002
|
|
|
1692
2003
|
this.el.style.transformOrigin = `50% 50%`;
|
|
1693
2004
|
this.el.style.transform = transformStr;
|
|
@@ -1705,8 +2016,60 @@ const liquidGL = (() => {
|
|
|
1705
2016
|
this.renderer.render();
|
|
1706
2017
|
};
|
|
1707
2018
|
|
|
2019
|
+
this._cancelTiltEase = () => {
|
|
2020
|
+
if (this._tiltEaseRaf) {
|
|
2021
|
+
cancelAnimationFrame(this._tiltEaseRaf);
|
|
2022
|
+
this._tiltEaseRaf = null;
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
|
|
2026
|
+
this._easeTiltTo = (toX, toY, duration) => {
|
|
2027
|
+
this._cancelTiltEase();
|
|
2028
|
+
|
|
2029
|
+
const fromX = this.tiltX;
|
|
2030
|
+
const fromY = this.tiltY;
|
|
2031
|
+
if (fromX === toX && fromY === toY) return;
|
|
2032
|
+
|
|
2033
|
+
if (!duration || duration <= 0) {
|
|
2034
|
+
this.tiltX = toX;
|
|
2035
|
+
this.tiltY = toY;
|
|
2036
|
+
this.renderer.render();
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
const start =
|
|
2041
|
+
typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
2042
|
+
|
|
2043
|
+
const step = (now) => {
|
|
2044
|
+
const elapsed =
|
|
2045
|
+
(typeof now === "number"
|
|
2046
|
+
? now
|
|
2047
|
+
: typeof performance !== "undefined"
|
|
2048
|
+
? performance.now()
|
|
2049
|
+
: Date.now()) - start;
|
|
2050
|
+
const t = Math.min(1, elapsed / duration);
|
|
2051
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
2052
|
+
|
|
2053
|
+
this.tiltX = fromX + (toX - fromX) * eased;
|
|
2054
|
+
this.tiltY = fromY + (toY - fromY) * eased;
|
|
2055
|
+
this.renderer.render();
|
|
2056
|
+
|
|
2057
|
+
if (t < 1) {
|
|
2058
|
+
this._tiltEaseRaf = requestAnimationFrame(step);
|
|
2059
|
+
} else {
|
|
2060
|
+
this._tiltEaseRaf = null;
|
|
2061
|
+
this.tiltX = toX;
|
|
2062
|
+
this.tiltY = toY;
|
|
2063
|
+
this.renderer.render();
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
|
|
2067
|
+
this._tiltEaseRaf = requestAnimationFrame(step);
|
|
2068
|
+
};
|
|
2069
|
+
|
|
1708
2070
|
this._smoothReset = () => {
|
|
1709
|
-
|
|
2071
|
+
const restMs = getTiltEase();
|
|
2072
|
+
setTiltTransition(restMs);
|
|
1710
2073
|
this.el.style.transformOrigin = `50% 50%`;
|
|
1711
2074
|
const baseRest =
|
|
1712
2075
|
this._savedTransform && this._savedTransform !== "none"
|
|
@@ -1726,13 +2089,11 @@ const liquidGL = (() => {
|
|
|
1726
2089
|
|
|
1727
2090
|
this.el.style.transform = `${overscrollCompensation}${baseRest}perspective(800px) rotateX(0deg) rotateY(0deg)`;
|
|
1728
2091
|
|
|
1729
|
-
this.
|
|
1730
|
-
this.
|
|
1731
|
-
this.
|
|
2092
|
+
this._tiltEntering = false;
|
|
2093
|
+
this._clearTiltEnterTimer();
|
|
2094
|
+
this._easeTiltTo(0, 0, getTiltEase());
|
|
1732
2095
|
|
|
1733
2096
|
if (this._mirror) {
|
|
1734
|
-
this._mirror.style.transition =
|
|
1735
|
-
"transform 0.4s cubic-bezier(0.33, 1, 0.68, 1)";
|
|
1736
2097
|
this._mirror.style.transformOrigin = this._pivotOrigin || "50% 50%";
|
|
1737
2098
|
this._mirror.style.transform = `${baseRest}perspective(800px) rotateX(0deg) rotateY(0deg)`;
|
|
1738
2099
|
const clean = () => {
|
|
@@ -1742,12 +2103,10 @@ const liquidGL = (() => {
|
|
|
1742
2103
|
this._mirror.addEventListener("transitionend", clean, {
|
|
1743
2104
|
once: true,
|
|
1744
2105
|
});
|
|
1745
|
-
this._resetCleanupTimer = setTimeout(clean,
|
|
2106
|
+
this._resetCleanupTimer = setTimeout(clean, restMs + 50);
|
|
1746
2107
|
}
|
|
1747
2108
|
|
|
1748
2109
|
if (this._shadowEl) {
|
|
1749
|
-
this._shadowEl.style.transition =
|
|
1750
|
-
"transform 0.4s cubic-bezier(0.33,1,0.68,1)";
|
|
1751
2110
|
this._shadowEl.style.transformOrigin = `50% 50%`;
|
|
1752
2111
|
this._shadowEl.style.transform = `${baseRest}perspective(800px) rotateX(0deg) rotateY(0deg)`;
|
|
1753
2112
|
}
|
|
@@ -1766,7 +2125,7 @@ const liquidGL = (() => {
|
|
|
1766
2125
|
this._tiltInteracting = false;
|
|
1767
2126
|
this._createMirrorCanvas();
|
|
1768
2127
|
|
|
1769
|
-
const r = this._baseRect || this.
|
|
2128
|
+
const r = this._baseRect || this._measureBaseRect();
|
|
1770
2129
|
const cx = r.left + r.width / 2;
|
|
1771
2130
|
const cy = r.top + r.height / 2;
|
|
1772
2131
|
|
|
@@ -1853,6 +2212,11 @@ const liquidGL = (() => {
|
|
|
1853
2212
|
|
|
1854
2213
|
_unbindTiltHandlers() {
|
|
1855
2214
|
if (!this._tiltHandlersBound) return;
|
|
2215
|
+
if (this._cancelTiltEase) this._cancelTiltEase();
|
|
2216
|
+
if (this._clearTiltEnterTimer) this._clearTiltEnterTimer();
|
|
2217
|
+
this._tiltEntering = false;
|
|
2218
|
+
this.tiltX = 0;
|
|
2219
|
+
this.tiltY = 0;
|
|
1856
2220
|
this.el.removeEventListener("mouseenter", this._onMouseEnter.bind(this));
|
|
1857
2221
|
this.el.removeEventListener("mousemove", this._onMouseMove.bind(this));
|
|
1858
2222
|
document.removeEventListener("mousemove", this._boundCheckLeave);
|
|
@@ -1872,8 +2236,26 @@ const liquidGL = (() => {
|
|
|
1872
2236
|
this.renderer.render();
|
|
1873
2237
|
}
|
|
1874
2238
|
|
|
2239
|
+
_measureBaseRect() {
|
|
2240
|
+
const tilted =
|
|
2241
|
+
this.tiltX !== 0 || this.tiltY !== 0 || !!this._tiltEaseRaf;
|
|
2242
|
+
if (!tilted) return this.el.getBoundingClientRect();
|
|
2243
|
+
|
|
2244
|
+
const prevTransition = this.el.style.transition;
|
|
2245
|
+
const prevTransform = this.el.style.transform;
|
|
2246
|
+
|
|
2247
|
+
this.el.style.transition = "none";
|
|
2248
|
+
this.el.style.transform = this._savedTransform || "";
|
|
2249
|
+
const rect = this.el.getBoundingClientRect();
|
|
2250
|
+
|
|
2251
|
+
this.el.style.transform = prevTransform;
|
|
2252
|
+
this.el.style.transition = prevTransition;
|
|
2253
|
+
|
|
2254
|
+
return rect;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
1875
2257
|
_createMirrorCanvas() {
|
|
1876
|
-
this._baseRect = this.
|
|
2258
|
+
this._baseRect = this._measureBaseRect();
|
|
1877
2259
|
if (this._mirror) return;
|
|
1878
2260
|
this._mirror = document.createElement("canvas");
|
|
1879
2261
|
Object.assign(this._mirror.style, {
|
|
@@ -1891,9 +2273,9 @@ const liquidGL = (() => {
|
|
|
1891
2273
|
|
|
1892
2274
|
const updateClip = () => {
|
|
1893
2275
|
if (this._mirrorActive) {
|
|
1894
|
-
this._baseRect = this._baseRect || this.
|
|
2276
|
+
this._baseRect = this._baseRect || this._measureBaseRect();
|
|
1895
2277
|
}
|
|
1896
|
-
const r = this._baseRect || this.
|
|
2278
|
+
const r = this._baseRect || this._measureBaseRect();
|
|
1897
2279
|
const radius = `${this.radiusCss}px`;
|
|
1898
2280
|
this._mirror.style.clipPath = `inset(${r.top}px ${
|
|
1899
2281
|
innerWidth - r.right
|
|
@@ -1934,6 +2316,7 @@ const liquidGL = (() => {
|
|
|
1934
2316
|
snapshot: "body",
|
|
1935
2317
|
resolution: 2.0,
|
|
1936
2318
|
refraction: 0.01,
|
|
2319
|
+
aberration: 0,
|
|
1937
2320
|
bevelDepth: 0.08,
|
|
1938
2321
|
bevelWidth: 0.15,
|
|
1939
2322
|
frost: 0,
|
|
@@ -1942,6 +2325,7 @@ const liquidGL = (() => {
|
|
|
1942
2325
|
reveal: "fade",
|
|
1943
2326
|
tilt: false,
|
|
1944
2327
|
tiltFactor: 5,
|
|
2328
|
+
tiltEase: 400,
|
|
1945
2329
|
magnify: 1,
|
|
1946
2330
|
on: {},
|
|
1947
2331
|
};
|
|
@@ -1960,7 +2344,7 @@ const liquidGL = (() => {
|
|
|
1960
2344
|
|
|
1961
2345
|
if (noWebGL) {
|
|
1962
2346
|
console.warn(
|
|
1963
|
-
"liquidGL: WebGL not available – falling back to CSS backdrop-filter."
|
|
2347
|
+
"liquidGL: WebGL not available – falling back to CSS backdrop-filter.",
|
|
1964
2348
|
);
|
|
1965
2349
|
const fallbackNodes = document.querySelectorAll(options.target);
|
|
1966
2350
|
fallbackNodes.forEach((node) => {
|
|
@@ -1984,13 +2368,13 @@ const liquidGL = (() => {
|
|
|
1984
2368
|
const nodeList = document.querySelectorAll(options.target);
|
|
1985
2369
|
if (!nodeList || nodeList.length === 0) {
|
|
1986
2370
|
console.warn(
|
|
1987
|
-
`liquidGL: Target element(s) '${options.target}' not found
|
|
2371
|
+
`liquidGL: Target element(s) '${options.target}' not found.`,
|
|
1988
2372
|
);
|
|
1989
2373
|
return;
|
|
1990
2374
|
}
|
|
1991
2375
|
|
|
1992
2376
|
const instances = Array.from(nodeList).map((el) =>
|
|
1993
|
-
renderer.addLens(el, options)
|
|
2377
|
+
renderer.addLens(el, options),
|
|
1994
2378
|
);
|
|
1995
2379
|
|
|
1996
2380
|
if (!renderer._rafId && !renderer.useExternalTicker) {
|
|
@@ -2023,15 +2407,15 @@ const liquidGL = (() => {
|
|
|
2023
2407
|
const renderer = window.__liquidGLRenderer__;
|
|
2024
2408
|
if (!renderer) {
|
|
2025
2409
|
console.warn(
|
|
2026
|
-
"liquidGL: Please initialize liquidGL *before* calling syncWith()."
|
|
2410
|
+
"liquidGL: Please initialize liquidGL *before* calling syncWith().",
|
|
2027
2411
|
);
|
|
2028
2412
|
return;
|
|
2029
2413
|
}
|
|
2030
2414
|
|
|
2031
|
-
const G = window.gsap;
|
|
2415
|
+
const G = config.gsap === false ? null : config.gsap || window.gsap;
|
|
2032
2416
|
const L = window.Lenis;
|
|
2033
2417
|
const LS = window.LocomotiveScroll;
|
|
2034
|
-
const ST = G ? G.ScrollTrigger : null;
|
|
2418
|
+
const ST = G ? config.ScrollTrigger || G.ScrollTrigger : null;
|
|
2035
2419
|
|
|
2036
2420
|
let lenis = config.lenis;
|
|
2037
2421
|
let loco = config.locomotiveScroll;
|
|
@@ -2103,7 +2487,6 @@ const liquidGL = (() => {
|
|
|
2103
2487
|
|
|
2104
2488
|
return { lenis, locomotiveScroll: loco };
|
|
2105
2489
|
};
|
|
2106
|
-
|
|
2107
2490
|
return liquidGL;
|
|
2108
2491
|
})();
|
|
2109
2492
|
|