Jump to content
Search Community

Multiple TimelineMax onComplete function help

patrk test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I'm new to GSAP but trying out TimelineMax and am having difficulty getting the onComplete function to work properly. Basically I want to trigger another timeline after one is finished playing. 

 

Here's the pen.

 

Backstory: I encountered this issue with a more complex snippet inside ScrollMagic. So... wondering if I ran into a bug?

 

Thanks for your help hive!

See the Pen Kyaorg by patrk (@patrk) on CodePen

Link to comment
Share on other sites

Hello

 

The first issue i see is that your adding an onComplete callback to the play() method. But the play() method does not accept a callback, see the play() docs:

 

https://greensock.com/docs/TimelineMax/play()

 

You can see play() only accepts 2 parameters but no callback.

  • from: *
    (default = null) — The time (or label for TimelineLite/TimelineMax instances) from which the animation should begin playing (if none is defined, it will begin playing from wherever the playhead currently is).
     
  • suppressEvents: Boolean
    (default = true) — If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the from parameter.

So you would need to place that onComplete callback inside the tl1 timeline constructor, like this:

 

var tl1 = new TimelineMax({
       paused: true,
       onComplete: function(){
          tl2.play();
       }
    }),
    tl2 = new TimelineMax({paused: true});

tl1.from(".foo", 2, {x:"400", autoAlpha: 0})
   .from(".goo", 1, {y:"400", autoAlpha: 20});

tl2.to(".foo", 1, {scale: "2"})
   .to("goo", 2, {scale: "4"});

tl1.play();

 

Here is a link to the TimelineMax Constructor docs: https://greensock.com/docs/TimelineMax/TimelineMax()

 

And here your forked pen with the onComplete firing with your call to tl2.play()

 

See the Pen Kyaemr by jonathan (@jonathan) on CodePen

 

Does that help?

 

Happy Tweening! :)

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

Thanks @Jonathan for the detailed explanation and fork. You answered my question perfectly.

 

One caveat, I would like to keep onComplete outside of tl1 timeline constructor in order to use tl1 elsewhere (without the tl2 firing).

 

I think the videos @Sahil provided might have insight into this. I'm sort of just getting started so forgive my ignorance.

 

Thanks guys!

  • Like 3
Link to comment
Share on other sites

@patrk the GSAP way of sequencing multiple timelines would be done like this:

 

See the Pen ZaLMKr?editors=0010 by jonathan (@jonathan) on CodePen

 

var masterTL = new TimelineMax({paused:true});

function timeline1(){
  
  var tl1 = new TimelineMax();  
  tl1
  .from(".foo", 2, {x:"400", autoAlpha: 0})
  .from(".goo", 1, {y:"400", autoAlpha: 20});
  
  return tl1;
}

function timeline2(){
  
  var tl2 = new TimelineMax();  
  tl2
  .to(".foo", 1, {scale: "2"})
  .to("goo", 2, {scale: "4"});
  
  return tl2;  
}

masterTL
.add(timeline1())
.add(timeline2())
.progress(1).progress(0) // preloads animation
.play();

 

Or In the case of my first example above you can just do this and have the onComplete callback be a function:

 

var tl1 = new TimelineMax({
       paused: true,
       onComplete: play_tl2
    }),
    tl2 = new TimelineMax({paused: true});

tl1.from(".foo", 2, {x:"400", autoAlpha: 0})
   .from(".goo", 1, {y:"400", autoAlpha: 20});

tl2.to(".foo", 1, {scale: "2"})
   .to("goo", 2, {scale: "4"});

tl1.play();

function play_tl2(){
    tl2.play();
}

 

 

:)

  • Like 5
Link to comment
Share on other sites

Hi guys,

I'm still having a bit of difficulty wrapping my head around how to control the playhead with a function.

 

I love the approach @Sahil shared of chaining multiple timelines. It's really modular and, I think, what I should be focusing on. 

 

In the case of this project:

 

1. I have three timelines which I'm compiling into a master timeline. I've also added some labels just to make things easy.

 

var master = new TimelineMax();
master.add("start");
master.add(intro());
master.add("curtain");
master.add(curtain());
master.add("borders");
master.add(borders());
master.add("end");

 

2. I am using ScrollMagic to control their behavior.

 

