GSAP & Elementor V4 POPPING IMAGES ON SCROLL

SHARE

SIGN UP FOR THE NEWSLETTER

Your subscription could not be saved. Please try again.
Your subscription has been successful.

Code:

JavaScript



// Mobile optimized code

<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>

<script>
  gsap.registerPlugin(ScrollTrigger);

  const isMobile = window.innerWidth < 768;

  // Desktop: all 14 positions
  const desktopPositions = [
    { top: "0%",  left: "0%"  },
    { top: "0%",  left: "10%" },
    { top: "0%",  left: "60%" },
    { top: "16%", left: "30%" },
    { top: "16%", left: "40%" },
    { top: "16%", left: "90%" },
    { top: "32%", left: "65%" },
    { top: "32%", left: "75%" },
    { top: "48%", left: "0%"  },
    { top: "64%", left: "30%" },
    { top: "64%", left: "50%" },
    { top: "64%", left: "90%" },
    { top: "80%", left: "20%" },
    { top: "80%", left: "70%" },
  ];

  // Mobile: fewer, tighter positions (adjust to taste)
  const mobilePositions = [
    { top: "5%",  left: "10%" },
    { top: "5%",  left: "55%" },
    { top: "30%", left: "30%" },
    { top: "55%", left: "5%"  },
    { top: "55%", left: "60%" },
    { top: "80%", left: "35%" },
  ];

  const positions = isMobile ? mobilePositions : desktopPositions;
  const maxImages = positions.length;

  // Hide images beyond the mobile limit
  const allImages = document.querySelectorAll(".image-pop");
  if (isMobile) {
    allImages.forEach((img, i) => {
      if (i >= maxImages) img.style.display = "none";
    });
  }

  // Sizing values per breakpoint
  const stackedSize = isMobile
    ? { scale: 1.2, width: "200px", height: "260px" }
    : { scale: 1.5, width: "300px", height: "400px" };

  const scatteredSize = isMobile
    ? { width: "100px", height: "120px" }
    : { width: "175px", height: "200px" };

  gsap.set(".image-pop", {
    top: "50%",
    left: "50%",
    transform: "translate(-50%, -50%) scale(0)",
  });

  ScrollTrigger.create({
    trigger: ".image-pop",
    start: "top 100%",
    onEnter: () => animateImages(),
    once: true,
  });

  function animateImages() {
    gsap.to(".image-pop", {
      ...stackedSize,
      stagger: 0.15,
      duration: 0.75,
      ease: "power2.out",
      delay: 0.5,
      onComplete: scatterAndShrink,
    });
  }

  function scatterAndShrink() {
    gsap.to(".image-pop:not([style*='display: none'])", {
      top: (i) => positions[i].top,
      left: (i) => positions[i].left,
      transform: "none",
      ...scatteredSize,
      stagger: 0.075,
      duration: 0.75,
      ease: "power2.out",
    });
  }
</script>


// NOT Mobile optimized code
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<script>
  gsap.registerPlugin(ScrollTrigger);

  const positions = [
    { top: "0%",  left: "0%"  },
    { top: "0%",  left: "10%" },
    { top: "0%",  left: "60%" },
    { top: "16%", left: "30%" },
    { top: "16%", left: "40%" },
    { top: "16%", left: "90%" },
    { top: "32%", left: "65%" },
    { top: "32%", left: "75%" },
    { top: "48%", left: "0%"  },
    { top: "64%", left: "30%" },
    { top: "64%", left: "50%" },
    { top: "64%", left: "90%" },
    { top: "80%", left: "20%" },
    { top: "80%", left: "70%" },
  ];

  gsap.set(".image-pop", {
    top: "50%",
    left: "50%",
    transform: "translate(-50%, -50%) scale(0)",
  });

  ScrollTrigger.create({
    trigger: ".image-pop",
    start: "top 90%",
    onEnter: () => animateImages(),
    once: true,
  });

  function animateImages() {
    gsap.to(".image-pop", {
      scale: 1.5,
      width: "300px",
      height: "400px",
      stagger: 0.15,
      duration: 0.75,
      ease: "power2.out",
      delay: 0.5,
      onComplete: scatterAndShrink,
    });
  }

  function scatterAndShrink() {
    gsap.to(".image-pop", {
      top: (i) => positions[i].top,
      left: (i) => positions[i].left,
      transform: "none",
      width: "175px",
      height: "200px",
      stagger: 0.075,
      duration: 0.75,
      ease: "power2.out",
    });
  }
</script>

CSS

.gallery {
  height: calc(100vh - 60px);
  }



Watch on YouTube:


SIGN UP FOR THE NEWSLETTER

Your subscription could not be saved. Please try again.
Your subscription has been successful.