Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
patrk

Multiple TimelineMax onComplete function help

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 https://codepen.io/patrk/pen/Kyaorg.

 

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!

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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.

 

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Nothing really, you are doing great. Just if you don't know, you can pass tweens to ScrollMagic and it will play and reverse it for you. So if you are simply reusing same tween, you can use 'tween' method of ScrollMagic to pass tween.

  • Like 3

Share this post


Link to post
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?

 

 

Share this post


Link to post
Share on other sites

I think this old pen may be of help. press the buttons whenever you want. The current animation will reverse before the requested animation plays. Its set to run super slow so you can see how it works

 

See the Pen XMygdo?editors=0010 by GreenSock (@GreenSock) on CodePen

 

  • Like 3

Share this post


Link to post
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.

 

 

 

Share this post


Link to post
Share on other sites

One question for now, are you sure onload and ScrollMagic enter events are not overlapping?

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×