Jump to content
Search Community

Losing original transform css on animation

annam 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

We noticed several cases where GSAP resets the transform CSS property of an item when animating, even if we are animating a non-transform css property (eg opacity). We've also had this issue when initializing Draggable on an item. 

 

this usually occurs if an item is hidden when animating/initialized, and in the case of animations it seems to be caused if we have the force3D option to true/auto.

 

Try this JSFiddle: https://jsfiddle.net/annam/snLa4w5v/2/

 

First "show" and then "hide" again to see the initial position, then "gsap animate" and then "show" again to see how the position is reset. 

 

 

I know that the workaround is to set the transform using tweenlite.set(), however I think this should not be a requirement, considering that the original transform may be set in CSS styles and not through javascript. Even through javascript (which is what we use in our case), it's much more expensive to set the transform using gsap than through plain jquery, as you can see in this jsfiddle:

 

https://jsfiddle.net/annam/cL4bpwuy/1/

 

Open the console to see the difference between the time taken for gsap to set the transform to 1000 elements, as opposed to regular jquery. on average, it takes 10ms for jquery and about 1200ms for greensock! for us this is an unacceptable performance hit which is why we tried switching to regular jquery .css(), but came across the issue above where if you then try to initialise draggable or animate a hidden item, the position is lost. 

 

As I said above I strongly believe that this is something that GSAP should address. The issue seems to be that GSAP can't get the correct position if the item is hidden, so the extra effort could be limited to getting the correct position only in these cases, and would not affect the overall performance of the engine.

Link to comment
Share on other sites

I see what you mean, and this is a bit of a tricky issue. Let me explain...

 

This is a browser issue, not a GSAP issue. Try running this code when you've got that box hidden (or just put it above the TweenLite.set() line):

console.log(document.defaultView.getComputedStyle(document.getElementById("box")).transform); //"none"

So even though you've got a transform applied in your CSS, the browser is saying that the transform is "none" (wrong). Weird, I know. 

 

To get around this, we could run some logic every time anything gets a transform applied and see if its "display" is set to "none" and also make sure it's in the DOM (if not, add it temporarily so that the getComputedStyle() returns a valid value)...BUT...(and this is a big but)...that's expensive performance-wise. As you probably know, we put a HUGE emphasis on performance in GSAP, so this is a pretty painful proposition especially because:

  1. This is a very rare edge case (most people don't apply transforms to elements that are display:none)
  2. There are other solutions that allow performance to be maximized. Instead of display:none you could use visibility:hidden.
  3. If you actually set display:"none" (or any display value) in your actual tween, GSAP will recognize that and make sure that it's "block" when it reads the transform values in order to work around this browser issue. So, in your case, just add display:"none" to that tween and you're golden. Again this keeps us from having to run that logic for EVERY transform tween for everyone else (most of whom never need it because they don't try setting/animating transform-related properties on elements with display:none). 

Does that help? Do you have any other suggestions? Again, I totally understand why you'd think this is an issue with GSAP (or at least why you'd hope that GSAP would "fix" it if it's a browser issue), but hopefully it's more clear now why it's not as simple as slapping in some extra logic to verify display values and force it into the DOM if it's not there.

 

Oh, and as for the performance of TweenLite.set() compared to jQuery.css(), there's a very good reason why set() takes longer - a TweenLite.set() is actually just a zero-duration tween, meaning you could even drop it into a timeline and scrub back and forth and see the values toggle at that spot. Every tween must record starting and ending values so that it can work its magic. A jQuery.css() call is much more "dumb" in the sense that it merely sets the values and doesn't care about anything else. It doesn't need to record starting/ending values or spit back an object that you can check the progress() of or startTime() or anything else like that. I'm sure you've seen that GSAP blows jQuery out of the water when it comes to animation speed (literally like 20x faster), so clearly we're performance-minded but the fundamental nature of a TweenLite.set() is totally different (and more inherently complex) than jQuery.css(). Remember, GSAP is an animation library, not a generalized DOM utility, so it's highly optimized for animation tasks. :)

  • Like 4
Link to comment
Share on other sites

To add to Jacks great advice!

 

When you use display:none it removes the element from the DOM rendering tree. It would be better like Jack advised to just comment out the display:none; property in your #box CSS rule. And add the CSS property visibility:hidden;

#box {
  position: absolute;
  width: 100px; height: 100px;
  background: #ccc;
  transform: translate(100px, 100px);
  /*display: none;*/
  visibility:hidden;
}

Then switch your GSAP to() tween to use autoAlpha instead of opacity. This way when the tween animates, it will interpolate the opacity value and automatically set visibility to it's appropriate value. Plus using autoAlpha helps performance.

// so change this using opacity:
TweenLite.to('#box', 0.1, { opacity: 0, force3D: true })

// to this using autoAlpha
TweenLite.to('#box', 0.1, { autoAlpha: 0, force3D: true })

x
autoAlpha is part of the GSAP CSSPlugin

 

http://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/

  • autoAlpha
    Identical to opacity except that when the value hits 0 the visibility property will be set to "hidden" in order to improve browser rendering performance and prevent clicks/interactivity on the target. When the value is anything other than 0, visibility will be set to "inherit". It is not set to "visible" in order to honor inheritance (imagine the parent element is hidden - setting the child to visible explicitly would cause it to appear when that's probably not what was intended). And for convenience, if the element's visibility is initially set to "hidden" and opacity is 1, it will assume opacity should also start at 0. This makes it simple to start things out on your page as invisible (set your css visibility:hidden) and then fade them in whenever you want.

How to use autoAlpha:

//fade out and set visibility:hidden
TweenLite.to(element, 2, {autoAlpha:0});

//in 2 seconds, fade back in with visibility:visible
TweenLite.to(element, 2, {autoAlpha:1, delay:2});

x
Also if you can help it you should use the GSAP set() method instead of jQuery css() method like Jack advised. This way your not trying to change CSS properties with both jQuery and GSAP. GSAP is better since it will help make your CSS cross browser by adding vendor prefixes if needed. Unlike the jQuery css() method that does not add vendor prefixes and just fails silently. This way you don't have both jQuery and GSAP fighting to apply CSS properties on the same element. You want to be careful of changing CSS properties outside of GSAP. Allow GSAP to keep track and record your CSS properties and values.

 

But again your better off using visibility: hidden and the GSAP autoAlpha special property when animating show hide states.

 

The CSS visibility:hidden in your stylesheet prevents your element form showing on page load. And allows it to be animated using the autoAlpha property for smooth opacity animations. :)

 

Resource:

GSAP CSSPlugin: http://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/

 

:)

  • Like 4
