@widelab-nc/widelab 1.1.37 → 1.1.39
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 +279 -93
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() {
|
|
@@ -142,50 +204,54 @@ function contactFormOpenAnimation() {
|
|
|
142
204
|
}
|
|
143
205
|
}
|
|
144
206
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
});
|
|
182
|
-
}
|
|
207
|
+
// Page load animation (no-op below 992px)
|
|
208
|
+
function pageLoadAnimation() {
|
|
209
|
+
if (window.innerWidth < 992) return; // guard for mobile/tablet
|
|
210
|
+
|
|
211
|
+
const loaderAnimationIn = gsap.timeline({});
|
|
212
|
+
loaderAnimationIn.add(function () {
|
|
213
|
+
history.scrollRestoration = 'manual';
|
|
214
|
+
lenis.scrollTo(0);
|
|
215
|
+
});
|
|
216
|
+
loaderAnimationIn.to(
|
|
217
|
+
'.loader_ract.is-left',
|
|
218
|
+
{
|
|
219
|
+
delay: 0.2,
|
|
220
|
+
duration: 0.7,
|
|
221
|
+
x: '100%',
|
|
222
|
+
ease: 'loader2',
|
|
223
|
+
},
|
|
224
|
+
'>'
|
|
225
|
+
);
|
|
226
|
+
loaderAnimationIn.to(
|
|
227
|
+
'.loader_ract.is-right',
|
|
228
|
+
{
|
|
229
|
+
duration: 0.7,
|
|
230
|
+
x: '-100%',
|
|
231
|
+
ease: 'loader2',
|
|
232
|
+
},
|
|
233
|
+
'<'
|
|
234
|
+
);
|
|
235
|
+
loaderAnimationIn.add(function () {
|
|
236
|
+
lenis.start();
|
|
237
|
+
}, '>');
|
|
238
|
+
loaderAnimationIn.to('.loader_wrapper', { display: 'none', duration: 0.1 }, '>');
|
|
239
|
+
// Keep your resize fix
|
|
240
|
+
window.addEventListener('resize', function () {
|
|
241
|
+
setTimeout(function () {
|
|
242
|
+
gsap.set('.loader_wrapper', { display: 'none' });
|
|
243
|
+
}, 50);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Page transition animation (no-op below 992px)
|
|
248
|
+
function pageTransitionAnimation() {
|
|
249
|
+
if (window.innerWidth < 992) return; // guard for mobile/tablet
|
|
183
250
|
|
|
184
|
-
// Page transition animation
|
|
185
|
-
function pageTransitionAnimation() {
|
|
186
251
|
const transitionLinks = document.querySelectorAll(
|
|
187
|
-
'a:not([
|
|
252
|
+
'a:not([href^="#"]):not([href^="javascript:"]):not([href=""]):not([href^="mailto:"]):not([href^="tel:"])'
|
|
188
253
|
);
|
|
254
|
+
|
|
189
255
|
transitionLinks.forEach((link) => {
|
|
190
256
|
if (link.getAttribute('target') !== '_blank') {
|
|
191
257
|
link.addEventListener('click', (e) => {
|
|
@@ -201,15 +267,12 @@ function contactFormOpenAnimation() {
|
|
|
201
267
|
duration: 0.4,
|
|
202
268
|
width: '0%',
|
|
203
269
|
ease: 'loader2',
|
|
204
|
-
})
|
|
205
|
-
'>';
|
|
206
|
-
// play the timeline
|
|
270
|
+
});
|
|
207
271
|
loaderAnimationOut.play();
|
|
208
272
|
});
|
|
209
273
|
}
|
|
210
274
|
});
|
|
211
|
-
|
|
212
|
-
|
|
275
|
+
}
|
|
213
276
|
// Header on scroll animation
|
|
214
277
|
const infoBar = document.querySelector('.info-bar');
|
|
215
278
|
const showHideHeader = gsap.timeline({ paused: true,})
|
|
@@ -239,6 +302,114 @@ if (infoBar) {
|
|
|
239
302
|
});
|
|
240
303
|
}
|
|
241
304
|
|
|
305
|
+
// === SKILLS TRIGGER HARD-FIX (drop-in) ===
|
|
306
|
+
(function () {
|
|
307
|
+
const DEBUG = false; // flip to true to see console + markers
|
|
308
|
+
|
|
309
|
+
if (!window.gsap || !window.ScrollTrigger) {
|
|
310
|
+
console.error('[skills-fix] GSAP/ScrollTrigger missing');
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
314
|
+
|
|
315
|
+
// 1) Pick the VISIBLE "skills" section (ignore duplicates/hidden)
|
|
316
|
+
const candidates = Array.from(document.querySelectorAll('#skills, .section.is-skills'));
|
|
317
|
+
const el = candidates.find(n => {
|
|
318
|
+
const r = n.getBoundingClientRect();
|
|
319
|
+
const style = getComputedStyle(n);
|
|
320
|
+
return r.width > 0 && r.height > 0 && style.display !== 'none' && style.visibility !== 'hidden';
|
|
321
|
+
});
|
|
322
|
+
if (!el) {
|
|
323
|
+
console.error('[skills-fix] skills section not found or not visible');
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 2) Diagnose ancestors that break ScrollTrigger (transform / overflow != visible)
|
|
328
|
+
const badAncestors = [];
|
|
329
|
+
let p = el.parentElement;
|
|
330
|
+
while (p && p !== document.body) {
|
|
331
|
+
const cs = getComputedStyle(p);
|
|
332
|
+
const badTransform = cs.transform && cs.transform !== 'none';
|
|
333
|
+
const badOverflow = (cs.overflow !== 'visible' || cs.overflowY !== 'visible' || cs.overflowX !== 'visible');
|
|
334
|
+
if (badTransform || badOverflow) badAncestors.push({ node: p, badTransform, badOverflow });
|
|
335
|
+
p = p.parentElement;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 3) Neutralize only the dangerous bits, minimally
|
|
339
|
+
badAncestors.forEach(({ node, badTransform }) => {
|
|
340
|
+
// overflow hidden na wrapperach zostawiamy (często layout), ScrollTrigger sobie radzi;
|
|
341
|
+
// PRZEWAŻNIE zabija go transform na rodzicu — zdejmujemy go klasą.
|
|
342
|
+
if (badTransform) node.classList.add('st-fix-transform');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// 4) Inject minimal CSS to neutralize transforms (non-destructive)
|
|
346
|
+
const styleTag = document.createElement('style');
|
|
347
|
+
styleTag.textContent = `
|
|
348
|
+
.st-fix-transform { transform: none !important; }
|
|
349
|
+
`;
|
|
350
|
+
document.head.appendChild(styleTag);
|
|
351
|
+
|
|
352
|
+
// 5) Kill duplicated triggers on the same element
|
|
353
|
+
ScrollTrigger.getAll().forEach(st => { if (st.trigger === el) st.kill(false); });
|
|
354
|
+
|
|
355
|
+
// 6) The exact action you expect
|
|
356
|
+
const run = (target) => {
|
|
357
|
+
try { window.sectionBgChange && window.sectionBgChange(target); } catch (e) {}
|
|
358
|
+
try { window.badgeColorChange && window.badgeColorChange(target); } catch (e) {}
|
|
359
|
+
if (DEBUG) console.log('[skills-fix] run()', target);
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// 7) Fire immediately if already on screen (new layout often loads in-view)
|
|
363
|
+
const r = el.getBoundingClientRect();
|
|
364
|
+
if (r.top < innerHeight && r.bottom > 0) run(el);
|
|
365
|
+
|
|
366
|
+
// 8) Create robust trigger (same feel as old), with optional markers
|
|
367
|
+
ScrollTrigger.create({
|
|
368
|
+
trigger: el,
|
|
369
|
+
start: 'top 80%',
|
|
370
|
+
end: 'bottom top',
|
|
371
|
+
onEnter: self => run(self.trigger),
|
|
372
|
+
onEnterBack: self => run(self.trigger),
|
|
373
|
+
markers: !!DEBUG,
|
|
374
|
+
fastScrollEnd: true,
|
|
375
|
+
invalidateOnRefresh: true
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// 9) Refresh after things that commonly shift layout on new home
|
|
379
|
+
const doRefresh = () => {
|
|
380
|
+
if (DEBUG) console.log('[skills-fix] refresh()');
|
|
381
|
+
ScrollTrigger.refresh();
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// after images
|
|
385
|
+
if (document.readyState === 'complete') requestAnimationFrame(doRefresh);
|
|
386
|
+
else window.addEventListener('load', () => requestAnimationFrame(doRefresh));
|
|
387
|
+
|
|
388
|
+
// after Webflow IX (if present)
|
|
389
|
+
try {
|
|
390
|
+
if (window.Webflow && Webflow.require) {
|
|
391
|
+
const ix2 = Webflow.require('ix2');
|
|
392
|
+
if (ix2 && ix2.ready) setTimeout(doRefresh, 300); // allow IX to apply styles
|
|
393
|
+
}
|
|
394
|
+
} catch (e) {}
|
|
395
|
+
|
|
396
|
+
// after Alpine renders (x-init etc.)
|
|
397
|
+
document.addEventListener('alpine:init', () => setTimeout(doRefresh, 100));
|
|
398
|
+
document.addEventListener('DOMContentLoaded', () => setTimeout(doRefresh, 150));
|
|
399
|
+
|
|
400
|
+
// As a last resort: one more refresh when user starts scrolling
|
|
401
|
+
let firstScroll = false;
|
|
402
|
+
const onFirstScroll = () => { if (!firstScroll) { firstScroll = true; doRefresh(); } };
|
|
403
|
+
window.addEventListener('scroll', onFirstScroll, { passive: true, once: true });
|
|
404
|
+
|
|
405
|
+
if (DEBUG) {
|
|
406
|
+
console.group('[skills-fix] diagnostics');
|
|
407
|
+
console.log('picked element:', el);
|
|
408
|
+
console.log('bad ancestors:', badAncestors);
|
|
409
|
+
console.groupEnd();
|
|
410
|
+
}
|
|
411
|
+
})();
|
|
412
|
+
|
|
242
413
|
// Header scroll trigger
|
|
243
414
|
window.standardHeaderScrollAnimation = function() {
|
|
244
415
|
ScrollTrigger.create({
|
|
@@ -360,76 +531,89 @@ window.customCursorTextUpdate = function(el) {
|
|
|
360
531
|
);
|
|
361
532
|
}
|
|
362
533
|
|
|
363
|
-
|
|
534
|
+
|
|
535
|
+
// Case study mask animation + pill fade-in (full, iOS-safe, L->R reveal)
|
|
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');
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
//
|
|
376
|
-
if (pill) gsap.set(pill, { autoAlpha: 0 });
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (pill) {
|
|
386
|
-
tl.to(pill, {
|
|
387
|
-
autoAlpha: 1,
|
|
388
|
-
duration: 0.25
|
|
539
|
+
const triggerEl = e.querySelector('.portfolio_item_image-wrapper') || e;
|
|
540
|
+
|
|
541
|
+
// iOS detection for safe fallbacks
|
|
542
|
+
const isIOS =
|
|
543
|
+
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
|
544
|
+
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
|
545
|
+
|
|
546
|
+
// --- Initial states ---
|
|
547
|
+
if (pill) gsap.set(pill, { autoAlpha: 0 });
|
|
548
|
+
if (mask) {
|
|
549
|
+
// We fully cover first, then shrink to reveal from LEFT to RIGHT.
|
|
550
|
+
// To reveal L->R we set origin to RIGHT and scaleX: 1 -> 0
|
|
551
|
+
gsap.set(mask, {
|
|
552
|
+
clearProps: 'width', // ignore any inline width
|
|
553
|
+
scaleX: 1,
|
|
554
|
+
transformOrigin: 'right center',
|
|
555
|
+
willChange: 'transform'
|
|
389
556
|
});
|
|
390
557
|
}
|
|
391
558
|
|
|
392
|
-
//
|
|
559
|
+
// --- Timeline: mask reveal then pill ---
|
|
560
|
+
const tl = gsap.timeline({ paused: true, defaults: { ease: 'loader2' } });
|
|
561
|
+
if (mask) tl.to(mask, { scaleX: 0, duration: 0.35 }); // L->R reveal
|
|
562
|
+
if (pill) tl.to(pill, { autoAlpha: 1, duration: 0.25 }, '>-0.05');
|
|
563
|
+
|
|
564
|
+
// --- Scroll trigger (use wrapper for reliable bounds on iOS) ---
|
|
393
565
|
ScrollTrigger.create({
|
|
394
566
|
animation: tl,
|
|
395
|
-
trigger:
|
|
567
|
+
trigger: triggerEl,
|
|
396
568
|
start: 'top 80%',
|
|
397
|
-
|
|
569
|
+
once: true,
|
|
398
570
|
});
|
|
399
571
|
|
|
400
|
-
// Video
|
|
572
|
+
// --- Video handling ---
|
|
401
573
|
const videoElements = e.getElementsByTagName('video');
|
|
574
|
+
|
|
402
575
|
if (window.innerWidth > 992) {
|
|
403
|
-
const
|
|
404
|
-
if (!
|
|
576
|
+
const video = videoElements.length ? videoElements[0] : null;
|
|
577
|
+
if (!video) return;
|
|
405
578
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
579
|
+
// Safari compositing hints
|
|
580
|
+
video.style.transform = 'translateZ(0)';
|
|
581
|
+
|
|
582
|
+
const hoverIN = () => video.play();
|
|
583
|
+
const hoverOUT = () => { video.pause(); /* video.currentTime = 0; */ };
|
|
409
584
|
|
|
585
|
+
video.addEventListener('loadeddata', () => { video.pause(); });
|
|
410
586
|
e.addEventListener('mouseenter', hoverIN, false);
|
|
411
587
|
e.addEventListener('mouseleave', hoverOUT, false);
|
|
412
588
|
|
|
413
|
-
function hoverIN() {
|
|
414
|
-
videoElement.play();
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function hoverOUT() {
|
|
418
|
-
videoElement.pause();
|
|
419
|
-
// videoElement.currentTime = 0;
|
|
420
|
-
}
|
|
421
589
|
} else {
|
|
422
|
-
// Mobile: keep
|
|
423
|
-
Array.from(videoElements).forEach((
|
|
424
|
-
const poster =
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
590
|
+
// Mobile: poster-only, but keep sources on Android; replace with <img> on iOS
|
|
591
|
+
Array.from(videoElements).forEach((v) => {
|
|
592
|
+
const poster = v.getAttribute('poster');
|
|
593
|
+
|
|
594
|
+
if (isIOS && poster) {
|
|
595
|
+
const img = new Image();
|
|
596
|
+
img.src = poster;
|
|
597
|
+
img.alt = '';
|
|
598
|
+
img.className = v.className;
|
|
599
|
+
// Keep sizing from CSS on wrapper; this prevents 0-height glitches
|
|
600
|
+
v.replaceWith(img);
|
|
601
|
+
} else {
|
|
602
|
+
// Android: keep sources to preserve intrinsic size and poster rendering
|
|
603
|
+
v.removeAttribute('autoplay');
|
|
604
|
+
v.pause();
|
|
605
|
+
v.preload = 'none';
|
|
606
|
+
// v.load(); // enable only if you notice stale frames
|
|
607
|
+
}
|
|
429
608
|
});
|
|
430
609
|
}
|
|
610
|
+
|
|
611
|
+
// After layout settles (posters/images), refresh triggers
|
|
612
|
+
setTimeout(() => ScrollTrigger.refresh(), 120);
|
|
431
613
|
};
|
|
432
614
|
|
|
615
|
+
|
|
616
|
+
|
|
433
617
|
// Reviews slider
|
|
434
618
|
window.reviewsSlider = function(e) {
|
|
435
619
|
// Put content of static slide into list
|
|
@@ -2023,6 +2207,7 @@ window.closeInfoBar = function() {
|
|
|
2023
2207
|
lenis.destroy();
|
|
2024
2208
|
}
|
|
2025
2209
|
customGsapEasing();
|
|
2210
|
+
|
|
2026
2211
|
reloadOnBack();
|
|
2027
2212
|
revealFooter();
|
|
2028
2213
|
pageLoadAnimation();
|
|
@@ -2031,4 +2216,5 @@ window.closeInfoBar = function() {
|
|
|
2031
2216
|
contactFormOpenAnimation();
|
|
2032
2217
|
disableScrollMenu();
|
|
2033
2218
|
scrollToHashOnLoad();
|
|
2219
|
+
initClientLogosAnimation();
|
|
2034
2220
|
});
|