How to prevent repaint of other elements when using CSS transition - css

I am trying to prevent browser repainting to improve performance on a large single page app that makes use of CSS animations.
If I have an element that has a :hover { transform: scale(...) } effect on it, I can prevent repaints by using the will-change: transform and/or transform: translateZ(0) as mentioned here.
But as soon as I add a transition to that element, many elements start being repainted again.
Here is a demo codepen and a gif of it
The only thing that worked to prevent this repaint is to put the other elements before the animating element in the DOM order, or to z-index the animating element above the other elements. But with a complex single-page app, I do not want to have to micromanage the z-index of any element that just wants a hover transition.
Any other ideas or references? I haven't seen this specific problem with transitions discussed elsewhere.

It seemed like the position: absolute; on .other was not creating a new layer, I was able to solve the issue by adding transform: translateZ(0) to the .other class.
JSbin
Given that you're working on a SPA, i'm not sure how feasible this approach is, since there could be hundreds of elements after the circle.

Related

css blur filter's edges become solid during translateY transition

I have an element with a -webkit-filter: blur(10px); applied to it. I'm using a CSS animation to move the element up and down with it's translateY property. When the element is animating or transitioning the blur remains but the edges become hard. When the animation or transition ends the the edges become blurred again like they're suppose to. I made a demo that shows examples of the notes that follow it.
Demo: http://jsbin.com/bofahekuko/1/edit?html,css,output
I Tried Fixing it With:
adding -webkit-font-smoothing: subpixel-antialiased; to the animating element.
forcing hardware acceleration on the animating element and adding backface-visibility: hidden on the parent
Things of Note
Happens with both CSS transitions and animations
If you move the element up and down via the CSS top property the blur filter renders correctly.
Browser Testing
Bug appears in Google Chrome (running Version 50.0.2661.86 (64-bit)) as well as in Canary.
Both Firefox and Safari (iOS and Desktop) correctly render the blur filter during the animation.
I'd really like to be able to run the animation with the translateY transition property instead of the top property. If there really isn't a fix it'd still be interesting to know what exactly is going on here to cause the problem.
Thanks in advance for any help on this.
This is a problem that's probably caused by hardware accceleration - the GPU is just moving the original blurred content without updating its background.
To fix, don't use translate or use another trick to disable hardware acceleration (like simultaneously animating margin or padding)

Avoiding absolute positioned elements from jumping on animations in chrome for mac

I just build this website : http://rcmm.minnie.mico.dk/
And on the front page i have a slideshow (bad UX - I know). If you click one of the small square icons in the bottom right corner the slider will animate.
This works great, except in chrome for mac. Every time the slider animates, all the elements that are positioned absolute will do a tiny pixel-jump. The animations on the slider however is using transform: translate() to do the animation. js is only swapping classes - no actualt animating - thats all handled by css.
Does anyone know why this is, and if there is a possible fix for this?
Any help would be much appreciated.
Simply add -webkit-backface-visibility: hidden; to the <body> and the jumping should stop.
Any time you have minor glitches when using CSS Transforms, backface-visibility: hidden will straighten things out. It typically has to do with how the browser will handle hardware acceleration.
From CSS3 Animations: the Hiccups and Bugs You'll Want to Avoid:
The reason behind this phenomenon is unclear, but the fix is pretty simple. Simply adding the -webkit-backface-visibility: hidden; rule to your CSS should help prevent the problem. Apply it to the container of the element, like so:
.container {
-webkit-backface-visibility: hidden;
}
It boils down to another hardware acceleration problem - using this property simply kicks the acceleration into gear. You could also use things like -webkit-perspective: 1000; or other 3D properties.

iPad Safari scrolling causes HTML elements to disappear and reappear with a delay