Link to comment
Share on other sites

Hello guys, 

 

as always, thanks for the very fast reply!

 

I understand that you've always been performance-oriented and that's definitely a requirement for an animation engine! However it's not the first time that we've had to add workarounds in our own code just to overcome issues that aren't handled in GSAP for performance considerations.

 

The solution to this issue is to add lines in our code checking whether the item is actually displayed or not, on every case where we call a tween, to determine whether we need to add the display: none property in the tween.  You can see how this is not at all efficient on our end as well. I respect how you keep your engine performing well by avoiding such code but in this case I don't see how a simple "isvisible" check would really affect the performance, which sounds like it's all that would be required to do on all elements and then handle it on the very rare, as you said, edge cases where this happens.

 

We'll look into the visibility solution as well however this would be a fundamental change to our code with possible issues arising elsewhere.

 

Also thanks to Jonathan for the extra explanation on how opacity/visibility works in GSAP, autoAlpha actually sounds very useful, but it wouldn't be much help in our case, as this issue occurs whenever animation any property, even eg backgroundColor.

Link to comment
Share on other sites

Hm, you mentioned a "simple isvisible" check - can you elaborate? 

 

Also, please do let us know about any time you need to implement extra code to work around something in GSAP. We work very hard to make your life easier as a developer, not cause headaches. Of course it's always a balancing act between convenience and performance, so sometimes we must avoid a convenience if it's too costly performance-wise but that's relatively rare. Again, my point is that I want to hear from you about any of those issues you're running into (or have historically). 

  • Like 3
Link to comment
Share on other sites

Hi Jack,

 

thanks for your concern!

 

this is a recent issue that I can recall that we had to work around it in our own code, however I get that this is a much more complex case.. http://greensock.com/forums/topic/13449-seek-causes-timeline-to-jump-on-transition-when-two-similar-tweens-are-found/#entry56122

 

What I meant with my post above is that, if you were to check for this issue, all you'd need to check on all elements you are animating (which would affect performance) would be if they are display: none or not. if not, then you'd need to perform no further actions.. so the only overhead would be an "is visible" check. in the cases where this does happen of course you would need more code of course, but that's to fix an issue where the tween wouldn't be played correctly if it wasn't there..

 

