@widelab-nc/widelab 1.1.38 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@widelab-nc/widelab",
3
- "version": "1.1.38",
3
+ "version": "1.1.39",
4
4
  "description": "Widelab starter template based on Finsweet template + add-ons.",
5
5
  "homepage": "https://widelab.co",
6
6
  "license": "ISC",
package/src/index.js CHANGED
@@ -204,50 +204,54 @@ function contactFormOpenAnimation() {
204
204
  }
205
205
  }
206
206
 
207
- // Page load animation
208
- function pageLoadAnimation() {
209
- const loaderAnimationIn = gsap.timeline({});
210
- loaderAnimationIn
211
- .add(function() {
212
- history.scrollRestoration = 'manual';
213
- lenis.scrollTo(0);
214
- })
215
- loaderAnimationIn.to(
216
- '.loader_ract.is-left',
217
- {
218
- delay: 0.2,
219
- duration: 0.7,
220
- x: '100%',
221
- ease: 'loader2',
222
- },
223
- '>'
224
- );
225
- loaderAnimationIn.to(
226
- '.loader_ract.is-right',
227
- {
228
- duration: 0.7,
229
- x: '-100%',
230
- ease: 'loader2',
231
- },
232
- '<'
233
- );
234
- loaderAnimationIn.add(function() {
235
- lenis.start();
236
- }, '>');
237
- loaderAnimationIn.to('.loader_wrapper', { display: 'none', duration: 0.1 }, '>');
238
- // Update on screen resize
239
- window.addEventListener('resize', function () {
240
- setTimeout(function () {
241
- gsap.set('.loader_wrapper', { display: 'none' });
242
- }, 50);
243
- });
244
- }
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
245
250
 
246
- // Page transition animation
247
- function pageTransitionAnimation() {
248
251
  const transitionLinks = document.querySelectorAll(
249
- 'a:not([/**/href^="#"]):not([href^="javascript:"]):not([href=""]):not([href^="mailto:"]):not([href^="tel:"])'
252
+ 'a:not([href^="#"]):not([href^="javascript:"]):not([href=""]):not([href^="mailto:"]):not([href^="tel:"])'
250
253
  );
254
+
251
255
  transitionLinks.forEach((link) => {
252
256
  if (link.getAttribute('target') !== '_blank') {
253
257
  link.addEventListener('click', (e) => {
@@ -263,15 +267,12 @@ function contactFormOpenAnimation() {
263
267
  duration: 0.4,
264
268
  width: '0%',
265
269
  ease: 'loader2',
266
- }),
267
- '>';
268
- // play the timeline
270
+ });
269
271
  loaderAnimationOut.play();
270
272
  });
271
273
  }
272
274
  });
273
- }
274
-
275
+ }
275
276
  // Header on scroll animation
276
277
  const infoBar = document.querySelector('.info-bar');
277
278
  const showHideHeader = gsap.timeline({ paused: true,})
@@ -531,110 +532,88 @@ window.customCursorTextUpdate = function(el) {
531
532
  }
532
533
 
533
534
 
534
-
535
- // Case study mask animation + pill fade-in (last step)
535
+ // Case study mask animation + pill fade-in (full, iOS-safe, L->R reveal)
536
536
  window.caseStudyInteraction = function (e) {
537
537
  const mask = e.querySelector('.portfolio_item_image-mask');
538
538
  const pill = e.querySelector('.cs_pill');
539
- const triggerEl = e.querySelector('.portfolio_item_image-wrapper') || e; // safer trigger
539
+ const triggerEl = e.querySelector('.portfolio_item_image-wrapper') || e;
540
540
 
541
- // --- iOS detection (reliable enough for layout fallbacks) ---
542
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
541
+ // iOS detection for safe fallbacks
542
+ const isIOS =
543
+ /iPad|iPhone|iPod/.test(navigator.userAgent) ||
543
544
  (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
544
545
 
545
- // Ensure initial states
546
- if (pill) gsap.set(pill, { autoAlpha: 0 }); // hide pill
546
+ // --- Initial states ---
547
+ if (pill) gsap.set(pill, { autoAlpha: 0 });
547
548
  if (mask) {
548
- // Use transform-based reveal, much more reliable on iOS
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
549
551
  gsap.set(mask, {
552
+ clearProps: 'width', // ignore any inline width
550
553
  scaleX: 1,
551
- transformOrigin: 'left center',
554
+ transformOrigin: 'right center',
552
555
  willChange: 'transform'
553
556
  });
554
557
  }
555
558
 
556
- // Timeline: mask reveal -> pill fade-in
559
+ // --- Timeline: mask reveal then pill ---
557
560
  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
- }
562
- if (pill) {
563
- tl.to(pill, { autoAlpha: 1, duration: 0.25 }, '>-0.05');
564
- }
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');
565
563
 
566
- // Scroll trigger (use wrapper, not absolute mask)
564
+ // --- Scroll trigger (use wrapper for reliable bounds on iOS) ---
567
565
  ScrollTrigger.create({
568
566
  animation: tl,
569
567
  trigger: triggerEl,
570
568
  start: 'top 80%',
571
- once: true
569
+ once: true,
572
570
  });
573
571
 
574
572
  // --- Video handling ---
575
573
  const videoElements = e.getElementsByTagName('video');
576
574
 
577
575
  if (window.innerWidth > 992) {
578
- const videoElement = videoElements.length > 0 ? videoElements[0] : null;
579
- if (!videoElement) return;
576
+ const video = videoElements.length ? videoElements[0] : null;
577
+ if (!video) return;
580
578
 
581
- // Force Safari compositing on the video
582
- videoElement.style.transform = 'translateZ(0)';
579
+ // Safari compositing hints
580
+ video.style.transform = 'translateZ(0)';
583
581
 
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
592
- videoElement.addEventListener('loadeddata', () => {
593
- videoElement.pause();
594
- });
582
+ const hoverIN = () => video.play();
583
+ const hoverOUT = () => { video.pause(); /* video.currentTime = 0; */ };
595
584
 
585
+ video.addEventListener('loadeddata', () => { video.pause(); });
596
586
  e.addEventListener('mouseenter', hoverIN, false);
597
587
  e.addEventListener('mouseleave', hoverOUT, false);
598
588
 
599
589
  } else {
600
- // Mobile
590
+ // Mobile: poster-only, but keep sources on Android; replace with <img> on iOS
601
591
  Array.from(videoElements).forEach((v) => {
602
592
  const poster = v.getAttribute('poster');
603
593
 
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
- }
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);
619
601
  } else {
620
- // Android: keep poster only, but DON'T remove sources (some browsers need metadata for sizing)
602
+ // Android: keep sources to preserve intrinsic size and poster rendering
621
603
  v.removeAttribute('autoplay');
622
604
  v.pause();
623
605
  v.preload = 'none';
624
- // Keeping sources avoids 0-height issues in some engines
625
- // v.load(); // optional: only if you observe stale frames
606
+ // v.load(); // enable only if you notice stale frames
626
607
  }
627
608
  });
628
609
  }
629
610
 
630
- // After images/posters load, refresh ScrollTrigger (prevents late layout causing missed triggers)
631
- // Safe to call; GSAP debounces
632
- setTimeout(() => ScrollTrigger.refresh(), 100);
611
+ // After layout settles (posters/images), refresh triggers
612
+ setTimeout(() => ScrollTrigger.refresh(), 120);
633
613
  };
634
614
 
635
615
 
636
616
 
637
-
638
617
  // Reviews slider
639
618
  window.reviewsSlider = function(e) {
640
619
  // Put content of static slide into list