Jump to content
Search Community

Transform origin updated only once

RolandSoos 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

Well, you've got a very odd scenario here that's a bit tricky.

 

First, let me give you a few solutions you can choose from:

  1. Don't pause the timeline initially. There's absolutely no reason to do that here - you play() it on the same tick, so it's useless. 
  2. Set immediateRender:false on the fromTo() tween. 
  3. Put the transformOrigin in the "from" part of the fromTo() (eliminate the set() altogether).
  4. Don't embed the set() in the timeline - just do a TweenMax.set() first, before creating the timeline.

The problem you ran into would only happen on a paused timeline with a set() that's placed exactly at the beginning and a transform-related fromTo() in the same timeline. In all our years of having GSAP in the wild, I don't think anyone else has reported anything like this - so gold star for you! ;) 

 

By default, fromTo() tweens have immediateRender set to true, but the set(), when in a timeline, will actually check to see if it's at the current time of that parent timeline and if so (and if the parent isn't paused), it'll set immediateRender:true. So in your case, fromTo() was rendering the transforms FIRST, and then when you play(), it's triggering the set() but that's too late - the fromTo() has already recorded its starting/ending values. And when you do the first transform on an element, GSAP must set its transformOrigin in order to work around some browser bugs. So again, your scenario was very uncommon. 

 

I think I've got a workaround I'll put into the next release for this particular scenario. Let me know if you'd like to get an early copy. 

  • Like 1
Link to comment
Share on other sites

Thanks Jack! I'm pretty sure that I had the same issue in the past, but I thought it is my fault :)

 

Quote

1. Don't pause the timeline initially. There's absolutely no reason to do that here - you play() it on the same tick, so it's useless.

I create several timelines at the start of the application for the same element and those events started based on events/conditions. This is why I must pause the timeline when created. 

Also each timeline can start with a different transform origin, this is why I need to set the transform origin at the start of the timeline.

 

Quote

2. Set immediateRender:false on the fromTo() tween. 

I know which is the first timeline to play, so I need the fromTo to immediateRender: true. In my real world scenario rotationZ might start with 45deg, so it has to get its initial position when I place the fromTo into the timeline. As I know the starting position of the element, maybe I should do an initial set of values to the element at application start, so immediateRender could be false.

 

Quote

3. Put the transformOrigin in the "from" part of the fromTo() (eliminate the set() altogether).

Not a bad idea, will try.

 

Quote

4. Don't embed the set() in the timeline - just do a TweenMax.set() first, before creating the timeline.

My timelines might be repeated, so it would be a lot easier if set could stay on the timeline.

 

 

Based on your detailed explanation, it seems like that the workaround for the transform origin z-offset cached to early for the fromTo tween and does not use what is set in the to. For example the same scenario with x is working fine: 

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

 

 

  • Like 1
Link to comment
Share on other sites

8 hours ago, RolandSoos said:

I create several timelines at the start of the application for the same element and those events started based on events/conditions. This is why I must pause the timeline when created. 

 

You could always just pause() it after you create everything. 

 

8 hours ago, RolandSoos said:

As I know the starting position of the element, maybe I should do an initial set of values to the element at application start, so immediateRender could be false.

 

Yes, exactly. 

 

8 hours ago, RolandSoos said:

My timelines might be repeated, so it would be a lot easier if set could stay on the timeline.

 

Yes, and it would be if you simply put it into the "from" part of your initial fromTo() tween(s). 

Link to comment
Share on other sites

I think I found a bug related to this topic. After you set non-zero value for the transform-origin z-offset, you are not able to reset it back to 0. I made an example which should set the z-offset to 0 every second turn. You can start a turn by clicking anywhere:

See the Pen gZMzwa?editors=0110 by anon (@anon) on CodePen

 

Clearing the transform-origin seems to solve this issue:

TweenLite.set("div",{clearProps:"transformOrigin"});

 

Link to comment
Share on other sites

Thanks Jack, will try it when I have some free time :)

 

I just check the sourcecode diff and there is an encoding issue:

// allows a node process to exit even if the timeout’s callback hasn't been invoked. Without it, the node process could hang as this function is called every two seconds.

 

Link to comment
Share on other sites

Well Jack, here I go again with an another possible bug. :)

 

