javascriptcssanimationgsapscrolltrigger

GSAP ScrollTrigger runs on all animations at the same time, even for elements not in the viewport


I have several content blocks stacked on a page that will all share the same animation on scroll.

Currently, the animation runs on all the blocks at the same time, when the first item with the relevant class/trigger enters the viewport.

How do I make sure the animation only runs on the relevant element that is entering/leaving the viewport?

As an aside I'd like the animation to run the length of the viewport, so the animation starts when the top edge of the image enters the viewport and ends as the bottom edge leaves ...not sure if it's doing that at the moment.

I tried putting the script into an array based on this article but it doesn't seem to work with my timeline.

const stories = gsap.utils.toArray('.story__media');
stories.forEach(story => {
    var tl =  gsap.timeline(story, {
        scrollTrigger: {
            trigger: ".story__media",
            scrub: true,
            start: "top top",
            end: "bottom bottom"
        }
    });
    tl.from(".story__media img", {scale: 2, ease: "power2"})
});

gsap.registerPlugin(ScrollTrigger);

var tl = gsap.timeline({
  scrollTrigger: {
    trigger: ".story__media",
    scrub: true,
    //pin: true,
    start: "-100%",
    end: "+=200%"
  }
});

tl.from(".story__media img", {
  scale: 1.4,
  ease: "power2"
})
img {
  display: block;
  width: 100%;
  max-width: 100%;
}

.story {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 4px;
  padding: 64px 0;
}

.story__media {
  grid-column: 2 / span 5;
  overflow: hidden;
}

.story__caption-wrap {
  grid-column: 8 / span 4;
}
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/gsap-latest-beta.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/ScrollTrigger.min.js?v=3.3.0-3"></script>


<!-- Start Story -->
<div class="story">
  <div class="story__media">
    <img src="https://images.pexels.com/photos/16763202/pexels-photo-16763202.jpeg" alt="ALT TEXT">
  </div>
  <div class="story__caption-wrap">
    <div class="story__caption">
      <h2>Title of Block 1</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
    </div>
  </div>
</div>
<!-- End Story -->

<!-- Start Story -->
<div class="story">
  <div class="story__media">
    <img src="https://images.pexels.com/photos/16763202/pexels-photo-16763202.jpeg" alt="ALT TEXT">
  </div>
  <div class="story__caption-wrap">
    <div class="story__caption">
      <h2>Title of Block 2</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
    </div>
  </div>
</div>
<!-- End Story -->

<!-- Start Story -->
<div class="story">
  <div class="story__media">
    <img src="https://images.pexels.com/photos/16763202/pexels-photo-16763202.jpeg" alt="ALT TEXT">
  </div>
  <div class="story__caption-wrap">
    <div class="story__caption">
      <h2>Title of Block 3</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
    </div>
  </div>
</div>
<!-- End Story -->


Solution

  • You need to grab all the elements that you wanna animate, loop over them and set the trigger to be equal to the current one, not to the class that target all of them at once (the reason all of them are being animated at the same time). Something like so:

    gsap.registerPlugin(ScrollTrigger);
    const stories = document.querySelectorAll(".story");
    
    stories.forEach((s) => {
      var tl = gsap.timeline({
        scrollTrigger: {
          trigger: s,
          scrub: true,
          end: "bottom top"
        }
      });
    
      tl.from(s.querySelector("img"), {
        scale: 2,
        ease: "power2"
      });
    });
    .intro {
      height: 80vh;
      display: grid;
      place-items: center;
      text-align: center;
      border: 1px solid lightgray;
      border-radius: 1rem;
      margin-bottom: 10px;
    }
    img {
      display: block;
      height: 100%;
      width: 100%;
      object-fit: cover;
    }
    
    .story {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 2rem;
      padding: 1rem;
      border: 1px solid lightgray;
      border-radius: 1rem;
      margin-bottom: 10px;
    }
    
    .story__media {
      overflow: hidden;
      border-radius: 1rem;
    }
    <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/gsap-latest-beta.min.js"></script>
    <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/ScrollTrigger.min.js?v=3.3.0-3"></script>
    <div class="intro">
      <h2>Scroll to see magic happen</h2>
    </div>
    <!-- Start Story -->
    <div class="story">
      <div class="story__media">
        <img src="https://images.pexels.com/photos/16763202/pexels-photo-16763202.jpeg" alt="ALT TEXT">
      </div>
      <div class="story__caption-wrap">
        <div class="story__caption">
          <h2>Title of Block 1</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
      </div>
    </div>
    <!-- End Story -->
    
    <!-- Start Story -->
    <div class="story">
      <div class="story__media">
        <img src="https://images.pexels.com/photos/16763202/pexels-photo-16763202.jpeg" alt="ALT TEXT">
      </div>
      <div class="story__caption-wrap">
        <div class="story__caption">
          <h2>Title of Block 2</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
      </div>
    </div>
    <!-- End Story -->
    
    <!-- Start Story -->
    <div class="story">
      <div class="story__media">
        <img src="https://images.pexels.com/photos/16763202/pexels-photo-16763202.jpeg" alt="ALT TEXT">
      </div>
      <div class="story__caption-wrap">
        <div class="story__caption">
          <h2>Title of Block 3</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
      </div>
    </div>
    <!-- End Story -->