← GSAP crash course for absolute beginners

Bonus: build the image loader

Chapter 14 of 15 · 57:19

Live demoFull screen ↗
gsap.registerPlugin(SplitText);

window.addEventListener("load", () => {
  document.fonts.ready.then(() => {
    const split = SplitText.create(".headline", {
      type: "lines",
      mask: "lines"
    });

    gsap.set(".headline", { visibility: "visible" });

    const tl = gsap.timeline();

    tl.to(".photo", {
        clipPath: "inset(0% 0% 0% 0%)",
        duration: 0.8,
        ease: "power4.out",
        stagger: { amount: 2, ease: "power1.out" }
      })
      .from(".photo img", {
        scale: 1.3,
        duration: 1,
        ease: "power3.out",
        stagger: { amount: 2, ease: "power1.out" }
      }, "<")
      .to(".ones span", { yPercent: -2000, duration: 3, ease: "power1.in" }, "<")
      .to(".tens span", { yPercent: -1000, duration: 3, ease: "power1.in" }, "<")
      .to(".hundreds span", { yPercent: -100, duration: 0.4, ease: "power2.in" }, "-=0.4")
      .to(".hero", { clipPath: "inset(0% 0% 0% 0%)", duration: 0.8, ease: "power4.out" }, "-=0.8")
      .addLabel("reveal")
      .to(".hero", { width: "100vw", height: "100vh", duration: 1.2, ease: "power2.inOut" }, "reveal")
      .from(".hero img", { scale: 1.25, duration: 1.6, ease: "power2.out" }, "reveal")
      .to(".digit", { yPercent: -100, duration: 0.5, ease: "power2.in", stagger: 0.04 }, "reveal+=0.2")
      .to(".label", { opacity: 0, duration: 0.4 }, "<")
      .to(".shade", { opacity: 1, duration: 1 }, "reveal+=0.4")
      .from(split.lines, { yPercent: 100, duration: 1, ease: "power4.out", stagger: 0.12 }, "reveal+=0.8");
  });
});

Loads GSAP 3.13.0, SplitText from a CDN