Jump to content
Search Community

How to combine stagger / batch and forEach? Or do it differently?

Snail test
Moderator Tag

Go to solution Solved by Cassie,

Recommended Posts

Dear supporters,

 

I've done a lot of one-time-solutions with GSAP the last months, and they worked fine.

Currently I'm doing some cleanup, abstraction and more module like things to create a small toolkit for my own purposes to add in into various projects.

 

One thing i'd like to get really clean and modular is this simple thing:

  • I have a DIV as a row, with 3 DIVs as 33% columns side by side inside of it.  (100% stacked in small viewports)
  • They all have an animation-class to trigger their animation.
  • They shall appear with an animation when entering the viewport, and go away again when leaving the viewport, both directions.

Honestly, i've been copy-pasting and adjusting things mostly, not always completely understanding how things work together.

So for the moment, i have this code, that creates one timeline and scrolltrigger configuration for each instance found in the HTML code:

      gsap.utils.toArray('.ani-flash-in').forEach(item => {
        var tl_item = gsap.timeline({
          scrollTrigger: {
              trigger: item,
              start: "top 80%",
              end:   "bottom 20%",
              toggleActions: 'play reverse play reverse',
              markers:false
          }
        });
        tl_item.from(item, { scale: 1.1, opacity:0, filter: "blur(10px)", ease:"power2.inOut", duration: 0.666 }, "0");
      });
  <div class="row">
    <a href="#" class="col col-33 ani-flash-in">content</a>
    <a href="#" class="col col-33 ani-flash-in">content</a>
    <a href="#" class="col col-33 ani-flash-in">content</a>
  </div>

This if fine for me (except if you have significant improvements).

But obviously, they all trigger at the same time and play their animation(s) independently as they come into viewport at the same moment.

 

Now i'd like to stagger the animation of the boxes a bit, and that's where i'm struggling now.

As i understand, currently the forEach creates 3 completely independent objects with independent timelines and timings, despite they are all the same – they can't interact by design.

 

Competing thoughts that might solve this, but i'm not able to implement it:

  • Just adding a stagger:0.5 to my "from" definition does nothing (as expected)
  • I've found that there is the scrollTrigger.batch() function, that would create a batch of triggers that are grouped logically, and therefore then use the stagger-attribute in a timeline?  But this does not seem to do it, when leaving the "forEach" intact.
  • Is the "forEach" actually the right approach or is that some copy-paste-relic i should get rid of? I've found code like this, that puts scrollTrigger.batch() on top in the logic, and this works, but I don't know how to create a timeline then (as scrollTrigger is part OF a timeline usually?). Do I need to specify animations for all directions instead instead of play / reverse which feels like a lot of overlap in definitions?
    gsap.set(".ani-flash-in", {scale: 1.1, opacity:0, filter: "blur(10px)" });
    
    ScrollTrigger.batch(".ani-flash-in", {
            onEnter:     batch => gsap.to(batch, { scale: 1.0, opacity:1, filter: "blur(0px)",  ease:"power2.inOut", duration: 0.666, stagger:0.1666, overwrite: true } ),
            onLeaveBack: batch => gsap.to(batch, { scale: 1.1, opacity:0, filter: "blur(10px)", ease:"power2.inOut", duration: 0.666, stagger:0.1666, overwrite: true } ),
            markers:false,
            start: "top 70%,
          });

     

  • Or do i need to set a trigger for the .row element instead of the columns, so it then staggers the animation for their children .ani-flash-in?

 

As said, i'd like to have this rather universal from the HTML perspective – so just adding some "ani-flash-in" to columns, not adding anything to the row, would be best.

If the columns wrap to 100% widths in a responsive layout, not being side-by-side anymore, i'd like to trigger the animations immediately with no stagger delay for the second and third item (but as i understand, this is built in into the trigger / stagger function anyway).

 

Thanks for any directions ...

See the Pen abrEQPd by Kapitel4 (@Kapitel4) on CodePen

Link to comment
Share on other sites

  • Solution

Heya!

 

Quote

Just adding a stagger:0.5 to my "from" definition does nothing (as expected)

Right, as each forEach loop just contains one of your elements, so there's nothing to stagger

 

Quote

Is the "forEach" actually the right approach or is that some copy-paste-relic i should get rid of? I've found code like this, that puts scrollTrigger.batch() on top in the logic, and this works, but I don't know how to create a timeline then (as scrollTrigger is part OF a timeline usually?). Do I need to specify animations for all directions instead instead of play / reverse which feels like a lot of overlap in definitions?

A ScrollTrigger can be added to a tween, or a timeline or it can just be standalone. (or in this case, with the batch method, it's an entirely different pattern, where ScrollTrigger is internally creating a batched group of ScrollTriggers for you)

 

Quote

Or do i need to set a trigger for the .row element instead of the columns, so it then staggers the animation for their children .ani-flash-in?

If you set a trigger on the row you'll be able to stagger the elements inside as all three will enter the viewport at once (when they're in a row) BUT you won't be able to detect when each one individually enters the viewport when they're in a column, (as the trigger logic is on the container and not on the individual children) This is largely the problem that batch is solves

 

Quote

I've found that there is the scrollTrigger.batch() function, that would create a batch of triggers that are grouped logically, and therefore then use the stagger-attribute in a timeline?  But this does not seem to do it, when leaving the "forEach" intact.

You don't need a timeline or a loop if you're going with the batch route. I think it's the best option here. You just tell ScrollTrigger which elements you want to 'batch' and then when they pass the trigger points they get passed through to the callback functions.

If multiple enter at the same time (like a row), and you've defined a stagger in the animation in the callback, they'll be staggered. However, if you've only got one element entering the viewport at a time, (like a column) there's nothing to stagger, so they'll be animated one by one.

You can define different animations in the callbacks

See the Pen ExzoGNN?editors=1010 by GreenSock (@GreenSock) on CodePen



Definitely give the docs a read too, they're very detailed.
https://gsap.com/docs/v3/Plugins/ScrollTrigger/static.batch()/

 

Hope this helps.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Also - small bit of feedback.

You don't need a timeline here. A timeline is for sequencing multiple tween.

// this works but there's no benefit to a timeline
gsap.utils.toArray('.ani-flash-in').forEach(item => {
  var tl_item = gsap.timeline({
    scrollTrigger: {
      ...
    }
  });
  tl_item.from(item, { 
    scale: 1.1, 
    opacity:0, 
    filter: "blur(10px)", 
    ease:"power2.inOut", 
    duration: 0.666 
  },"0"); // <--- this 0 is redundant, it's a position parameter that's placing the tween at the beginning (0 seconds in to the timeline
});

// better - just use a tween
gsap.utils.toArray('.ani-flash-in').forEach(item => {
  gsap.from(item, { 
    scale: 1.1, 
    opacity:0, 
    filter: "blur(10px)", 
    ease:"power2.inOut", 
    duration: 0.666,
    scrollTrigger: {
    ...
   }
  });
});

 

🔥 Also filter is VERY hard on browsers to animate. (Bad for performance) I would advise to use an opacity fade or something instead of blur.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Thanks Cassie for the very detailed feedback!

 

I'll have to chew through all options and the examples now and implement it.

But understanding what batch() + scrolltrigger internally does, helps me a lot for understanding what's actually going on.

 

Thanks too for cleaning up my code in the second answer.

Just one question there: in case i'd wanted a more complex animation than a simple tween, i still *can* use a timeline instead of a tween while using batch,  correct?  (Sorry if this is a stupid question, still wrapping my head around the basics obviously ...)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...