@widelab-nc/widelab 1.1.37 → 1.1.38
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/index.js +4 -2
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/src/index.js +247 -40
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -35,6 +35,68 @@ function scrollToHashOnLoad(offsetY = 0) {
|
|
|
35
35
|
|
|
36
36
|
// Functions definitions
|
|
37
37
|
|
|
38
|
+
// === Skills: ScrollTrigger + GSAP init (definition only) ===
|
|
39
|
+
// You will call this yourself later, e.g. initSkillsTriggers({ section: '#skills' })
|
|
40
|
+
// Client logos reveal inside a section (definition only; you call it later)
|
|
41
|
+
function initClientLogosAnimation(rootEl, userOpts = {}) {
|
|
42
|
+
// ---- guards ----
|
|
43
|
+
if (!window.gsap || !window.ScrollTrigger) {
|
|
44
|
+
console.warn('[client-logos] GSAP/ScrollTrigger missing');
|
|
45
|
+
return { ok: false, reason: 'gsap-or-plugin-missing' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const gsap = window.gsap;
|
|
49
|
+
const ScrollTrigger = window.ScrollTrigger;
|
|
50
|
+
try { gsap.registerPlugin(ScrollTrigger); } catch (_) {}
|
|
51
|
+
|
|
52
|
+
// ---- config ----
|
|
53
|
+
const opts = {
|
|
54
|
+
// adjust these to your real structure if needed
|
|
55
|
+
listSelector: '.clients_wrapper-bento',
|
|
56
|
+
itemSelector: '.clients_wrapper-bento .bento_item, .client-logo, .logo',
|
|
57
|
+
start: 'top 80%',
|
|
58
|
+
end: 'bottom top',
|
|
59
|
+
markers: false,
|
|
60
|
+
...userOpts
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ---- scope ----
|
|
64
|
+
const root = rootEl && rootEl.nodeType === 1 ? rootEl : document;
|
|
65
|
+
const list = root.querySelector(opts.listSelector);
|
|
66
|
+
const items = list ? Array.from(list.querySelectorAll(opts.itemSelector)) : [];
|
|
67
|
+
|
|
68
|
+
// nothing to animate? bail quietly (prevents "GSAP target … not found")
|
|
69
|
+
if (!list || !items.length) {
|
|
70
|
+
return { ok: false, reason: 'targets-not-found', listFound: !!list, count: items.length };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// kill old triggers bound to this list (idempotent reruns)
|
|
74
|
+
ScrollTrigger.getAll().forEach(st => {
|
|
75
|
+
if (st && st.trigger === list) st.kill(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// ---- animation ----
|
|
79
|
+
gsap.from(items, {
|
|
80
|
+
y: 16,
|
|
81
|
+
opacity: 0,
|
|
82
|
+
duration: 0.6,
|
|
83
|
+
ease: 'power2.out',
|
|
84
|
+
stagger: 0.06,
|
|
85
|
+
scrollTrigger: {
|
|
86
|
+
trigger: list,
|
|
87
|
+
start: opts.start,
|
|
88
|
+
end: opts.end,
|
|
89
|
+
toggleActions: 'play none none reverse',
|
|
90
|
+
markers: opts.markers,
|
|
91
|
+
invalidateOnRefresh: true
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// refresh after layout settles
|
|
96
|
+
requestAnimationFrame(() => { try { ScrollTrigger.refresh(); } catch(_) {} });
|
|
97
|
+
|
|
98
|
+
return { ok: true, count: items.length };
|
|
99
|
+
}
|
|
38
100
|
// Contact form open animation
|
|
39
101
|
let contactFormOpen;
|
|
40
102
|
function contactFormOpenAnimation() {
|
|
@@ -239,6 +301,114 @@ if (infoBar) {
|
|
|
239
301
|
});
|
|
240
302
|
}
|
|
241
303
|
|
|
304
|
+
// === SKILLS TRIGGER HARD-FIX (drop-in) ===
|
|
305
|
+
(function () {
|
|
306
|
+
const DEBUG = false; // flip to true to see console + markers
|
|
307
|
+
|
|
308
|
+
if (!window.gsap || !window.ScrollTrigger) {
|
|
309
|
+
console.error('[skills-fix] GSAP/ScrollTrigger missing');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
313
|
+
|
|
314
|
+
// 1) Pick the VISIBLE "skills" section (ignore duplicates/hidden)
|
|
315
|
+
const candidates = Array.from(document.querySelectorAll('#skills, .section.is-skills'));
|
|
316
|
+
const el = candidates.find(n => {
|
|
317
|
+
const r = n.getBoundingClientRect();
|
|
318
|
+
const style = getComputedStyle(n);
|
|
319
|
+
return r.width > 0 && r.height > 0 && style.display !== 'none' && style.visibility !== 'hidden';
|
|
320
|
+
});
|
|
321
|
+
if (!el) {
|
|
322
|
+
console.error('[skills-fix] skills section not found or not visible');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// 2) Diagnose ancestors that break ScrollTrigger (transform / overflow != visible)
|
|
327
|
+
const badAncestors = [];
|
|
328
|
+
let p = el.parentElement;
|
|
329
|
+
while (p && p !== document.body) {
|
|
330
|
+
const cs = getComputedStyle(p);
|
|
331
|
+
const badTransform = cs.transform && cs.transform !== 'none';
|
|
332
|
+
const badOverflow = (cs.overflow !== 'visible' || cs.overflowY !== 'visible' || cs.overflowX !== 'visible');
|
|
333
|
+
if (badTransform || badOverflow) badAncestors.push({ node: p, badTransform, badOverflow });
|
|
334
|
+
p = p.parentElement;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 3) Neutralize only the dangerous bits, minimally
|
|
338
|
+
badAncestors.forEach(({ node, badTransform }) => {
|
|
339
|
+
// overflow hidden na wrapperach zostawiamy (często layout), ScrollTrigger sobie radzi;
|
|
340
|
+
// PRZEWAŻNIE zabija go transform na rodzicu — zdejmujemy go klasą.
|
|
341
|
+
if (badTransform) node.classList.add('st-fix-transform');
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// 4) Inject minimal CSS to neutralize transforms (non-destructive)
|
|
345
|
+
const styleTag = document.createElement('style');
|
|
346
|
+
styleTag.textContent = `
|
|
347
|
+
.st-fix-transform { transform: none !important; }
|
|
348
|
+
`;
|
|
349
|
+
document.head.appendChild(styleTag);
|
|
350
|
+
|
|
351
|
+
// 5) Kill duplicated triggers on the same element
|
|
352
|
+
ScrollTrigger.getAll().forEach(st => { if (st.trigger === el) st.kill(false); });
|
|
353
|
+
|
|
354
|
+
// 6) The exact action you expect
|
|
355
|
+
const run = (target) => {
|
|
356
|
+
try { window.sectionBgChange && window.sectionBgChange(target); } catch (e) {}
|
|
357
|
+
try { window.badgeColorChange && window.badgeColorChange(target); } catch (e) {}
|
|
358
|
+
if (DEBUG) console.log('[skills-fix] run()', target);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// 7) Fire immediately if already on screen (new layout often loads in-view)
|
|
362
|
+
const r = el.getBoundingClientRect();
|
|
363
|
+
if (r.top < innerHeight && r.bottom > 0) run(el);
|
|
364
|
+
|
|
365
|
+
// 8) Create robust trigger (same feel as old), with optional markers
|
|
366
|
+
ScrollTrigger.create({
|
|
367
|
+
trigger: el,
|
|
368
|
+
start: 'top 80%',
|
|
369
|
+
end: 'bottom top',
|
|
370
|
+
onEnter: self => run(self.trigger),
|
|
371
|
+
onEnterBack: self => run(self.trigger),
|
|
372
|
+
markers: !!DEBUG,
|
|
373
|
+
fastScrollEnd: true,
|
|
374
|
+
invalidateOnRefresh: true
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// 9) Refresh after things that commonly shift layout on new home
|
|
378
|
+
const doRefresh = () => {
|
|
379
|
+
if (DEBUG) console.log('[skills-fix] refresh()');
|
|
380
|
+
ScrollTrigger.refresh();
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// after images
|
|
384
|
+
if (document.readyState === 'complete') requestAnimationFrame(doRefresh);
|
|
385
|
+
else window.addEventListener('load', () => requestAnimationFrame(doRefresh));
|
|
386
|
+
|
|
387
|
+
// after Webflow IX (if present)
|
|
388
|
+
try {
|
|
389
|
+
if (window.Webflow && Webflow.require) {
|
|
390
|
+
const ix2 = Webflow.require('ix2');
|
|
391
|
+
if (ix2 && ix2.ready) setTimeout(doRefresh, 300); // allow IX to apply styles
|
|
392
|
+
}
|
|
393
|
+
} catch (e) {}
|
|
394
|
+
|
|
395
|
+
// after Alpine renders (x-init etc.)
|
|
396
|
+
document.addEventListener('alpine:init', () => setTimeout(doRefresh, 100));
|
|
397
|
+
document.addEventListener('DOMContentLoaded', () => setTimeout(doRefresh, 150));
|
|
398
|
+
|
|
399
|
+
// As a last resort: one more refresh when user starts scrolling
|
|
400
|
+
let firstScroll = false;
|
|
401
|
+
const onFirstScroll = () => { if (!firstScroll) { firstScroll = true; doRefresh(); } };
|
|
402
|
+
window.addEventListener('scroll', onFirstScroll, { passive: true, once: true });
|
|
403
|
+
|
|
404
|
+
if (DEBUG) {
|
|
405
|
+
console.group('[skills-fix] diagnostics');
|
|
406
|
+
console.log('picked element:', el);
|
|
407
|
+
console.log('bad ancestors:', badAncestors);
|
|
408
|
+
console.groupEnd();
|
|
409
|
+
}
|
|
410
|
+
})();
|
|
411
|
+
|
|
242
412
|
// Header scroll trigger
|
|
243
413
|
window.standardHeaderScrollAnimation = function() {
|
|
244
414
|
ScrollTrigger.create({
|
|
@@ -360,49 +530,65 @@ window.customCursorTextUpdate = function(el) {
|
|
|
360
530
|
);
|
|
361
531
|
}
|
|
362
532
|
|
|
533
|
+
|
|
534
|
+
|
|
363
535
|
// Case study mask animation + pill fade-in (last step)
|
|
364
536
|
window.caseStudyInteraction = function (e) {
|
|
365
|
-
// Video mask + pill timeline
|
|
366
537
|
const mask = e.querySelector('.portfolio_item_image-mask');
|
|
367
538
|
const pill = e.querySelector('.cs_pill');
|
|
539
|
+
const triggerEl = e.querySelector('.portfolio_item_image-wrapper') || e; // safer trigger
|
|
540
|
+
|
|
541
|
+
// --- iOS detection (reliable enough for layout fallbacks) ---
|
|
542
|
+
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
543
|
+
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
|
544
|
+
|
|
545
|
+
// Ensure initial states
|
|
546
|
+
if (pill) gsap.set(pill, { autoAlpha: 0 }); // hide pill
|
|
547
|
+
if (mask) {
|
|
548
|
+
// Use transform-based reveal, much more reliable on iOS
|
|
549
|
+
gsap.set(mask, {
|
|
550
|
+
scaleX: 1,
|
|
551
|
+
transformOrigin: 'left center',
|
|
552
|
+
willChange: 'transform'
|
|
553
|
+
});
|
|
554
|
+
}
|
|
368
555
|
|
|
369
|
-
//
|
|
370
|
-
const tl = gsap.timeline({
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
// Ensure pill starts hidden
|
|
376
|
-
if (pill) gsap.set(pill, { autoAlpha: 0 }); // autoAlpha controls visibility + opacity
|
|
377
|
-
|
|
378
|
-
// Mask reveals first...
|
|
379
|
-
tl.from(mask, {
|
|
380
|
-
width: '100%',
|
|
381
|
-
duration: 0.35
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// ...then pill fades in as the last step
|
|
556
|
+
// Timeline: mask reveal -> pill fade-in
|
|
557
|
+
const tl = gsap.timeline({ paused: true, defaults: { ease: 'loader2' } });
|
|
558
|
+
if (mask) {
|
|
559
|
+
// Reveal by scaling mask from full (1) to zero (0)
|
|
560
|
+
tl.to(mask, { scaleX: 0, duration: 0.35 });
|
|
561
|
+
}
|
|
385
562
|
if (pill) {
|
|
386
|
-
tl.to(pill, {
|
|
387
|
-
autoAlpha: 1,
|
|
388
|
-
duration: 0.25
|
|
389
|
-
});
|
|
563
|
+
tl.to(pill, { autoAlpha: 1, duration: 0.25 }, '>-0.05');
|
|
390
564
|
}
|
|
391
565
|
|
|
392
|
-
//
|
|
566
|
+
// Scroll trigger (use wrapper, not absolute mask)
|
|
393
567
|
ScrollTrigger.create({
|
|
394
568
|
animation: tl,
|
|
395
|
-
trigger:
|
|
569
|
+
trigger: triggerEl,
|
|
396
570
|
start: 'top 80%',
|
|
397
|
-
|
|
571
|
+
once: true
|
|
398
572
|
});
|
|
399
573
|
|
|
400
|
-
// Video
|
|
574
|
+
// --- Video handling ---
|
|
401
575
|
const videoElements = e.getElementsByTagName('video');
|
|
576
|
+
|
|
402
577
|
if (window.innerWidth > 992) {
|
|
403
578
|
const videoElement = videoElements.length > 0 ? videoElements[0] : null;
|
|
404
579
|
if (!videoElement) return;
|
|
405
580
|
|
|
581
|
+
// Force Safari compositing on the video
|
|
582
|
+
videoElement.style.transform = 'translateZ(0)';
|
|
583
|
+
|
|
584
|
+
// Desktop: hover play/pause
|
|
585
|
+
const hoverIN = () => videoElement.play();
|
|
586
|
+
const hoverOUT = () => {
|
|
587
|
+
videoElement.pause();
|
|
588
|
+
// videoElement.currentTime = 0; // optional reset
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// Pause once metadata is ready
|
|
406
592
|
videoElement.addEventListener('loadeddata', () => {
|
|
407
593
|
videoElement.pause();
|
|
408
594
|
});
|
|
@@ -410,26 +596,45 @@ window.caseStudyInteraction = function (e) {
|
|
|
410
596
|
e.addEventListener('mouseenter', hoverIN, false);
|
|
411
597
|
e.addEventListener('mouseleave', hoverOUT, false);
|
|
412
598
|
|
|
413
|
-
function hoverIN() {
|
|
414
|
-
videoElement.play();
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function hoverOUT() {
|
|
418
|
-
videoElement.pause();
|
|
419
|
-
// videoElement.currentTime = 0;
|
|
420
|
-
}
|
|
421
599
|
} else {
|
|
422
|
-
// Mobile
|
|
423
|
-
Array.from(videoElements).forEach((
|
|
424
|
-
const poster =
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
600
|
+
// Mobile
|
|
601
|
+
Array.from(videoElements).forEach((v) => {
|
|
602
|
+
const poster = v.getAttribute('poster');
|
|
603
|
+
|
|
604
|
+
if (isIOS) {
|
|
605
|
+
// iOS: safest is to replace <video> with an <img> using the poster
|
|
606
|
+
if (poster) {
|
|
607
|
+
const img = new Image();
|
|
608
|
+
img.src = poster;
|
|
609
|
+
img.alt = '';
|
|
610
|
+
img.className = v.className; // preserve classes if needed
|
|
611
|
+
// Keep sizing via CSS from the video
|
|
612
|
+
v.replaceWith(img);
|
|
613
|
+
} else {
|
|
614
|
+
// If no poster, fall back to pausing & keeping sources
|
|
615
|
+
v.removeAttribute('autoplay');
|
|
616
|
+
v.pause();
|
|
617
|
+
v.preload = 'none';
|
|
618
|
+
}
|
|
619
|
+
} else {
|
|
620
|
+
// Android: keep poster only, but DON'T remove sources (some browsers need metadata for sizing)
|
|
621
|
+
v.removeAttribute('autoplay');
|
|
622
|
+
v.pause();
|
|
623
|
+
v.preload = 'none';
|
|
624
|
+
// Keeping sources avoids 0-height issues in some engines
|
|
625
|
+
// v.load(); // optional: only if you observe stale frames
|
|
626
|
+
}
|
|
429
627
|
});
|
|
430
628
|
}
|
|
629
|
+
|
|
630
|
+
// After images/posters load, refresh ScrollTrigger (prevents late layout causing missed triggers)
|
|
631
|
+
// Safe to call; GSAP debounces
|
|
632
|
+
setTimeout(() => ScrollTrigger.refresh(), 100);
|
|
431
633
|
};
|
|
432
634
|
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
|
|
433
638
|
// Reviews slider
|
|
434
639
|
window.reviewsSlider = function(e) {
|
|
435
640
|
// Put content of static slide into list
|
|
@@ -2023,6 +2228,7 @@ window.closeInfoBar = function() {
|
|
|
2023
2228
|
lenis.destroy();
|
|
2024
2229
|
}
|
|
2025
2230
|
customGsapEasing();
|
|
2231
|
+
|
|
2026
2232
|
reloadOnBack();
|
|
2027
2233
|
revealFooter();
|
|
2028
2234
|
pageLoadAnimation();
|
|
@@ -2031,4 +2237,5 @@ window.closeInfoBar = function() {
|
|
|
2031
2237
|
contactFormOpenAnimation();
|
|
2032
2238
|
disableScrollMenu();
|
|
2033
2239
|
scrollToHashOnLoad();
|
|
2240
|
+
initClientLogosAnimation();
|
|
2034
2241
|
});
|