var controller = new ScrollMagic.Controller();

var scene = new ScrollMagic.Scene({triggerHook:0, offset:1})
	.setPin("#h")
	.addIndicators()
  .on("enter", function(event) {
    console.log("leaving hero area");
    master.reverse("curtain");
  })
  .on("leave", function(event) {
    console.log("entering hero area");
    master.reverse("end");
  })
  .addTo(controller);

 

3. On page load, I only play the intro segment of the timeline so I'm using the tweenFromTo method.

 

// Play only intro timeline when page loads
master.tweenFromTo("start", "curtain");

 

4. On scroll, I want the intro segment to play in reverse, then play the remaining timeline from the curtain. This is where I get stuck.

 

I'm trying to write...

 .on("enter", function(event) {
    console.log("leaving hero area");
    master.reverse("curtain");
    // Play this after...
    master.tweenFromTo("curtain", "end");
  })

 

But of course it only runs the tweenFromTo first and doesn't run the master.reverse("curtain"); 

 

I hope I'm explaining this well. 

 

Any help controlling these timeline sequences would be most appreciated.

 

Link to comment
Share on other sites

I am bit confused, as you are saying certain part should reverse then play normally, won't that cause the jump in animation? You can just use tweenFromTo and pass labels in reverse order.

 

BTW just because you are using nested timelines doesn't mean you should add everything to master. I mean if you want certain animation to reverse, you just call that part function and play it in reverse. Or create master2 timeline and call it separately. I hope that gives you some ideas.

 

See the Pen pdryZp?editors=0010 by Sahil89 (@Sahil89) on CodePen

 

I can't really visualize what you are doing so if possible post a codepen demo that will be easier to answer.

  • Like 4
Link to comment
Share on other sites

Thanks @Sahil your example really helped. 

 

I ended up making two timelines and the issue I add was with how I broke things up using vars.

 

Here's the working js FWIW.

 

function intro() {
  var tl = new TimelineMax();
  tl.from($heroTitle, .5, {y:"-30", autoAlpha: 0, ease: Power4.easeInOut}, "herotext")
    .from($heroByline, .5, {y:"30", autoAlpha: 0, ease: Power4.easeInOut}, "herotext")
    .from($heroSubtext, .75, {y:"60", autoAlpha: 0, ease: Power4.easeInOut}, "herotext+=0.75")
    .from($heroSidecontainer, 1.5, {height:"500px", ease: Power4.easeInOut}, "herotext-=.75")
    .from($heroSidehash, .75, {height: "100%", y:"1000", autoAlpha: 0, ease: Power4.easeInOut}, "herotext-=.5")
    .from($heroSidetitle, .75, {x:"50%", autoAlpha: 0, ease: Power4.easeInOut}, "sidetext-=0.25")
    .from($heroSidecity, .75, {x:"-50%", autoAlpha: 0, ease: Power4.easeInOut}, "sidetext-=0.25")
    .from($mouseIcon, 1.5, {autoAlpha: 0, y:"100px"});
  return tl;
}

function curtain() {
  var tl = new TimelineMax();
  tl.to($heroCurtain, 1, {y:"-100%", ease: Power4.easeInOut})
    .to($burgerColor, 0.25, {backgroundColor: "#494949", ease: Power2.easeIn}, "-=.5");
  return tl;
}

function borders() {
  var tl = new TimelineMax();
  tl.from($borderT, .5, {width: "0%"}, "unison")
    .from($borderL, .5, {height: "0%"}, "unison")
    .from($borderR, .5, {height: "0%"}, "unison")
    .from($borderB, .5, {width: "0%"}, "unison");
  return tl;
}

var init = intro();
var curt = curtain();
var bord = borders();

var hello = new TimelineMax({paused:true});
hello.add(init.play());
hello.eventCallback("onComplete", null);

var engage = new TimelineMax({paused:true});
engage.add("start");
engage.add(init.reverse());
engage.add(curt.play());
engage.add(bord.play());
engage.add("end");

var controller = new ScrollMagic.Controller();

