100

%

Instructions

Instructions

<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.20/dist/lenis.css" />
<script src="https://unpkg.com/lenis@1.3.20/dist/lenis.min.js"></script>
<script>
  gsap.registerPlugin(ScrollTrigger);

  const lenis = new Lenis({
    duration: 1.5,
    easing: (t) => 1 - Math.pow(1 - t, 3),
    smooth: true,
    smoothTouch: false, // βœ… important
  });

  // sync
  lenis.on('scroll', ScrollTrigger.update);

  // raf loop
  function raf(time) {
    lenis.raf(time);
    requestAnimationFrame(raf);
  }
  requestAnimationFrame(raf);

  // βœ… correct scroller
  ScrollTrigger.scrollerProxy(document.documentElement, {
    scrollTop(value) {
      return arguments.length ? lenis.scrollTo(value, { immediate: true }) : lenis.scroll;
    },
    getBoundingClientRect() {
      return {
        top: 0,
        left: 0,
        width: window.innerWidth,
        height: window.innerHeight,
      };
    },
  });

  // βœ… important
  ScrollTrigger.defaults({
    scroller: document.documentElement,
  });

  // refresh fix
  ScrollTrigger.addEventListener('refresh', () => lenis.resize());

  ScrollTrigger.refresh();
</script>

<script>
gsap.registerPlugin(ScrollTrigger);

gsap.utils.toArray(".counter").forEach(counter => {

  const finalText = counter.textContent.trim();

  const endValue = parseFloat(finalText.replace(/[^0-9.-]/g, ""));
  const suffix = finalText.replace(/[0-9.-]/g, "");
  const decimals = finalText.includes(".") ? 1 : 0;

  // Reset to zero
  counter.textContent = "0" + suffix;

  const obj = { value: 0 };

  gsap.to(obj, {
    value: endValue,
    duration: 2,
    ease: "power2.out",
    scrollTrigger: {
      trigger: counter,
      start: "top 80%",
      once: true
    },
    onUpdate() {
      counter.textContent =
        obj.value.toFixed(decimals) + suffix;
    }
  });

});
</script>
<script>
document.addEventListener("DOMContentLoaded", () => {

    // ====================================
    // ELEMENTS
    // ====================================
    const cards = document.querySelectorAll(
        ".single-testimonials-clint-image-wrap"
    );

    const contents = document.querySelectorAll(
        ".single-testimonials-wrap"
    );

    const borders = document.querySelectorAll(
        ".single-testimonials-clint-image-border"
    );

    const DURATION = 5;

    let current = 0;
    let masterTL;

    // ====================================
    // INITIAL STATE
    // ====================================
    gsap.set(cards, {
        opacity: 0.35,
        scale: 0.75
    });

    gsap.set(cards[0], {
        opacity: 1,
        scale: 1
    });

    gsap.set(contents, {
        opacity: 0,
        y: 40,
        position: "absolute",
        top: 0,
        left: 0,
        width: "100%",
        display: "none"
    });

    gsap.set(contents[0], {
        opacity: 1,
        y: 0,
        display: "block"
    });

    gsap.set(borders, {
        opacity: 0,
        clipPath: "inset(0 100% 0 0)"
    });

    // ====================================
    // WRAPPER HEIGHT FIX
    // ====================================
    const wrapper = document.querySelector(
        ".testimonials-contant-wrapper"
    );

    wrapper.style.position = "relative";
    wrapper.style.minHeight =
        contents[0].offsetHeight + "px";

    // ====================================
    // GO TO TAB
    // ====================================
    function goTo(index) {

        if (index === current && masterTL) return;

        if (masterTL) {
            masterTL.kill();
        }

        const previous = current;

        // ====================================
        // TIMELINE
        // ====================================
        masterTL = gsap.timeline({
            onComplete: () => {
                const next =
                    (index + 1) % cards.length;

                goTo(next);
            }
        });

        // ====================================
        // IMAGE RESET
        // ====================================
        masterTL.to(
            cards,
            {
                opacity: 0.35,
                scale: 0.75,
                duration: 0.5,
                ease: "power2.out"
            },
            0
        );

        // Active image
        masterTL.to(
            cards[index],
            {
                opacity: 1,
                scale: 1,
                duration: 0.6,
                ease: "back.out(1.7)"
            },
            0
        );

        // ====================================
        // BORDER RESET
        // ====================================
        gsap.set(borders, {
            opacity: 0,
            clipPath: "inset(0 100% 0 0)"
        });

        gsap.set(borders[index], {
            opacity: 1
        });

        // Progress animation
        masterTL.to(
            borders[index],
            {
                clipPath: "inset(0 0% 0 0)",
                duration: DURATION,
                ease: "linear"
            },
            0
        );

        // ====================================
        // OLD CONTENT OUT
        // ====================================
        if (contents[previous]) {

            masterTL.to(
                contents[previous],
                {
                    opacity: 0,
                    y: -40,
                    duration: 0.4,
                    ease: "power2.out",

                    onComplete: () => {
                        gsap.set(
                            contents[previous],
                            {
                                display: "none"
                            }
                        );
                    }
                },
                0
            );
        }

        // ====================================
        // NEW CONTENT IN
        // ====================================
        gsap.set(contents[index], {
            display: "block"
        });

        wrapper.style.minHeight =
            contents[index].offsetHeight + "px";

        masterTL.fromTo(
            contents[index],
            {
                opacity: 0,
                y: 40
            },
            {
                opacity: 1,
                y: 0,
                duration: 0.8,
                ease: "power3.out"
            },
            0.2
        );

        // ====================================
        // TEXT STAGGER
        // ====================================
        masterTL.fromTo(
            contents[index].children,
            {
                opacity: 0,
                y: 20
            },
            {
                opacity: 1,
                y: 0,
                stagger: 0.08,
                duration: 0.5,
                ease: "power2.out"
            },
            0.3
        );

        current = index;
    }

    // ====================================
    // CLICK EVENT
    // ====================================
    cards.forEach((card, index) => {

        card.addEventListener("click", () => {
            goTo(index);
        });

    });

    // ====================================
    // START
    // ====================================
    current = -1;
    goTo(0);

});
</script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>

<script>
window.addEventListener("load", () => {

  const preloader = document.querySelector(".prelader-wrapper");
  const counter = document.querySelector(".prelader-counter-number");
  const progressBar = document.querySelector(".preloader-progress-bar");
  const preloaderBg = document.querySelector(".prelader-background");

  preloader.style.display = "flex";

  let count = { value: 0 };

  // πŸ”₯ Start state fix (important)
  counter.innerText = "0";
  progressBar.style.width = "0%";

  const tl = gsap.timeline();

  tl.to(preloaderBg, {
    clipPath: "circle(150% at 100% 0%)",
    duration: 1.4,
    ease: "power4.inOut"
  })

  .from(".prelader-wrap", {
    opacity: 0,
    y: 40,
    duration: 0.8,
    ease: "power3.out"
  })

  .to(count, {
    value: 100,
    duration: 3,
    ease: "power2.out",

    onUpdate: () => {
      let val = Math.floor(count.value);

      counter.innerText = val;        // βœ” starts from 0
      progressBar.style.width = val + "%";
    }
  })

  .to(".prelader-wrap", {
    opacity: 0,
    y: -40,
    duration: 0.6
  })

  .to(preloaderBg, {
    clipPath: "circle(0% at 100% 0%)",
    duration: 1.2
  })

  .to(preloader, {
    opacity: 0,
    duration: 0.4,
    onComplete: () => {
      preloader.style.display = "none";
    }
  });

});
</script>
Call To action

Let’s Build Something Amazing Together