Working example: I have a single timeline and one transform origin change at position 0 and one at position 2. Also I have a percent variable to be able to write to console log. When the timeline completes, it will be played again from 0.5 position. So it skips the initial transform origin and percent set at position 0, but in real world it renders it properly. I think this is good behavior.

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

 

Two timelines: Each timeline contains a transform origin change. When the first timeline completes, it starts the second timeline from position 0. When the second timeline completes, it starts the first timeline from position 0.5. The problem here that on second turn the first timelines transform origin and percent change is skipped. 

See the Pen PXWWdB?editors=0010 by anon (@anon) on CodePen

Same pen with 2.0.3 beta: 

See the Pen OrWWaB?editors=0010 by anon (@anon) on CodePen

 

 

As a temporary fix I placed a duplicated transform origin and percent set at the end of the first timeline. It solved the issue with transform origin, but the percent value still not updated. 

See the Pen EGZZqZ?editors=0010 by anon (@anon) on CodePen

 

 

 

 

Link to comment
Share on other sites

Hm, I hesitate to call this a bug. GSAP is highly optimized for speed and one of those optimizations is that a tween remembers where its playhead is and it'll only render when that playhead moves to a new position. Otherwise, it's totally wasteful to keep rendering at the same spot. Imagine you've got one hundred 2-second tweens at the start of a timeline and the playhead reaches 3 seconds...it shouldn't go back and tell all 100 tweens to render at their final state again...and again...on every tick. It's smart enough to say "nah, I'm already at my end state...there's no need to do any work here". 

 

In your case, though, you're creating completely separate animations that are operating on the same properties of the same objects. There's no way the other tweens can know that you altered those properties elsewhere. So, for example, tween1 tweens box.x to 100...and then you have tween2 (not even in the same timeline) change box.x to be 50...the next time tween1's timeline renders, it tells tween1 to render at its end state (I'm assuming the playhead as passed it), and it's like "nah, I'm already at my end state...I'll skip it." See the issue? box.x stays at 50. 

 

One "solution" would be to force all tweens to render all the time no matter what...which would be a big performance hit in timelines. And 99.999% of animations out there wouldn't benefit at all, but everyone would pay the performance price. That doesn't sound attractive. GSAP has been in the wild for almost a decade and I can't remember anyone else running into this edge case. And it's used on over 7,000,000 web sites. I gotta believe this isn't a significant issue that's worth such a steep price to "fix", especially because it'd be very easy to resolve it by writing your animation code slightly differently.

 

And to be honest, the way you're writing things is rather convoluted and inefficient. If you just use a define the transformOrigin directly in the to() tween (or use a fromTo()), that'd get you the behavior you're after:

See the Pen 3eca5c7e489dc9c3f5b57daed5c0513f by GreenSock (@GreenSock) on CodePen

 

 Or, if you must, you could force things to render by jumping to the start and then to the spot you want (just to force the playhead to move and trigger a render), like tl.seek(0).play(0.5). See what I mean? 

 

Or did I misunderstand something? 

  • Like 1
Link to comment
Share on other sites

Thanks Jack for the details. Well, I think my use case is very rare. I have to maintain 3 timeline for every element. Incoming -> Loop -> Outgoing timelines. Loop might take forever and events can stop it and jump to the outgoing timeline. Then also based on a condition, the while timelines are repeated. I think I would be able to make the tree timelines into one if I would add labels where incoming, loop and outgoing part comes. The only problem what it would cause that I'm not sure if I would be able to change the loop part from forever to stop after next turn and continue with outgoing.

 

I tried to create something like that, but I'm not sure if it is possible with GSAP: 

See the Pen ZVLJRK?editors=0010 by anon (@anon) on CodePen

 

Outgoing should wait until the loop timeline ended. Loop timeline will stop repeating if you click on the body. Currently the Outgoing will play after the first.

Then I tried to pause the main timeline after the first iteration of loop and resume it when the loop timeline completed, but unfortunately it pauses the loop timeline too: 

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

 

 

Link to comment
Share on other sites

Thanks Mikel, but no. The outgoing part should play when the loop ended. So you click somewhere, notify loop to play until the current repeat ends and then play the outgoing part.

 

Red box: move from x:100 to x:0 -> spins around ..... click happen ->keeps spinning around until it reaches 360 deg -> move to x: 100

 

What it does with the current codepen:

move from x:100 to x:0 -> spins around ..... click happen ->keeps spinning around until it reaches 360 deg
                       -> after first spin -> move to x: 100

 

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