I'm currently developing a web application using HTML5 and jQuery for iPad Safari. I'm running into a problem wherein large scroll areas cause the elements that are offscreen to appear after a delay when I scroll down to them.
What I mean by that is, if I have a row of images (or even a div with a gradient) that is offscreen, when I scroll down (or up) to it, the expected behavior is for the element to appear on screen as I am scrolling to it.
However, the element does not appear until I lift my finger off the screen and the scroller finishes all its animations.
This is causing a super noticeable problem for me, making the whole thing look choppy, although it is not. I'm guessing the iPad Safari is trying to do something to save memory. Is there a way in which I can prevent this choppy-ness from happening?
Additionally, what is iPad Safari actually trying to do?
You need to trick the browser to use hardware acceleration more effectively. You can do this with an empty three-dimensional transform:
-webkit-transform: translate3d(0, 0, 0)
Particularly, you'll need this on child elements that have a position:relative; declaration (or, just go all out and do it to all child elements).
It is not a guaranteed fix, but it is fairly successful most of the time.
Hat tip: iOS 5 Native Scrolling–Grins & Gotchas
I was using translate3d before. It produced unwanted results. Basically, it would chop off and not render elements that were offscreen, until I interacted with them. So, basically, in landscape orientation, half of my site that was offscreen was not being shown. This is a iPad web application, owing to which I was in a fix.
Applying translate3d to relatively positioned elements solved the problem for those elements, but other elements stopped rendering, once offscreen. The elements that I couldn't interact with (artwork) would never render again, unless I reloaded the page.
The complete solution:
*:not(html) {
-webkit-transform: translate3d(0, 0, 0);
}
Now, although this might not be the most "efficient" solution, it was the only one that works. Mobile Safari does not render the elements that are offscreen, or sometimes renders erratically, when using -webkit-overflow-scrolling: touch. Unless a translate3d is applied to all other elements that might go offscreen owing to that scroll, those elements will be chopped off after scrolling.
(This is the complete answer to my question. I had originally marked Colin Williams' answer as the correct answer, as it helped me get to the complete solution. A community member, #Slipp D. Thompson edited my question, after about 2.5 years of me having asked it, and told me I was abusing SO's Q & A format. He also told me to separately post this as the answer.
#Colin Williams, thank you! The answer and the article you linked out to gave me a lead to try something with CSS. So, thanks again, and hope this helps some other lost soul. This surely helped me big time!)
Targeting all elements but html: *:not(html)
caused problems on other elements in my case. It modified the stacking context, causing some z-index to break.
We should better try to target the right element and apply -webkit-transform: translate3d(0,0,0) to it only.
Sometimes the translate3D(0,0,0) doesn't work. We can use the following method, targeting the right element:
#keyframes redraw{
0% {opacity: 1;}
100% {opacity: .99;}
}
/* iOS redraw fix */
animation: redraw 1s linear infinite;
When the translate3d doesn't work, try to add perspective. It always works for me
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
perspective: 1000;
-webkit-perspective: 1000;
Increase Your Site’s Performance with Hardware-Accelerated CSS
Adding -webkit-transform: translate3d(0,0,0) to an element statically doesn't work for me.
I apply this property dynamically. For example, when a page is scrolled, I set -webkit-transform: translate3d(0,0,0) on a element. Then after a short delay, I reset this property, that is, -webkit-transform: none
This approach seems to work.
Thank you, Colin Williams for pointing me in the right direction.
I had the same issue with iscroll 4.2.5 on iOS 7. The whole scroll element just disappear.
I've tried to add translate3d(0,0,0) as was suggested here. It has solved the problem, but it disabled the iscroll "snap" effect.
The solution came with giving the "position:relative; z-index:1000;display:block" CSS properties to the whole container that holds the scroll element and there isn't any need to give translate3d to child elements.
I had the same issue using an older version of Fancybox.
Upgrading to v3 will solve your problem or you can just add:
html, body {
-webkit-overflow-scrolling : touch !important;
overflow: auto !important;
height: 100% !important;
}
At time translate3d may not work. In those cases perspective can be used
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
perspective: 1000;
-webkit-perspective: 1000;
I'm pretty darn sure I just solved this with:
overflow-y: auto;
(Presumably just overflow: auto; would work too depending on your needs.)
There are cases where a rotation is applied and/or a Z index is used.
Rotation: An existing declaration of -webkit-transform to rotate an element might not be enough to tackle the appearance problem as well (like -webkit-transform: rotate(-45deg)). In this case you can use -webkit-transform: translateZ(0px) rotateZ(-45deg) as a trick (mind the rotateZ).
Z index: Together with the rotation you might define a positive z-index property, like z-index: 42. The above steps described under "Rotation" were in my case enough to resolve the issue, even with the empty translateZ(0px). I suspect though that the Z index in this case may have caused the disappearing and reappearing in the first place. In any case the z-index: 42 property needs to be kept -- -webkit-transform: translateZ(42px) only is not enough.
This is a very common problem faced by developers and that is mainly due to Safari's property of not recreating elements defined as position : fixed.
So either change the position property or some hack needs to be applied as mentioned in other answers.
Issues with position fixed & scrolling on iOS
Change to position fixed on iOS Safari while scrolling
In my case (an iOS PhoneGap app), applying translate3d to relative child elements did not resolve the issue. My scrollable element didn't have a set height as it was absolutely positioned and I was defining the top and bottom positions.
Adding a min-height (of 100 pixels) fixed it for me.
I faced this problem in a Framework7 and Cordova project. I tried all the solutions above. They did not solve my problem.
In my case, I was using more than 10 CSS animations on the same page with infinite rotation (transform). I had to remove the animations. It is OK now with the lack of some visual features.
If the solutions in other answers do not help you, you may start eliminating some animations.
The -webkit-transform: translate3d(0, 0, 0); trick didn't work for me. In my case I had set a parent to:
/* Parent */
height: 100vh;
Changing that to
height: auto;
min-height: 100vh;
solved the issue in case someone else is facing the
same situation.
In my case, CSS did not fix the issue. I noticed the problem while using jQuery re-render a button.
$("#myButton").html("text")
Try this:
$("#myButton").html("<span>text</span>")

