Jump to content
Search Community

Nested Timeline with ScrollTrigger

Web Bae test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Is there a recommended way to make one scrollTrigger timeline last the same duration as another timeline?

 

In the codepen example, I want the orange box to complete it's timeline over the time it takes the green box to rotate 360 degrees.

 

I found this post which says the solution is to not nest them.  I made it work for my example but was wondering if there's a .add() sort of syntax to have a master timeline?

 

Thanks!

See the Pen wvEGKVq by learyjk (@learyjk) on CodePen

Link to comment
Share on other sites

Hi,

 

This could be one of the ways I can think of:

function orangeTimeline() {
  return gsap
    .timeline()
    .from(".box.orange", {
      xPercent: -100
    })
    .to(".box.orange", { xPercent: 0 })
    .to(".box.orange", { xPercent: 100 });
}

masterTl
  .to(".box.green", { rotateZ: 360 })
  .add(orangeTimeline(), "<");

const children = masterTl.getChildren(false);

children[0].duration(children[1].totalDuration());

This approach counts on the fact that you'll add the instances in the same order all the time.

 

If you can set an ID on the timeline and the instance, then it becomes far easier and you don't depend on the index position of each instance:

function orangeTimeline() {
  return gsap
    .timeline({
    id: "orangeTl"
  })
    .from(".box.orange", {
      xPercent: -100
    })
    .to(".box.orange", { xPercent: 0 })
    .to(".box.orange", { xPercent: 100 });
}

masterTl
  .to(".box.green", { rotateZ: 360, id: "box" })
  .add(orangeTimeline(), "<");

const orangeTl = masterTl.getById("orangeTl");
const boxTween = masterTl.getById("box");

boxTween.duration(orangeTl.totalDuration());

I'm pretty sure that if you get creative you'll find a few other ways to achieve this.

 

Finally in your ScrollTrigger configuration, be aware that there is no element with the class body (".body"), it should be without the dot:

let masterTl = gsap.timeline({
  scrollTrigger: {
    trigger: "body",
    start: "top top",
    end: "+=3000",
    pin: ".box-wrap",
    scrub: 1,
    markers: true
  }
});

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

  • Solution
1 hour ago, Web Bae said:

In the codepen example, I want the orange box to complete it's timeline over the time it takes the green box to rotate 360 degrees.

I know you want them to match durations, but which one is supposed to determine the overall duration? The orange or green? I'll assume orange for now (so you want the green's duration to change to match the orange's).

 

There are tons of ways to approach this. Examples...

 

Place them in a certain order that makes it easier: 

masterTl.add(orangeTimeline()).to(".box.green", { rotation: 360, duration: masterTl.duration() }, "<");

See the Pen rNZeLdK?editors=0110 by GreenSock (@GreenSock) on CodePen

 

Or if for whatever reason you need to add the green one to the timeline first (not sure why you would)...

let orangeTL = orangeTimeline();
masterTl.to(".box.green", { rotation: 360, duration: orangeTL.duration() }).add(orangeTL, "<");

There are plenty of other ways too, but I don't want to overwhelm you. :)

 

Does any of this help? If you have other requirements that weren't clear initially, just let us know. 

  • Like 1
Link to comment
Share on other sites

Thanks so much - really helpful stuff.

 

I'm building a component that has text content and progress bar on the left side and assets (video) on the right. So the goal is to animate some things on the left (timeline1) while the corresponding videos animate in and out on the right side. (timeline 2). I've got to sync up the timing so everything swaps over on cue.

 

I added two more pairs of boxes to make the example a little truer to my use case.

 

I've been able to get it working with Jack's solution. (thanks again). It's a little unintuitive to me that calling masterTl.duration() in the greenbox tween  makes it take the duration that I want it to take (call it 1 "scroll distance unit"). I would think that masterTl.duration() would be 3x "scroll distance units" based on the ScrollTrigger trigger and start/end points. Can you help explain why my intuition is incorrect?

 

Trying to explain it to myself, I'm thinking that ordering them with orangeTimeline first, followed by green, when we call .duration() is just the duration for the tween at that 'point in time' i.e. the second and third animation pairs or .add and .to aren't affecting the duration at this point, just the first orangeTime (green box doesn't matter since we are explicitly telling it to have the duration of the masterTl at this point in time and it starts at same time as orange). Would that be a good way to think about it?

 

I like Rodrigo's id examples as well - going to dive into that now.

 

Thanks again!

Link to comment
Share on other sites

2 hours ago, Web Bae said:

It's a little unintuitive to me that calling masterTl.duration() in the greenbox tween  makes it take the duration that I want it to take (call it 1 "scroll distance unit"). I would think that masterTl.duration() would be 3x "scroll distance units" based on the ScrollTrigger trigger and start/end points. Can you help explain why my intuition is incorrect?

Timelines/Tweens have absolutely no clue about "scroll distance units" - they work purely in time/duration/seconds. When you add a ScrollTrigger to a tween/timeline, think of it like that ScrollTrigger controls the playhead of that animation. When you apply a scrub value to a ScrollTrigger, all that does is force it to squish/stretch that animation to play inbetween the start/end scroll positions but it never actually changes its duration. So you could have a timeline that's 10,000 seconds long, but if you apply a ScrollTrigger to it with scrub: true and it the start/end scroll positions are only 50px apart, that super long timeline would still get squished to fit inside that 50px area, but if you check timeline.duration(), you'd still see 10,000. All the ScrollTrigger does is map the "progress" of the scroll position to the progress of the animation (the playhead).

 

It's like watching a 2-hour video, but having the video playhead mapped to something that can move it around fast or slow. It's still a 2-hour movie, you're simply messing with the playhead.

 

In your demo, the orange timeline is 1.5 seconds, so as soon as you add() that to the empty master timeline, the master timeline's duration is 1.5 seconds. Then, when we add the green tween and use the master timeline's duration(), it'll be 1.5 (matching the orange one). See why?

 

2 hours ago, Web Bae said:

when we call .duration() is just the duration for the tween at that 'point in time' i.e. the second and third animation pairs or .add and .to aren't affecting the duration at this point, just the first orangeTime (green box doesn't matter since we are explicitly telling it to have the duration of the masterTl at this point in time and it starts at same time as orange). Would that be a good way to think about it?

Pretty much, but be careful not to mistakenly think that while the timeline is playing (or after it's built), it's grabbing that duration dynamically. It has nothing to do with playback. We're just creating the tweens/timelines (nothing is happening yet). So think of it purely in terms of JavaScript (don't think animation yet)...

 

// insert 1.5-second orange timeline
masterTl.add(orangeTimeline());

// the duration at this point is 1.5
console.log(masterTl.duration());

masterTl.to(".box.green", { 
  rotation: 360, 
  duration: masterTl.duration() // this resolves to 1.5 because it's invoked NOW (before our green tween is inserted)
}, "<");

It's just a shorter way of doing this with a variable: 

// insert 1.5-second orange timeline
masterTl.add(orangeTimeline());

let duration = masterTl.duration();

masterTl.to(".box.green", { 
  rotation: 360, 
  duration: duration 
}, "<");

Does that clear things up?

  • Like 4
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...