var scene = new ScrollMagic.Scene({triggerHook:0, offset:1})
	.setPin("#h")
	.addIndicators()
  .on("enter", function(event) {
    console.log("leaving hero area");
    engage.tweenFromTo("start", "end");
  })
  .on("leave", function(event) {
    console.log("entering hero area");
    engage.tweenFromTo("end", "start");
  })
  .addTo(controller);

// Play only intro timeline when page loads
hello.play();

 

+ And if you have any ideas on how I could further make this more concise... I'm all ears.

 

Thanks for the great support. This forum is awesome!

  • Like 2
Link to comment
Share on other sites

Thanks @Sahil

 

I think I may have spoke too soon when I said I was all set ;)

 

I'm still struggling to get multiple timelines to reverse and then play. 

 

Here's

See the Pen ZaJONz by patrk (@patrk) on CodePen

I forked from yours. The goal with each button press is to reverse the previous timeline, then play that function's timeline.

 

Any ideas?

 

 

Link to comment
Share on other sites

Thanks @Carl. I've taken a look at this and can see where it's definitely useful.

 

In my example I used buttons to try to simply the pen, hoping someone would help me with an onComplete method or a better written function. 

 

What I'm really trying to do is have scroll trigger my events. And the key behavior I'm going after is to have an intro timeline reverse and play another timeline after that. I'm new to GSAP and still learning JS, so I'm sure there's an elegant way to solve this.

 

The problem I'm having is with the window.onload event. I want my intro animation to play in, but if I place an onload event after the following code it messes up the ScrollMagic timeline.

 

var intro = intro(), scroll = scroll(), controller = new ScrollMagic.Controller();

function intro() {
  var tl = new TimelineMax();
  tl.from($heroTitle, .5, {y:"-30", autoAlpha: 0, ease: Power4.easeInOut}, "herotext")
    .from($heroByline, .5, {y:"30", autoAlpha: 0, ease: Power4.easeInOut}, "herotext")
    .from($heroSubtext, .75, {y:"60", autoAlpha: 0, ease: Power4.easeInOut}, "herotext+=0.75")
    .from($heroSidecontainer, 1.5, {height:"500px", ease: Power4.easeInOut}, "herotext-=.75")
    .from($heroSidehash, .75, {height: "100%", y:"1000", autoAlpha: 0, ease: Power4.easeInOut}, "herotext-=.5")
    .from($heroSidetitle, .75, {x:"50%", autoAlpha: 0, ease: Power4.easeInOut}, "sidetext-=0.25")
    .from($heroSidecity, .75, {x:"-50%", autoAlpha: 0, ease: Power4.easeInOut}, "sidetext-=0.25")
    .from($mouseIcon, 1.5, {autoAlpha: 0, y:"100px"});
  return tl;
}

function curtain() {
  var tl = new TimelineMax();
  tl.to($heroCurtain, 1, {y:"-100%", ease: Power4.easeInOut})
    .to($burgerColor, 0.25, {backgroundColor: "#494949", ease: Power2.easeIn}, "-=.5");
  return tl;
}

function scroll() {
  var tl = new TimelineMax({paused:true});
  tl.add(intro.reverse())
    .add(curtain())
  return tl;
}

var scene = new ScrollMagic.Scene({triggerHook:0, offset:1})
	.setPin("#h")
	.addIndicators()
  .on("enter", function(event) {
    console.log("leaving hero");
    scroll.play();
  })
  .on("leave", function(event) {
    console.log("entering hero");
    scroll.reverse();
  })
  .addTo(controller);

window.onload = function() {
  intro.play();
};

 

Any help would be awesome.

 

 

 

Link to comment
Share on other sites

Ohk I found the issue, it is because your scroll function returns paused tween. It takes control over all the elements and because you are using from tweens inside timeline, it renders all elements to their end position and pauses itself, which overrides everything. If you remove paused: true from the timeline returned by scroll function, you will see what is happening.

 

I tried to trigger scroll function when onload animation finishes but I don't know exactly what to suggest, a limited codepen demo would be a lot easier to understand visually.

 

Following is the what I tried to quickly have something to test.

 

See the Pen POOKWE?editors=0010 by Sahil89 (@Sahil89) on CodePen

 

Following is the video that explains what immediateRender is, it reminded me how I was super excited about using from tween everywhere just to later find out how it gets really tricky.

 

 

  • Like 1
  • Thanks 1
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...