Considerations for CSS3 Transition Performance

As part of a project that needs to support mobile devices, I have been working on mimicking the iPhone toggle control using CSS3. I have the look and feel of the element pretty much there, and am using CSS3 transitions to animate its state change.
When I have the element itself on a page with nothing else, the transition is relatively smooth on iOS. However, when I combine it with other CSS elements on a page, the result in iOS is laggy as anything. It's slightly better than a raw jQuery animation, but not much.
I've set up two test pages to demonstrate what I mean (the difference is hardly noticeable in a regular browser):
Toggle Control on its own > http://ben-major.co.uk/labs/iPhone%20UI/ios_toggle.html
Combined with other elements > http://ben-major.co.uk/labs/iPhone%20UI/
I am looking for any advice on speeding up the transition in mobile devices. What could be the factors that are slowing down its performance on the full page test?
Any advice and comments welcome.
You have to be careful with this, as it can alter the z-index of the element it's applied to, but adding:
-webkit-transform-style: preserve-3d;
To the element you're applying the transition to, can speed animation up considerably, as it forces the hardware to use hardware acceleration for the animation.
If you do encounter layout bugs, you can just switch your 2d transitions to 3d values, so:
-webkit-transform: translate(100px, 100px)
becomes:
-webkit-transform: translate3d(100px, 100px, 0px)
You can see a demo of how this helps speed things up, at http://stickmanventures.com/labs/demo/spinning-gears-Chrome-preserve-3d/#
If after applying this to the element, you see it or elements around it blink upon use, then use:
-webkit-backface-visibility: hidden;
To the element, and that should correct the problem.
These tips have helped me to produce fast, efficient CSS transitions, hope they help. :)
Chrome has recently improved the 2D transition performance, and now this trick is no longer needed. The best thing is that if removed the translate3d you'll no longer have those z-index problems! Use the test to prove. http://stickmanventures.com/labs/demo/spinning-gears-Chrome-preserve-3d/
also you can try will-change: transform; , read more about it here:
https://developer.mozilla.org/en-US/docs/Web/CSS/will-change#Browser_compatibility
I think it quite old already but for anyone who still needs tricks to improve the transition performance on mobile device, you can apply :
-webkit-transform: translateZ(0);
to the element you are animating.
This trick is according to this blog : http://chrissilich.com/blog/fix-css-animation-slow-or-choppy-in-mobile-browsers/.
I have tried and it works quite well.

Prevent flicker on webkit-transition of webkit-transform [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
iphone webkit css animations cause flicker
For some reason, right before my animation of the webkit-transform property occurs, there is a slight flicker. Here is what I am doing:
CSS:
#element {
-webkit-transition: -webkit-transform 500ms;
}
JavaScript:
$("#element").css("-webkit-transform", "translateX(" + value + "px)");
Right before the transition takes place, there is a flicker. Any idea why this is, and how I could fix the problem?
Thanks!
Update: this only occurs in Safari. It does not happen in Chrome, although the animation does work.
The solution is mentioned here: iPhone WebKit CSS animations cause flicker.
For your element, you need to set
-webkit-backface-visibility: hidden;
The rule:
-webkit-backface-visibility: hidden;
will not work for sprites or image backgrounds.
body {-webkit-transform:translate3d(0,0,0);}
screws up backgrounds that are tiled.
I prefer to make a class called no-flick and do this:
.no-flick{-webkit-transform:translate3d(0,0,0);}
Add this css property to the element being flickered:
-webkit-transform-style: preserve-3d;
(And a big thanks to Nathan Hoad: http://nathanhoad.net/how-to-stop-css-animation-flicker-in-webkit)
For a more detailed explanation, check out this post:
http://www.viget.com/inspire/webkit-transform-kill-the-flash/
I would definitely avoid applying it to the entire body. The key is to make sure whatever specific element you plan on transforming in the future starts out rendered in 3d so the browsers doesn't have to switch in and out of rendering modes. Adding
-webkit-transform: translateZ(0)
(or either of the options already mentioned) to the animated element will accomplish this.
I had to use:
-webkit-perspective: 1000;
-webkit-backface-visibility: hidden;
on the element, or I would still get a flickr the first time a transition occurred after page load
I found that applying the -webkit-backface-visibility: hidden; to the translating element and -webkit-transform: translate3d(0,0,0); to all its children, the flicker then disappears
Trigger hardware accelerated rendering for the problematic element. I would advice to not do this on *, body or html tags for performance.
.problem{
-webkit-transform:translate3d(0,0,0);
}
Both of the above two answers work for me with a similar problem.
However, the body {-webkit-transform} approach causes all elements on the page to effectively be rendered in 3D. This isn't the worst thing, but it slightly changes the rendering of text and other CSS-styled elements.
It may be an effect you want. It may be useful if you're doing a lot of transform on your page. Otherwise, -webkit-backface-visibility:hidden on the element your transforming is the least invasive option.

Resources