Instuctions

Lenis Scroll

<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.15/dist/lenis.css" />
<script src="https://unpkg.com/lenis@1.3.15/dist/lenis.min.js"></script>

<script>
  const lenis = new Lenis({ duration: 1.4 });

  lenis.on('scroll', ScrollTrigger.update);
  gsap.ticker.add((time) => {
    lenis.raf(time * 1000);
  });
  gsap.ticker.lagSmoothing(0);
</script>

Image Parallax

<script>
  document.addEventListener('DOMContentLoaded', () => {
    gsap.registerPlugin(ScrollTrigger);

    const mm = gsap.matchMedia();

    mm.add(
      {
        mobile: '(max-width: 479px)',
        mobileLandscape: '(min-width: 480px) and (max-width: 767px)',
        tablet: '(min-width: 768px) and (max-width: 991px)',
        desktop: '(min-width: 992px)',
      },
      (context) => {
        const { mobile, mobileLandscape, tablet, desktop } = context.conditions;

        document.querySelectorAll('[data-parallax="trigger"]').forEach((trigger) => {
          // Disable parallax on selected breakpoints
          const disable = trigger.getAttribute('data-parallax-disable');

          if ((disable === 'mobile' && mobile) || (disable === 'mobileLandscape' && mobileLandscape) || (disable === 'tablet' && tablet) || (disable === 'desktop' && desktop)) {
            return;
          }

          // Target element
          const target = trigger.querySelector('[data-parallax="target"]') || trigger;

          // Direction
          const direction = trigger.getAttribute('data-parallax-direction') || 'vertical';

          const property = direction === 'horizontal' ? 'xPercent' : 'yPercent';

          // Parallax values
          const startValue = parseFloat(trigger.getAttribute('data-parallax-start')) || 0;

          const endValue = parseFloat(trigger.getAttribute('data-parallax-end')) || -15;

          // Scrub
          const scrubAttribute = trigger.getAttribute('data-parallax-scrub');

          const scrub = scrubAttribute !== null ? parseFloat(scrubAttribute) : true;

          // ScrollTrigger positions
          const scrollStart = trigger.getAttribute('data-parallax-scroll-start') || 'top bottom';

          const scrollEnd = trigger.getAttribute('data-parallax-scroll-end') || 'bottom top';

          gsap.fromTo(
            target,
            {
              [property]: startValue,
            },
            {
              [property]: endValue,
              ease: 'none',
              scrollTrigger: {
                trigger: trigger,
                start: `clamp(${scrollStart})`,
                end: `clamp(${scrollEnd})`,
                scrub: scrub,
                invalidateOnRefresh: true,
              },
            },
          );
        });

        ScrollTrigger.refresh();
      },
    );
  });
</script>