Jump to content
Search Community

Object "jumps" to original position while modifying

Suresh 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 trying to build a timeline based application and I'm seeing some behavior that I don't quite understand. I've created a codepen along with steps for 3 scenarios to recreate this:

See the Pen tlFDc by anon (@anon) on CodePen

 

From those 3 scenarios, 1 and 3 work as expected, but scenario 2 doesn't - the rectangle in there just jumps back to its original position. (Sorry if this doesn't make sense on its own, but it'll be clear in the codepen). In scenario 3, if I somehow "reset" the GSAP timeline, then everything starts to work fine again.

 

Why is this happening? Any ideas?

Link to comment
Share on other sites

Hi Suresh.

 

First, I'd like to acknowledge how wonderful you codepen example was. What a treat. Very clear and expertly described the issue. Great Job!

 

The way to resolve the issue is to add

 

t1.time(duration) to the end of the updateEndPos method

 

function updateEndPos() {
        t1.clear();
        endPos.x = rect.getX();
        endPos.y = rect.getY();
        var duration = $("#slider").slider("value");
        t1.fromTo(rect, duration, {setX:startPos.x, setY:startPos.y}, {setX:endPos.x, setY:endPos.y});
        t1.time(duration);
    }

You can see it working here:

http://codepen.io/GreenSock/pen/4e75f72205f8b79294b01be2e5b1d94e

 

----

 

Now go back to your original pen and follow the steps in scenario 2. When you mousedown the second time and see the object jump back to its original position continue to drag and you will notice that the blue box is still being dragged its just offset from the mouse in an odd way. Furthermore, once you release the mouse you can nudge the slider around and the animation will work as expected.

 

So what was happening was that once you started dragging again, the next render showed the object where the timeline had it a time() of 0 (at the beginning) but it was still allowing you to drag it based on the last position that KineticJS was aware of.

 

Keep in mind the timeline had a tween in it but the playhead was paused at the beginning.

The timeline is trying to render the startX and startY positions at the beginning of the tween, but you are dragging the object somewhere else and KineticJS is allowing you to move the object around at the same time TimelineLite is trying to keep the object in its start position.

 

When you are done dragging, the dragEnd is the last thing to notify the layer to re-render and thus the object stays where you dropped it. When you mousedown that somehow triggers another render and this time it appears that the paused start position of the timeline is being honored again. 

 

By adding t1.time(duration) to the end of the updateEndPos() function we are syncing up the current time() of the timeline with the way things should be displayed when the timeline is complete.

 

Still confusing? I know, but another way to look at it was that somewhere along the way you were asking to see the beginning of the timeline and end at the same time... which would be very difficult.

 

Hopefully my solution works and the explanation helps.

 

[edit] i've modified this post a few times recently. give it another read. perhaps it will be better the second time around ;) [/edit]

  • Like 3
Link to comment
Share on other sites

Hi Carl,


 


Thanks for the super detailed explanation! I didn't get it the first time, but after taking a nap and coming back to it, it makes sense :D 


 


This line was what I was missing:


    Keep in mind the timeline had a tween in it but the playhead was paused at the beginning.


 


Thanks again! I'm constantly amazed by how awesome your support is. Glad you liked the codepen. :)


  • Like 1
Link to comment
Share on other sites

Carl, I've got a follow up question on this.


 


Let's say I'm trying to add 2 tweens to a timeline one after the other - one for x position and one for y position (I'm keeping them as separate tweens coz I'll need to update each one individually). Now, here's what's happening:


 


If I update the timeline after each tween is inserted, then, it behaves like the second update was never called.


ie,


- create tween for x & add to timeline


- set the timeline to the current time again so it can update (the t1.time(duration) bit)


 


- create tween for y & add to timeline


- set the timeline to the current time again so it can update


 


When I do this, then as soon as I release the mouse, the object's x position will be updated on screen, but its y position will not. If I scrub the timeline a bit, then it'll update.


 


However, if I update the timeline's time only once at the end, like so, it works:


- create tween for x & add to timeline


- create tween for y & add to timeline


- set the timeline to the current time again so it can update


 


I've added a codepen to show this: 

See the Pen IrfpH by anon (@anon) on CodePen


 


Is there an issue in calling an update too quickly, or is it something else?


Link to comment
Share on other sites

Hi Suresh,

 

Again you have gone beyond expectations with your very nice codepen and also have managed to stumble into a sort of tricky behavior.

 

I'm 98% certain the reason the second y value snaps back to its previous position and is NOT rendered at the "end state" of the tween (where it was released at the end of the drag) is due to an optimization built into TimelineLite.

 

When you added the first tween you updated the timeline's time(). TimelineLite said, "hey it looks like I have to jump my playhead to a new time, cool, I'll do that and make sure that all the properties of all the objects that I'm tweening are updated to reflect the NEW time and position of my timeline's playhead"

 

When you added the second tween, you again tried to set the timeline's time(). This time though TimelineLite says, "Whoa, the time() you want to change to is the same as the current time(), I'm going to take it easy and not bother updating everything again"

 

TimelineLite isn't as lazy or careless as I may make him sound. Its just that in order to eek as much speed out of the engine he has been configured to avoid "seemingly" unnecessary updates such as updating all internal values of objects when time() gets set again to the same time(). 

 

Obviously in your case, you have added new tweens and it makes perfect sense for all the properties of all the objects controlled by the timeline to be updated. 

 

The good news is that you can force the timeline to update by moving the playhead via progress().

 

A little nudge like this 

t1.progress(0).progress(1);

Forces everything to work as expected.

 

I made a more simplified pen to illustrate the fix working:

http://codepen.io/GreenSock/pen/6209e21f04be1e606d2248bef71644f5

 

I simplified the pen just so that someone like Jack (with fresh eyes) can easily ascertain if my logic is sound.

 

 

Again, I'm pretty sure I'm on the right track here. If I'm grossly out of line, Jack will clarify.

 

Let us know if you need any more help.

  • Like 2
Link to comment
Share on other sites

Thanks a lot for going into such detail, Carl. You're awesome, in case you didn't already know! I think I understand the behavior a lot better now. I didn't imagine I would hit against an optimization trick, but what you said definitely makes sense. 


 


1. BTW, is this the only way to force an update? I'm asking because I'm actually trying to build a timeline based application. So, let's say the code is


progress(0).progress(currentTime)


 


Now, if the user tries to modify the box's position at time 0, currentTime will be 0, so we're essentially doing progress(0).progress(0) - which won't force an update. I can very easily work around this special case of course, but I just wanted to check if using progress( ) is the only way to force an update.


 


2. Does it matter whether I use progress(0) or time(0)? For all practical purposes, I assume they're the same? I tried it with time(0) and it seems to work, so I'm really just asking out of technical curiosity.


 


Oh.. and.. um.. uh.. well, since you keep praising the codepen I posted, I thought I should tell you this. The main reason I posted it that way is coz… I have no idea how to explain that behavior in just a post. It's way too complicated! :D. So, every time you praise the codepen, I end up feeling a tiny bit guilty about it (but only a tiiiiny bit)


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