@widelab-nc/widehue 1.0.47 → 1.0.49

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/widehue",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
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
@@ -469,9 +469,6 @@ window.customCursorText = function(e) {
469
469
  customCursorTextElement.innerHTML = wideCursorTextValue;
470
470
  });
471
471
  }
472
-
473
-
474
-
475
472
  window.heroAnimationOnLoad = function(e) {
476
473
  const video = document.getElementById('herovideo');
477
474
 
@@ -482,39 +479,47 @@ window.heroAnimationOnLoad = function(e) {
482
479
  }
483
480
 
484
481
  let videoPlayListener = null;
482
+
485
483
  if (video) {
484
+ const isMobile = window.innerWidth < 992;
485
+
486
+ const mp4Source = video.querySelector('source[type="video/mp4"]');
487
+ const webmSource = video.querySelector('source[type="video/webm"]');
488
+
489
+ const desktopMp4 = mp4Source?.getAttribute('data-desktop') || mp4Source?.getAttribute('src');
490
+ const desktopWebm = webmSource?.getAttribute('data-desktop') || webmSource?.getAttribute('src');
491
+
492
+ const mobileMp4 = video.getAttribute('mobile-mp4');
493
+ const mobileWebm = video.getAttribute('mobile-webm');
494
+
495
+ if (isMobile && mobileMp4 && mobileWebm) {
496
+ if (mp4Source) mp4Source.src = mobileMp4;
497
+ if (webmSource) webmSource.src = mobileWebm;
498
+ } else {
499
+ if (mp4Source && desktopMp4) mp4Source.src = desktopMp4;
500
+ if (webmSource && desktopWebm) webmSource.src = desktopWebm;
501
+ }
502
+
503
+ video.load();
486
504
  video.pause();
487
505
  video.currentTime = 0;
488
- function updateVideoSources() {
489
- const isMobile = window.innerWidth < 992;
490
- const mobileMp4 = video.getAttribute('mobile-mp4');
491
- const mobileWebm = video.getAttribute('mobile-webm');
492
- const mp4Source = video.querySelector('source[type="video/mp4"]');
493
- const webmSource = video.querySelector('source[type="video/webm"]');
494
- if (mobileMp4 && mp4Source) mp4Source.setAttribute('data-src', mobileMp4);
495
- if (mobileWebm && webmSource) webmSource.setAttribute('data-src', mobileWebm);
496
- if (!isMobile && mp4Source) mp4Source.setAttribute('data-src', mp4Source.getAttribute('data-src')?.replace('mobile-', '') || '');
497
- if (!isMobile && webmSource) webmSource.setAttribute('data-src', webmSource.getAttribute('data-src')?.replace('mobile-', '') || '');
498
- }
499
- updateVideoSources();
500
- window.addEventListener('resize', updateVideoSources);
501
506
  video.setAttribute('data-no-autoplay', 'true');
507
+
502
508
  video.addEventListener('loadeddata', () => {
503
509
  if (video.hasAttribute('data-no-autoplay')) {
504
510
  video.pause();
505
511
  video.currentTime = 0;
506
512
  }
507
513
  });
514
+
508
515
  videoPlayListener = () => {
509
- console.log('[heroAnimationOnLoad] Video play event! currentTime:', video.currentTime);
510
516
  if (video.hasAttribute('data-no-autoplay')) {
511
- console.log('[heroAnimationOnLoad] Video has data-no-autoplay, forcing pause');
512
517
  video.pause();
513
518
  video.currentTime = 0;
514
519
  }
515
520
  };
521
+
516
522
  video.addEventListener('play', videoPlayListener);
517
- } else {
518
523
  }
519
524
 
520
525
  function run() {
@@ -542,9 +547,6 @@ window.heroAnimationOnLoad = function(e) {
542
547
  document.addEventListener('touchmove', preventScroll, { passive: false });
543
548
  }
544
549
 
545
- // Lock scroll IMMEDIATELY at the start - before any animation
546
- lockScroll();
547
-
548
550
  function unlockScroll() {
549
551
  document.body.style.position = '';
550
552
  document.body.style.top = '';
@@ -559,6 +561,8 @@ window.heroAnimationOnLoad = function(e) {
559
561
  if (typeof lenis !== 'undefined' && lenis.start) lenis.start();
560
562
  }
561
563
 
564
+ lockScroll();
565
+
562
566
  const heroAnimation = gsap.timeline({});
563
567
 
564
568
  if (preloader && introBrand && introSubtitle && widelettersIntro.length > 0) {
@@ -572,9 +576,7 @@ window.heroAnimationOnLoad = function(e) {
572
576
  heroAnimation.to(widelettersIntro, { y: '-150%', opacity: 0, duration: 0.6, ease: 'power2.in', stagger: 0.03 });
573
577
  heroAnimation.fromTo(introSubtitle, { y: '50%', opacity: 0 }, { y: '0%', opacity: 1, duration: 0.6, ease: 'power2.out' });
574
578
  heroAnimation.to({}, { duration: 2 });
575
- // 5. .wrap-it: height 100vh → 0vh (2s). Video play na START animacji height.
576
- // Set position: absolute before height animation to prevent body height change
577
- // NOTE: lockScroll() is already called at the start of run() function
579
+
578
580
  heroAnimation.set(wrapIt, { position: 'absolute', top: 0, left: 0, width: '100%', height: '100vh', overflow: 'hidden', zIndex: 9999 });
579
581
  heroAnimation.to(wrapIt, {
580
582
  height: '0vh',
@@ -583,25 +585,13 @@ window.heroAnimationOnLoad = function(e) {
583
585
  onStart: () => {
584
586
  if (video) {
585
587
  video.currentTime = 0;
586
- // Remove attribute and play listener before playing to prevent conflicts
587
588
  video.removeAttribute('data-no-autoplay');
588
- if (videoPlayListener) {
589
- video.removeEventListener('play', videoPlayListener);
590
- }
591
- video.play().then(() => {
592
- }).catch(err => {
593
- });
589
+ if (videoPlayListener) video.removeEventListener('play', videoPlayListener);
590
+ video.play().catch(() => {});
594
591
  }
595
592
  },
596
- onComplete: () => {
597
- unlockScroll();
598
- gsap.set(wrapIt, { visibility: 'hidden', pointerEvents: 'none' });
599
- // Refresh ScrollTrigger after height animation completes to recalculate positions
600
- ScrollTrigger.refresh();
601
- },
602
593
  });
603
594
  } else if (wrapIt) {
604
- // NOTE: lockScroll() is already called at the start of run() function
605
595
  heroAnimation.set(wrapIt, { position: 'absolute', top: 0, left: 0, width: '100%', height: '100vh', overflow: 'hidden', zIndex: 9999 });
606
596
  heroAnimation.to(wrapIt, {
607
597
  height: '0vh',
@@ -611,31 +601,41 @@ window.heroAnimationOnLoad = function(e) {
611
601
  if (video) {
612
602
  video.currentTime = 0;
613
603
  video.removeAttribute('data-no-autoplay');
614
- if (videoPlayListener) {
615
- video.removeEventListener('play', videoPlayListener);
616
- }
604
+ if (videoPlayListener) video.removeEventListener('play', videoPlayListener);
617
605
  video.play();
618
606
  }
619
607
  },
620
- onComplete: () => {
621
- unlockScroll();
622
- gsap.set(wrapIt, { visibility: 'hidden', pointerEvents: 'none' });
623
- // Refresh ScrollTrigger after height animation completes to recalculate positions
624
- ScrollTrigger.refresh();
625
- },
626
608
  });
627
609
  }
628
610
 
629
611
  heroAnimation.from('#heroBottomWrapper', { opacity: 0, duration: 1, ease: 'loader2' }, '>-0.4');
630
612
 
631
- // Literki IN as last step in timeline (initial state)
632
613
  const wideletters = document.querySelectorAll('.wideletter');
633
614
  if (wideletters.length > 0) {
615
+ // teraz scroll odblokowuje się dopiero po animacji wszystkich liter
634
616
  heroAnimation.fromTo(wideletters,
635
617
  { y: '-150%', opacity: 0 },
636
- { y: '0%', opacity: 1, duration: 1, ease: 'loader2', stagger: 0.1 },
618
+ {
619
+ y: '0%',
620
+ opacity: 1,
621
+ duration: 1,
622
+ ease: 'loader2',
623
+ stagger: 0.1,
624
+ onComplete: () => {
625
+ unlockScroll();
626
+ gsap.set(wrapIt, { visibility: 'hidden', pointerEvents: 'none' });
627
+ ScrollTrigger.refresh();
628
+ }
629
+ },
637
630
  '>-0.6'
638
631
  );
632
+ } else {
633
+ // jeśli nie ma liter, odblokuj scroll po animacji wrapIt
634
+ heroAnimation.add(() => {
635
+ unlockScroll();
636
+ gsap.set(wrapIt, { visibility: 'hidden', pointerEvents: 'none' });
637
+ ScrollTrigger.refresh();
638
+ });
639
639
  }
640
640
  }
641
641
 
@@ -649,8 +649,6 @@ window.heroAnimationOnLoad = function(e) {
649
649
 
650
650
 
651
651
 
652
-
653
-
654
652
  window.briefContact = function(e) {
655
653
  const button = document.querySelector('.brief-button');
656
654
  const bottomOverlay = document.querySelector('#bottomWrapperOverlay');
@@ -2667,11 +2665,24 @@ window.heroAnimation2026 = function(e) {
2667
2665
 
2668
2666
  // Case Hero Animation - navbar letters on scroll
2669
2667
  window.CaseHeroAnimation = function(e) {
2668
+ // Scroll animation for wideletters
2669
+ const wideletters = document.querySelectorAll('.wideletter');
2670
+ const isCshero = document.querySelector('.is-cshero');
2671
+
2670
2672
  // Page load animation timeline (starts after 200ms)
2671
2673
  const pageLoadTimeline = gsap.timeline({ delay: 0.2 });
2672
2674
 
2673
- // All animations start at the same time with 0.2s stagger between them
2674
- // 1. .navbar and .button_small.is-navbar - slide down from top with stagger
2675
+ // 1. .wideletter IN as FIRST animation in page load timeline
2676
+ if (wideletters.length > 0) {
2677
+ pageLoadTimeline.fromTo(wideletters,
2678
+ { y: '-150%', opacity: 0 },
2679
+ { y: '0%', opacity: 1, duration: 1.2, ease: 'loader', stagger: 0.1 },
2680
+ 0
2681
+ );
2682
+ }
2683
+
2684
+ // All other animations start at the same time with 0.2s stagger between them
2685
+ // 2. .navbar and .button_small.is-navbar - slide down from top with stagger
2675
2686
  const navbar = document.querySelector('.navbar');
2676
2687
  const navButtons = document.querySelectorAll('.button_small.is-navbar');
2677
2688
  if (navbar || navButtons.length > 0) {
@@ -2684,7 +2695,7 @@ window.CaseHeroAnimation = function(e) {
2684
2695
  }, 0);
2685
2696
  }
2686
2697
 
2687
- // 2. .hero_headline - SplitText animation (like gsapTitleAnimation but play in timeline)
2698
+ // 3. .hero_headline - SplitText animation (like gsapTitleAnimation but play in timeline)
2688
2699
  const heroHeadline = document.querySelector('.hero_headline');
2689
2700
  let heroLines = [];
2690
2701
  if (heroHeadline) {
@@ -2703,13 +2714,14 @@ window.CaseHeroAnimation = function(e) {
2703
2714
  pageLoadTimeline.to(heroLines, {
2704
2715
  y: '0%',
2705
2716
  duration: 1.2,
2717
+ opacity: 1,
2706
2718
  stagger: 0.2,
2707
2719
  ease: 'loader',
2708
2720
  }, 0.2);
2709
2721
  }
2710
2722
  }
2711
2723
 
2712
- // 3. Direct children of .cs-tags - slide down from bottom with opacity, stagger
2724
+ // 4. Direct children of .cs-tags - slide down from bottom with opacity, stagger
2713
2725
  const csTags = document.querySelector('.cs-tags');
2714
2726
  if (csTags && csTags.children.length > 0) {
2715
2727
  pageLoadTimeline.from(Array.from(csTags.children), {
@@ -2721,10 +2733,6 @@ window.CaseHeroAnimation = function(e) {
2721
2733
  }, 0.4);
2722
2734
  }
2723
2735
 
2724
- // Scroll animation for wideletters
2725
- const wideletters = document.querySelectorAll('.wideletter');
2726
- const isCshero = document.querySelector('.is-cshero');
2727
-
2728
2736
  if (wideletters.length === 0 && !isCshero) return;
2729
2737
 
2730
2738
  // Timeline for letters OUT
@@ -2742,7 +2750,22 @@ window.CaseHeroAnimation = function(e) {
2742
2750
  );
2743
2751
  }
2744
2752
 
2745
- // Fade out .is-cshero on same timeline
2753
+ // Timeline for letters IN (for scroll up)
2754
+ const lettersInTimeline = gsap.timeline({ paused: true });
2755
+ if (wideletters.length > 0) {
2756
+ lettersInTimeline.fromTo(wideletters,
2757
+ { y: '-150%', opacity: 0 },
2758
+ {
2759
+ y: '0%',
2760
+ opacity: 1,
2761
+ duration: 1,
2762
+ ease: 'loader2',
2763
+ stagger: 0.1,
2764
+ }
2765
+ );
2766
+ }
2767
+
2768
+ // Fade out .is-cshero on same timeline as letters OUT
2746
2769
  if (isCshero) {
2747
2770
  lettersOutTimeline.to(isCshero,
2748
2771
  {
@@ -2759,12 +2782,37 @@ window.CaseHeroAnimation = function(e) {
2759
2782
  const firstAfterSticky = document.querySelector('.first-after-sticky');
2760
2783
  if (!firstAfterSticky) return;
2761
2784
 
2785
+ let scrollTriggerActive = false;
2786
+
2762
2787
  ScrollTrigger.create({
2763
2788
  trigger: firstAfterSticky,
2764
2789
  start: 'top 80%',
2765
2790
  end: 'top top',
2766
- onEnter: () => lettersOutTimeline.play(),
2767
- onLeaveBack: () => lettersOutTimeline.reverse(),
2791
+ onEnter: () => {
2792
+ scrollTriggerActive = true;
2793
+ lettersOutTimeline.play();
2794
+ },
2795
+ onLeaveBack: () => {
2796
+ scrollTriggerActive = false;
2797
+ lettersOutTimeline.reverse();
2798
+ },
2799
+ });
2800
+
2801
+ // Listen for scroll up globally to show letters IN (but not during page load or when ScrollTrigger is handling it)
2802
+ let lastScrollY = window.scrollY;
2803
+ let pageLoadComplete = false;
2804
+ pageLoadTimeline.eventCallback('onComplete', () => {
2805
+ pageLoadComplete = true;
2806
+ });
2807
+
2808
+ window.addEventListener('scroll', () => {
2809
+ if (!pageLoadComplete || scrollTriggerActive) return;
2810
+ const currentScrollY = window.scrollY;
2811
+ if (currentScrollY < lastScrollY && wideletters.length > 0) {
2812
+ // Scroll up - show letters IN (only when ScrollTrigger is not active)
2813
+ lettersInTimeline.play();
2814
+ }
2815
+ lastScrollY = currentScrollY;
2768
2816
  });
2769
2817
  };
2770
2818
 
@@ -2897,15 +2945,16 @@ function gsapTitleAnimation() {
2897
2945
  if (lines.length === 0) return;
2898
2946
 
2899
2947
  // Set initial state - lines hidden below
2900
- gsap.set(lines, { y: '100%' });
2948
+ gsap.set(lines, { y: '1.25rem', opacity: 0 });
2901
2949
 
2902
2950
  // Create paused timeline
2903
2951
  const titleTimeline = gsap.timeline({ paused: true });
2904
2952
 
2905
2953
  titleTimeline.to(lines, {
2906
- y: '0%',
2954
+ y: '0rem',
2955
+ opacity: 1,
2907
2956
  duration: 0.75,
2908
- stagger: 0.2,
2957
+ stagger: 0.15,
2909
2958
  ease: 'loader2',
2910
2959
  });
2911
2960