Jump to content
Search Community

Nested Tweens/Timeline inside master timeline - weird animations

OxyNinja test
Moderator Tag

Recommended Posts

Hello everybody,

I came across an issue, where inserting tweens or timeline in the master timeline results in totally wrong animation on some elements.
As you can see in the video attachments, buttons are animated properly, but the text just changes state from -300 to 0 without any animation.
I've tried multiple variation of the code below, and it only works when I assign animation to gsap, not to timeline.

Is this some sort of bug, or I've just overlooked something?

Thanks a lot!

P.S. The code below is simplified as there should be different values for each  selectorsArray, so that's why I am wrapping it up with first for..of instead of using gsap.utils.toArray(selectorsArray) directly.

 

 

// V1
window.addEventListener("load", (e) => {
  const selectorsArray = [".ct-text-block", ".c-btn-main"];
  const masterTimeline = gsap.timeline();

  const x = (item) => {
    return {
      trigger: item,
      endTrigger: item,
      start: "top+=0% 85%",
      end: "bottom+=0% 15%",
    };
  };

  for (const s of selectorsArray) {
    for (const item of gsap.utils.toArray(s)) {
      masterTimeline.from(item, { x: -300, duration: 1, scrollTrigger: x(item) }, 0);
    }
  }
});

// V2
window.addEventListener("load", (e) => {
  const selectorsArray = [".ct-text-block", ".c-btn-main"];
  const masterTimeline = gsap.timeline();

  const x = (item) => {
    return {
      trigger: item,
      endTrigger: item,
      start: "top+=0% 85%",
      end: "bottom+=0% 15%",
    };
  };

  for (const s of selectorsArray) {
    for (const item of gsap.utils.toArray(s)) {
      const timeline = gsap.timeline({ scrollTrigger: x(item) });
      timeline.fromTo(item, { x: -300, duration: 1 }, { x: 0 }, 0);
      masterTimeline.add(timeline);
    }
  }
});

 

Edited by OxyNinja
Forgot to add P.S. notice
Link to comment
Share on other sites

It looks like you're making one of the most common ScrollTrigger mistakes - nesting animations with ScrollTriggers. That's a logical impossibility (not really a limitation or bug in ScrollTrigger). You can't have two things that are fighting for control of the same playhead. If you nest animations into a parent timeline, that parent timeline's playhead is what moves each of the children's playheads (as it sweeps across them, they respond accordingly - the playheads are synchronized). However, if you apply ScrollTriggers to an animation, then you're telling it to have the scrollbar control its playhead. See what I mean? 


So if the parent timeline is moving that child timeline's playhead and the scrollbar is also trying to control it, they're gonna fight. 

 

Solution: don't nest them. I'm curious why you're using a masterTimeline anyway. 

 

A few other things to note: 

  1. There's no need to even define an endTrigger if it's the same as the trigger. That's the default. 
  2. It's totally useless and a bit confusing to have "+=0%" on those start/end values. 
    // odd
    trigger: item,
    endTrigger: item,
    start: "top+=0% 85%",
    end: "bottom+=0% 15%"
    
    // better
    trigger: item,
    start: "top 85%",
    end: "bottom 15%"
  3. The duration for a .fromTo() belongs in the to vars, not the from vars. And if you're only putting one tween into a timeline, there's no need to even create that timeline. Just use a regular tween. I mean there's nothing "wrong" with using a timeline too, but it feels a tad wasteful to me. 
    // bad
    const animation = gsap.timeline({ scrollTrigger: x(item) });
    timeline.fromTo(item, { x: -300, duration: 1 }, { x: 0 }, 0);
    
    // good
    const animation = gsap.fromTo(item, {x: -300}, {x: 0, duration: 1, scrollTrigger: x(item)});

Does that help? 

Link to comment
Share on other sites

@GreenSock

Hi Jack,

 

Thanks for pointing out other things. The code is generated one, not custom written, so that's the reason for those additional attributes, and I've didn't clean them up before posting. I've also quickly added duration, to prevent any confusion (still made a mistake :D) as we use gsap.defaults({duration: 1});

The reason for nesting is, that we created an animation builder that works on top of the GSAP. We created our custom timeline that controls and generates different tweens and merges them into a single timeline. In the case of scroll trigger, I need to controll/group them all, so I could kill/clear them all at once, but without affecting other tweens/timelines/selectors. Also, everything is synced together, so it moves the playhead by playing animation, or play/seek animation upon dragging the playhead

Would it work if a timeline is stopped at the beginning, so only a scroll trigger would control tweens?
If not, I guess I will target them via getTweensOf() or killTweensOf(). 
I've tests multiple different variations of the code and this only happens on scroll trigger without scrub, if scrub is defined, everything seems to work properly, so this was slightly confusing to me.

P.S. As this is my first post, I would like to thank you and all contributors for this amazing product!

Link to comment
Share on other sites

2 hours ago, OxyNinja said:

The reason for nesting is, that we created an animation builder that works on top of the GSAP. We created our custom timeline that controls and generates different tweens and merges them into a single timeline.

If your only reason is to somehow group them so you can kill() them at once, then I'd just populate an Array and do yourArray.forEach(a => a.kill())

 

2 hours ago, OxyNinja said:

Would it work if a timeline is stopped at the beginning, so only a scroll trigger would control tweens?

Not really, no. Again, if an animation is nested inside a parent timeline then its playhead will be controlled by that parent timeline. You can't have it both ways where it is controlled by the parent timeline AND the scrollbar simultaneously. It's a logical impossibility. 

 

2 hours ago, OxyNinja said:

I've tests multiple different variations of the code and this only happens on scroll trigger without scrub, if scrub is defined, everything seems to work properly, so this was slightly confusing to me.

You're probably just seeing the conflict in a state thats the one you're intuitively expecting. So let's say the master timeline is playing...and you grab the scrollbar and go up and down...the scrub on the ScrollTrigger may technically be rendering 2nd, for example, so you think it's all working. But if the parent timeline is also playing, it's gonna be wrestling with the scroll position for control, so when you're outside the range of the ScrollTrigger you'd probably see it jump to whatever the parent timeline is setting its playhead to. 

 

Example: maybe the parent playhead is at a time of 2 seconds and moving forward...but you grab the scrollbar and move it up which forces the scrubbed ScrollTrigger's animation to go backwards (while the parent is still moving forward!) See the problem? 

 

2 hours ago, OxyNinja said:

P.S. As this is my first post, I would like to thank you and all contributors for this amazing product!

Thanks for the kind words! 🙌

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...