Jump to content
Search Community

How to make a tween pickup the timeline from any given point?

Elindo test
Moderator Tag

Go to solution Solved by nico fonseca,

Recommended Posts

** this is were the real thing begins... button interaction ** 

 

btna push the green rectangle from left to right - to x:200  in 5 seconds with a timeline to follow

btnb push the green rectangle from right to left - to x:0 in 4 seconds with a timeline to follow.

 

What if you push btna, and then push btnb before btna finish his timeline?

 

What if kee pushing  btna and btnb every 2 to 3 seconds before any of the time line finish?

 

The problems is that the timelines are not recognizing the position of the green square to follow the timeline properly without jumping the green square into another place... the sequence of the red and blue squares  opacity also goes out of place.

 

How can you make the timelines recognize the position of the green square, so that the timelines can follow the movement of the green square from any location? 

 

 

 

 

See the Pen vYZLjJz by Elindo586 (@Elindo586) on CodePen

Link to comment
Share on other sites

Hey Elindo.

 

Apologies but I think we're having a hard time following what it is that you're trying to achieve.

Your demo is a little hard to follow as the naming conventions don't match what we're seeing visually.

I've put together this template for you to try and ease the process.

See the Pen 370d3b53e186b8924a1bcd3b190bbe6d by GreenSock (@GreenSock) on CodePen



Could you possibly add the code you're struggling with in to this pen? It would also help if you stated alongside the code what you're expecting the result to be and what you perceive as the issue. If you could use minimal jargon that would also help ease the confusion.

Let us figure out how to solve the problem. Just explain simply what the problem is. Maybe you could phrase it similarly to this?

When I press button one I would like green square to do
 

  • Like 2
Link to comment
Share on other sites

 

Button A pushes to the right

Button B pushes to the left 

 

The problem is when you *alternate between pushing A and B the green rectangle starts to jump.

 

If you do this:

Click A (good so far)

Click B (good so far) (push it before A finish his sequence) 

Click A again (before B finish his sequence) ... now the green rectangle jumps back to where it was with the A button, instead of picking the movement from where the B button left the green rectangle.   I don't want the A button to make the green rectangle jump. I want the A button to pickup the movement at whatever place the B button left the green rectangle.

 

Now, don't worry too much about the red and blue squares, those are only part of the timeline when you push A or B...  if we can fix the green rectangle from jumping, then likely the timeline will fall in order and fix the sequence of the blue and red squares. 

 

 

 

  • Like 2
Link to comment
Share on other sites

It would be easy to think you can put a "reverse" option to make the green rectangle go back, but for this animation when you take the green rectangle back, then I have a different set opacity settings that wouldn't work with the "reverse" coding..

 

I am trying to find a way to "reverse" the green rectangle, and play the proper set of opacities that go with the left and right movements. 

  • Like 1
Link to comment
Share on other sites

I think I might have the solution, but I need to think on how to code it this other way..

 

IF gsap doesn't have a tool to make a tween pick up a move from a random location... ELSE.. (see what I did there ;)) I would need to split the tween..

 

I could add the green box in a single tween  with push button A forward, and push button B reverse..

 

Then.. maybe I could add another set of separate tweens using some kind of IF/ELSE combination for proper set of opacities for each  button  based on the green box being < or > from position X .   I would also need to make sure the A button hides the opacities from B, and that B hides the opacities from the A button.

 

If I get this done, then I'll be half way through the hard parts for me... 

 

 

Link to comment
Share on other sites

@Elindo first of, all, thanks for joining Club GreenSock🙌

 

If I understand your question correctly, the problem is that you're only calling .play() every time but that only makes the playhead go forward from wherever it is at that moment. For example, let's say you've got a 5-second linear animation animating x from 0 to 500...and then after 4 seconds (x is now at 400), you pause() it and play a DIFFERENT timeline that makes x to back toward 0. Then, when x is at 200 you .invalidate() and .play() the first timeline...

 

Since the playhead is still at 4 seconds, it will start playing from there! And since you called invalidate(), that flushes the recorded starting values (0) and set it to whatever it is now (200), thus that animation is now interpolating from 200 -> 500 over the course of 5 seconds (the duration). Therefore at a time of 4 (which is where the playhead is), x would need to be 440, hence the jump from 400 to 440. It's all working exactly as it should.

 

The solution is simple: use .restart() instead of .play() so the playhead always starts at 0:

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

 

Does that clear things up for you? 

 

By the way, you can chain calls like animation.invalidate().restart()

 

Also, I'm not sure what you were trying to do with the clear: self in the timeline vars but that's definitely wrong. 

  • Like 1
Link to comment
Share on other sites

Thanks... indeed that solves much of the problem...

 

 

Saaaayyy...  Is there a way to link the animation to the position  of X:0  and X:200?????

 

 

If you see this code:

var extend = gsap.timeline({paused:true });
extend.to("#Rod", {x:200, duration:5, ease: "none"})
       .to("#P, #B1, #B2, #A2", {opacity:0, ease: "none"}, "<")
      .to("#A1", {opacity:1, ease: "none"}, "<")
      .to("#A1", {opacity:0, ease: "none"}, "+=0.1")
      .to("#A2", {opacity:1, ease: "none"}, "<")
      .to("#A2", {opacity:0, ease: "none"}, "+=1")
      .to ("#P", {opacity:1}, "<");

 

This part:

.to("#A1", {opacity:0, ease: "none"}, "+=0.1")

^^ This, and the tweens after it, should be happening when "#rod" reached x:200 in 5 seconds.   It works good if you start from x:0, or apparently any moment before x:200 ?

 

