Jump to content
GreenSock

trompx

Change ease on back & forth animation - Timeline animation with delay

Recommended Posts

Hello,

I want to apply a new ease when reversing a timeline.

To make it works, I tween the timeline progress. I get the current progress and calculate the duration remaining.

I can reproduce the same ease by doing:

 

  // Original animation ease

  tl.fromTo(
    wrapperRef.current,
    {
      opacity: 0.3,
    },
    {
      opacity: 1,
      duration: duration,
      ease: myease,
    },
    0
  );

  // Reproduce the animation ease by tweening the timeline

  tl.fromTo(
    wrapperRef.current,
    {
      opacity: 0.3,
    },
    {
      opacity: 1,
      duration: duration,
      // Without ease so Linear.easeNone because we set the ease on the tweenTl
    },
    0
  );
  const currentProgress = tl.progress();
  const remainingDuration = (1 - currentProgress) * tl.duration();
  tweenTl && tweenTl.kill();
  tweenTl = gsap.to(tl, {
    duration: remainingDuration,
    progress: 1,
    ease: myease,
  });

I do the same when I want to reverse, instead I just tween progress to 0.

 

However, if I have a delay in the timeline, when I tween its progress, it breaks.

By doing:


  tl.fromTo(
    wrapperRef.current,
    {
      opacity: 0.3,
    },
    {
      opacity: 1,
      delay: 1,
      duration: duration,
      ease: myease,
    },
    0
  );

The timeline waits 1 second before executing the animation and applies myease only to the animation.

Say the duration = 1s.

In the first case, the ease spans on only that 1s.

 

If now I do:

  const currentProgress = tl.progress();
  const remainingDuration = (1 - currentProgress) * tl.duration();
  tweenTl && tweenTl.kill();
  tweenTl = gsap.to(tl, {
    duration: remainingDuration,
    progress: 1,
    ease: myease,
  });

Here, tl.duration() = 2s

So the ease is also applied to the delay thus "breaking" the animation.

 

Is there a solution to that problem?

  • Like 1
Link to comment
Share on other sites

That's the correct behavior. If you insert a tween into a timeline and that tween has a 1 second delay, it will place the start of that tween at the 1-second spot: 

 

timeline |--------------------|
tween              |----------|

So the parent timeline would be 2-seconds long. Animating it backwards to the start would include that gap. 

 

Why are you tweening to a progress of 1 if your goal is to reverse the timeline? Did you mean 0? 

 

And if you're trying to reverse it with a different tween, why would you get the remaining time until the end rather than the elapsed time from the start? 

 

Why animate progress instead of time? It seems like that might be easier. 

 

I'm probably misunderstanding what you're trying to do, but here's a quick demo: 

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

 

If you still need help, please make sure you provide a minimal demo

 

Good luck!

Link to comment
Share on other sites

Thanks for your input Jack

Hum.. I get that the timeline duration increases when I add a delay, but, correct me if I am wrong, when I set an ease, it is only applied to the animation, and not to the animation + ease.

Hence the problem while animating the progress (or the duration) with another tween, as the ease applies to the whole (animation + delay).

As I stated

Quote

I do the same when I want to reverse, instead I just tween progress to 0

 

When I am talking remainingDuration, it is to have a single variable but its value is different in forward and backward.

// play
const remainingDuration = (1 - currentProgress) * tl.duration();
// reverse
const remainingDuration = currentProgress * tl.duration();

I could of course animate the duration directly but the problem stays the same.

 

Anyway I kind of found a solution by, separating my timeline in two timelines (one which animates a wrapper/background and a second that animates its content which animates after a delay). For the background, I can tween the timeline as it doesn't introduce a delay. For the content which uses a delay, I HAVE to use play()/reverse() (because of the problem of tweening the timeline which applies the ease to the delay and not only the animation) but managed to sync it with the first timeline by normalizing the reverse animation according to the first timeline duration through timescale.

 

Link to comment
Share on other sites

10 hours ago, trompx said:

correct me if I am wrong, when I set an ease, it is only applied to the animation, and not to the animation + ease.

I assume you meant "animation + delay", and yes. Timelines don't have any ease whatsoever - only a tween has an ease, and it applies to the animation, not the delay (that'd be super weird if it included the delay). 

 

I'm really struggling to understand WHY you're doing these things and I suspect you MIGHT be over-engineering things a bit and there's a much simpler solution but I can't say because it isn't clear to me what exactly you're trying to accomplish or the "why" behind it. For example, if you want to move the playhead across a tween using a particular ease, why are you putting it in a timeline anyway? Why not go directly to the tween? 

 

It sounds like you already got a solution (yay!) but if you still need help, please provide a minimal demo and we'll try to offer advice once we see it in context. Just a simple colored <div> animating is fine. 

Link to comment
Share on other sites

Yes my bad Jack, I meant "animation + delay".

If the ease is an easeOut for instance, when I play it starts fast, and if I reverse towards the end of the animation, it starts reversing slow.

So that's why I want to be able to set a different ease for the reverse, i want the reverse to behave like the easeOut.

 

So play/reverse doesn't work here. So the solution is to tween the timeline, so I can tween the progress (or the duration as you suggested) of the timeline with the ease I want.

 

Now for the usecase, I want to show/hide a menu with elements. I want to have the same ease to showMenu (forward) and to hideMenu (backward/reverse). I want the menu to hide immediately and as fast as it initially appears on hideMenu.

 

Now, when showing the menu, I have a menu background, AND menu elements that I animateIn with a delay, and that's here that everything falls appart with gsap.

As I can't use play/reverse (because of the condition of needing to start hiding the background as soon as I hideMenu: in fact if I were just to use reverse and if we were at the end of the animation, by reversing the timeline, the background would not hide immediately because it would first animateOut the menu elements), I have to tween my timeline.

 

Now if I animate both the menu background + the menu elements, there will be a problem.

In fact, due to the delay introduced by the menu elements animation, when tweening the timeline and setting my ease, it applies now to the tl.duration() (which takes into account the delay!).

 

So I had to split the timeline in two.

1. for the menu background. As it doesn't have a delay, tweening the timeline and setting the correct ease just works.

2. for the menu elements, because of the problem I mentioned (where tweening the timeline with an ease applies also to the delay), I have to use play/reverse (which is less than ideal as I lose the possibility to set a different ease on reverse). As a hack, I sync the duration of the menu background hiding with the elements animating out. So I needed to use timescale to end up with an animation duration = to the tween duration of the background.

 

It is not the best but the effect looks almost exactly as I'd wish. But it would be a infinitely easier if gsap would allow to set a different ease when reversing.

Anyway, thanks for the help, really appreciate!

 

Link to comment
Share on other sites

29 minutes ago, trompx said:

But it would be a infinitely easier if gsap would allow to set a different ease when reversing.

That would create significant logic problems. Trust me - a feature like that would be a disaster. :) Imagine you've got a "power4.out" ease on a tween and then halfway through you call reverse() and you want it to use a "power2.in" ease in reverse. Well at 50%, the interpolation would be in a completely different place, so you'd suddenly see a jump. 

 

For example, let's say you're tweening element.x from 0 to 100. With a power4.out tween, 50% might render element.x at 94.342, but a power2.in may render it at 29.851. So at the moment of reversing, element.x would JUMP from 94.342 to 29.851. 

 

GSAP already allows for the yoyo phase of an ease to be different and that's possible because it only switches when it is all the way complete (thus the eases would render identically). See the yoyoEase property. 

 

The most common way of dealing with the situation you described where you have a menu opening animation that you don't want a true reverse on (because you're CHANGING the ease) is to dynamically create it at that time. Don't pre-bake everything into a single timeline. Sorta like:

function open() {
  gsap.to(..., {x: 100, ease: "power2", overwrite: true})
  // and all the other parts...
}

function close() {
  gsap.to(..., {x: 0, ease: "power2", overwrite: true});
  // and all the other parts...
}

I hope that helps.

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