Anyway, that's my idea of the fix by your description above, and with very limited knowledge of your code.. I'm sure things are actually more complex :)

 

The difficulty with applying the suggested fix for us is that, as this could happen on any property transition, we need to add the code for possibly adding the "display: none" in the tween in many, many places. 

 

Thanks!

Anna

Link to comment
Share on other sites

Hello annam,

 

One thing to note if you use the autoAlpha way and not using display:none, but using visibility:hidden in your style-sheet. Since your using jQuery already you can take advantage of using :visible which is a special CSS pseudo-class used by jQuery.

 

Keep in mind that :visible only work on elements that use the CSS property visibility and opacity using a jQuery Selector collection wrapper $()

 

:visible : https://api.jquery.com/visible-selector/

 

Another way is if you want to still use display:none you can try and use the CSS pseudo-class :hidden

 

:hidden : https://api.jquery.com/hidden-selector/

 

Then when you target your elements you can check if it is :hidden with the appropriate CSS pseudo-class

 

For example if you want to target an element that has display:none you could use :hidden pseudo-class

<div class="box" style="display:none;">My Text</div>

and check like this:

// find elements with .box class that are hidden without an if conditional
// jQuery silently fails if the selector does not find a match
$(".box:hidden")

// when used in a if condition
if($(".box:hidden").length > 0) {
     // element is hidden
} else {
     // element is not hidden
}

// or checking if is():hidden
if($(".box").is(":hidden")) {
     // element is hidden
} else {
     // element is not hidden
}

Elements can be considered hidden for several reasons:

  • They have a CSS display value of none.
  • They are form elements with type="hidden".
  • Their width and height are explicitly set to 0.
  • An ancestor element is hidden, so the element is not shown on the page.

This way you just include the :hidden state within your selector, so you don't have to do an if check for comparing the elements style.display value is none.

 

Resources:

jQuery :visible : https://api.jquery.com/visible-selector/

jQuery :hidden : https://api.jquery.com/hidden-selector/

 

:)

  • Like 3
Link to comment
Share on other sites

By the way, I did figure out a way to just check for "display:none" in a relatively cheap way and work around the browser bug/annoyance internally...

 

How cheap? I know it nice to have GSAP work around certain browser issues, but this seems to be more of a development issue. 

 

I really don't see how this is a GSAP issue if it's the correct behavior. As Jonathan pointed out, using display:none removes the element from the rendering tree, so transforms will not be calculated. And this type of behavior is not unique to browsers. Most libraries that use a scene graph do not update transforms if the object is not going to be displayed.

 

So by using display:none, there are no transforms on the element, even if it's in the CSS. If you use force3D without setting any transforms, you'll get exactly what you asked for, which would be the identity matrix. That's essentially the same thing as doing this.  

$("#box").css("transform", "translateZ(0)");

I don't think anyone would say that jQuery is doing wrong. The solution to this issue seems pretty easy to me. Set your transforms with GSAP and use force3D when it's appropriate.

 

Just to touch on what Jack said about the performance difference between using jQuery.css() and TweenLite.set(), you're actually looking at those numbers incorrectly. If you're going to animate those elements, you would need to add the jQuery time to the TweenLite time to calculate the cost of using jQuery since you're basically going to end up doing the same thing twice.

  • Like 2
Link to comment
Share on other sites

Hi Jack, 

 

thanks for looking into the fix!

 

It actually didn't work for us. We toggle visibility by toggling a class that contains the "display: none" declaration, but when your fix runs it adds an inline "display: none" declaration on the DOM element and the items never get displayed when we remove the "hidden" class. 

 

Even if we could work around this, what's alarming is that even if we manually disable the inline "display: none" declaration, the items are incorrectly positioned, as before. 

 

Thanks again for looking into this!

Anna

Link to comment
Share on other sites

Sorry about that - I needed to tweak one block of code. It should be resolved in the preview now. https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/TweenMax-latest-beta.js Better?

 

Blake, if you notice any performance issues, please let me know. Yes, there is a small cost to doing the "display" check on elements when their transforms are being animated but it shouldn't be too bad. It skips the check if it senses that any transforms are applied (so the check only happens when it's "none" or an identity matrix, although granted that's the most common case initially). 

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