But if you start from X:200, then the tween goes way back from the beginning taking another 5 seconds and previous opacities before the "+=0.1") code line.

 

Also, if you push A several times it seems to increase the run time.

 

Sooo... is there a way for the timeline to recognize the position of x,  and play the opacities and timing of the  tween according to the x position?

 

And.. maybe not increase the time of the tween if you push A several times?  (I am not sure if this is asking for the moon on technology)

 

 

 

 

 

 

 

 

 

Link to comment
Share on other sites

 

1 hour ago, Elindo said:

This part:

.to("#A1", {opacity:0, ease: "none"}, "+=0.1")

^^ This, and the tweens after it, should be happening when "#rod" reached x:200 in 5 seconds.   It works good if you start from x:0, or apparently any moment before x:200 ?

I read this a few times and still don't understand, sorry. Again, please provide a minimal demo. @Cassie was kind enough to even create a template for you. It would be very nice if you used that. Your demos have been very difficult to follow, particularly because of the naming conventions and non-essential code. It's very difficult to clearly see what A1, A2, B1, B2, P, etc. are (there are no visual labels). 

 

2 hours ago, Elindo said:

But if you start from X:200, then the tween goes way back from the beginning taking another 5 seconds and previous opacities before the "+=0.1") code line.

If I understand you correctly, that's because you have a 5-second tween at the beginning of your timeline that basically says "animate from whatever x is currently to x: 200 over the course of 5 seconds", so if it is ALREADY at 200 when you restart() that animation, of course tweening x from 200 to 200 over the course of 5 seconds will look like it's doing nothing (because no movement is necessary going from 200 to 200). 

 

I wonder if you expected GSAP to just totally delete that entire animation from the timeline if the destination value matches the current value (skipping the 5 seconds)(?) That would be a very bad thing, as it would mess up all the timing in that timeline. Plus what would you do if that animation had multiple values where some of them matched and some didn't? 

 

Of course you could add custom logic to accommodate whatever you want. It wouldn't be terribly difficult to dynamically build the timeline such that you wouldn't add in certain animations if the values are already there, for example. You could even adjust the duration according to how far it is away from 200 so that it's always a consistent speed:

 

let x = gsap.getProperty("#Rod", "x"),
    speed = 40; // pixels per second
if (x !== 200) {
  extend.to("#Rod", {x: 200, duration: Math.abs(200 - x) / speed, ease: "none"});
}

As I recommended earlier, you would probably benefit from structuring your logic differently so that it's more dynamic instead of always calling .invalidate().restart() - just wrap your animation logic into a function that you call whenever you want to animate to a particular state: 

function goToState1() {
  let tl = gsap.timeline({defaults: {ease: "none"}),
      speed = 40;
  tl.to("#Rod", {x:200, duration:Math.abs(200 - gsap.getProperty("#Rod", "x")) / speed})
      .to("#P, #B1, #B2, #A2", {opacity:0}, "<")
      .to("#A1", {opacity:1}, "<")
      .to("#A1", {opacity:0}, "+=0.1")
      .to("#A2", {opacity:1}, "<")
      .to("#A2", {opacity:0}, "+=1")
      .to ("#P", {opacity:1}, "<");
  return tl;
}

Then whenever you need to animate to that state, you simply call that function. You can keep track of the currently-running timeline so that you can kill() it when you need to interrupt it to start a different one: 

let curAnimation;
buttonA.onclick = () => {
  if (curAnimation) {
    curAnimation.kill();
  }
  curAnimation = goToState1();
}

buttonB.onclick = () => {
  if (curAnimation) {
    curAnimation.kill();
  }
  curAnimation = goToState2();
}

And just make sure that you always return the timeline from the function so you can store it as the curAnimation. Then you've got a clean function where you define the animation to each state. It modularizes your code too. 

 

2 hours ago, Elindo said:

And.. maybe not increase the time of the tween if you push A several times?  (I am not sure if this is asking for the moon on technology)

I covered that in the code above where you alter the duration according to the current position. Very possible, yes. It's all just logic in your JavaScript. The tools let you do almost anything :)

 

I hope that helps.  

  • Like 1
Link to comment
Share on other sites

Oh I am sorry.. you guys are too nice.. I will put labels on the boxes on future postings.

 

Rod is the green rectangle... A1 and A2 are the set of squares to your left side of the screen, B1 and B2 are the squares on your right side of screen, and P are the two center squares..

 

If the tween starts at X:200 moving from your left to right, then you only need the set of on and off opacy sequence that comes after reaching x:200.

 

I need to digest your info and test to see if I can make work..

 

 

 

 

Link to comment
Share on other sites

Check this one..   I am getting a syntax error, but I am not sure why

 

I got 2 functions with animations inside of of them.

 

One function will play the entire sequence of the A button if #Rod < x:200

 

The second function will play the last few tweens of the entire sequence of the A button if #Rod == x:200

 

I am trying to do an if/ if else for button A...  but apparently the code is wrong and I don't know why?  :(

 

I would do something similar for the B button..

 

btna.onclick = () => {
  if ("#Rod"< x:200) {
      animation.kill();
      animation = stagea1();
}
 else if ("#Rod" == x:200) {
   animation.kill ();
   animation = stagea2();
   }
}

 

 

Link to comment
Share on other sites

  • Solution

@Elindo   x:200  This syntax doesn't exist in Javascript, you can use the getProperty method to get the x value from your element:
 

btna.onclick = () => {
  if (gsap.getProperty("#Rod","x") < 200) {
      if(animation)animation.kill();
      animation = stagea1();
}
 else if (gsap.getProperty("#Rod","x") == 200) {
   if(animation)animation.kill();
   animation = stagea2();
   }
}

 

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