iOS6 html hardware acceleration changes and how to fix them

As probably many developers have already experience this over the past weeks:
I had a phonegap application for iOS that contained some visually animated components  (a coverflow or a carousel for example). But after updating to iOS6 this is all flickring now and it feels horrible because it’s just stumbling and faltering on the new version of the OS.
There are several reasons for this and I cannot say which were already there in iOS5, however here are the two main reasons and instructions on how to fix that:

1. Not all CSS Properties trigger hardware acceleration any more

Previously there were quite a few properties that triggered a hardware acceleration, for example:

  • -webik-transform: translate3d(x,y,z);
  • -webkit-transform: preserve-3d;

As Apple states in its Developer Changelog for iOS6: ‘WebKit no longer always creates hardware-accelerated layers for elements with the -webkit-transform: preserve-3d option.[…]‘ There is a need for new CSS properties to trigger the acceleration, unfortunately I’m not aware of any official document by Apple listing those properties.

How to fix it:
Here are three properties that have been proven to work (at least in combination, I have not yet tested them solo, but they should):

  • -webkit-transform: translateZ(0);
  • -webkit-perspective: 1000;
  • -webkit-backface-visibility: hidden;

As this is going to trigger hardware acceleration for the specified elements this might still not completely fix all the issues, therefore please continue reading Nr.2.


2. Overlapping with other Elements

Since I cannot downgrade to iOS5 I cannot say for sure if this “issue” already persisted, however it heavily persists with iOS6:

If you have an element(Element A) that is hardware accelerated and animated, it will/might cause performance and visual issues when overlapping with other elements(Element B) that are not hardware accelerated, as Element A will trigger Element B to be re-rendered with every change of Element A(at the end of a CSS-transition or when updating CSS properties through JS), which will cause:

  • flickering of Element B, since it renders a lot slower
  • heavy slowdown of the animation, since it needs more CPU time to render leaving less time for JS execution

One thing I noticed: If Element A OVERlaps another element it is not slowing down the animation as heavy as if Element A was UNDERlapping another element. Also it does not really matter if the parent-container of Element A has ‘overflow: hidden;’ or ‘overflow: visible;’ – it still slows doen the execution if the overlapping occures outside the bounds of the parent container.

How to fix it:
 a) This can be simply fixed by giving ALL overlapping elements the CSS properties that trigger hardware accerleration. (see 1.)
(My first approach to fix this was by alternating the DOM-order to only have OVERlapping elements, however fix a) is way easier and works way better – for me.)

I have visualized this here:

 

edit1: 16-10-2012


3. Avoid changes between ‘display: none;’ and ‘display: something;’

Changing the display-state on iOS is like adding or removing an element to and from the DOM and is therefor a very heady calculation depending on how many items you have in your DOM that can easyly cause the UI to freeze for a second or more.

How to fix it:
Instead of using the display-property, use the tranform-property instead, if you want to hide an item you can use something like this: transform:

and if you want to show the element, just set the values to 0:

You have to set the ‘position: absolute;’ though. This method needs a little bit more work than using ‘display’, however the performance of your webapp will thank you greatly for that.

 

I hope this helps someone as I was struggeling with this issue for quite a while, thoughts or additions on this article are very welcome!

  • http://jarv.us Chris Alfano

    Does this mean an element’s container must be accelerated if it has a background ElementA is blending with?

    This is no fun, I thought the whole point of CSS was abstracting these complexities from us. Now we have two different environments with two different behaviors on iOS alone where we have to coordinate the hardware with CSS hackery. Good job Apple.

    • olsn

      I believe that if it works on iOS6, it will work on iOS5 as well, as I see it, they just “tightened” the use of hardware acceleration – howerver, as stated in the article – I currently have no iOS5 device to test this on, this is just an assumption.

      About your question: There is not “must” for hardware acceleration, however if you have an accelerated element which is animated you should also turn on hardware acceleration on all overlapping elements if you experience a drop in framerate and in visually unappealing flickering of elements.

  • http://www.zachstronaut.com Zachary Johnson

    Thanks for this! I’ll have to give it a try. I’ve certainly had to fix flickering with translateZ(0) many times on mobile.

    So… using translate3D(x, y, 0) for *2D* movement used to be critical for performance on iOS 4… however, it seems like this really wasn’t so much the case anymore with iOS 5. It seems like one could get good results with left/top again. Can you confirm this from your experience?

    There used to be a huge performance gap between 2D movement with 3D translation on the desktop, too. Moving things with left/top used to be very low frame rate in Safari and Chrome compared to translate3D(x, y, 0). About 6 – 9 months ago however, updates to those browsers brought tremendous improvements to DOM positioning. In fact, I have had better (and much more consistent) results using old fashioned absolute positioning for 2D animations on the desktop after this switch. Using translate3D(x, y, 0) seems to just bring with it a whole host of bugs with too little benefit after these new optimizations.

    Obviously when you are doing 3D animations using the Z axis then you don’t have a choice but to use transforms. And, it seems extremely important to not mix top/left DOM animation with transform/translation animation. Use all of one or the other for the best results.

    • olsn

      That’s true, mixing tranlate() and top/left will result in a horrible performance, but not only on mobile – however translate() performed allways better for me on mobile than top/left (i never really did any ‘true’ 3D-transformations) – it is possible that there are cases where top/left might outperform a translate()-tranformation on a PC

  • nitin

    Very useful information. Thanks.
    There is one more thing that seems to be broken in iOS6 with xcode 4.5 in the UIWebView. If you try to scroll a page using javscript scrollTo in a timeout, the UIWebview makes the document go away showing its background for a split second before scrolling the document/window. This results in a very annoying flicker. Even enabling hardware acceleration using CSS3 properties as described by you was of no help. Here’s a fiddle, http://jsfiddle.net/nitinbizsingh/G7wNV/ that shows the code causing the issue. Try it in a UIWebView in iOS6 with xcode 4.5 and it will flicker.Have you guys faced this issue? Any suggestions?

    • olsn

      I’m just guessing this, but with scrollTop() probably the whole view is rerendered and not displayed when it is done rendering, but already before (when there is nothing to see yet).
      I tried this: http://jsfiddle.net/b2xWg/1/ (i know it’s ugly, but at least the background was not there to see – maybe you can try a scroll-animation and see if that works)

  • http://byjoeybaker.com Joey Baker

    For what it’s worth, I’ve noticed the same thing on desktop webkit as well.

    • olsn

      What specs does your desktop computer have?
      And what webkit-version/browser?

      • http://byjoeybaker.com Joey Baker

        Tested on a variety of machines. None of them are low-end.

        • iMacs from 2011-2012 x3
        • Macbook Pro 2010 (2.66 Ghz, 8GB RAM, nVida 330m)

        Tested in Chrome 22, Chrome 24.

        I didn’t test in FF or IE, but conceivably could.

        • olsn

          Did you do some benchmarking or was it visible with the naked eye? I used Chrome on PC, but I did not notice ANY drop in performance there.
          Notebook (no name), 2.0Ghz Dual Core, 2GB RAM, onBoard GPU with 256BM.
          Chrome 22

          in my case this was the application that started acting up on iOS6

          • http://byjoeybaker.com Joey Baker

            No, no benchmarking, it was visible to the naked eye. Overlayed elements would flicker for several seconds before deciding they were actually supposed to show. Used a fix similar to what you described above and it instantly fixed it.

  • Teresa

    I just thought I’d leave a note to let others know that this is NOT the solution when dealing with SVG objects. We have had Highcharts JS in our website for several years, but the display was dramatically off in iOS 6. What it boiled down to was that any manipulations that were defined in the “transform” attribute of an SVG element (which is how Highcharts draws the chart graphics in most browsers) were being ignored on render in the new Webkit.

    What worked for us was removing all the webkit-transform rules for all the elements on the page. It didn’t seem to matter whether the CSS transform rule had anything to do with the SVG transform instruction; any webkit-transform rule applied to an element with the transform attribute caused iOS 6 Webkit to ignore those transform attribute instructions. For example, if you have a rule like “#id * { -webkit-transform: translateZ(0); }” then iOS 6 ignores rotate instruction in “<text transform=”rotate( x y z)”>Text</text>”

    I believe SVG objects are hardware accelerated now by default. Previously, you did need to add the webkit transform rules for hardware acceleration.

    Hopefully, this information will help others having the same problems.

    • olsn

      Hey, I was looking into this issue, this is not limited to iOS6, and I’m not too sure if this is an “issue” or more an override of an attribute through css:
      First I don’t think it is a very good practice to use the asterisk-selector for something like “transform: translate…;” What I just found out is, that if you use the “-webkit-translate:” property on a SVG-object such as <rect> then you have to at least initialize in CSS all transformations-types that you want to set through the transform-attribute, if you want to rotate the object, you would have to do something like: ‘-webkit-transform: translate…() rotate(1);’
      then rotation will work on the svg-object. I have created a little JSFiddle for that, if you want to check out an example: http://jsfiddle.net/ydU4H/

    • Ruchi

      Hi,

      I am having the same issues with highcharts. The charts get rendered properly at times and do not get rendered at other times. It used to work fine with iOS5 but now its causing a lot of issues with iOS6.
      What would be a solution to this?

      • olsn

        As far as I know Highcharts is using SVG to render its’ charts and from what you describe, your issue is not about the performance, so from my point of view you cannot exactly relate the issues from this article to your highchart-issues on iOS6, have you tried contacting highcharts or taking a look into their community board?

        • Ruchi

          Hi,
          No I havnt done that as yet. Would probably try that now. Thanks:)

  • http://iamsimon.se Simon

    I can confirm this works on IOS6, but there seems to be a problem on IOS5.

    The perspective property seems to slow down IOS5 alot, but is critical to get IOS6 into GPU mode.
    Why could adding the perspective attribute be any different to IOS5 then when i had only transform:Translate3d (0,0,0)…?

    So short:
    IOS5: translate3D works great to put it in GPU mode, adding perspective however slows it down alot (but the it is still in GPU mode though)
    IOS6: Transform + perspective works great.

    • olsn

      In your case you can also try ‘transform: translate(x,y) translateZ(0);’ this could work on both ios5 & 6.

  • Pingback: Revision 90: .mobi-Gate, Persona und TypeScript | Working Draft

  • http://m.zoho.com Anand

    I have faced the same issue.2 elements, A overlapping over B.when i remove element B,the app works fine.but if B is present it results in bad scrolling and I have done all the means to hardware accelerate B.but it didn’ work.

    • olsn

      do you have it online somewhere or on jsfiddle so I could take a look at it?

      cheers
      olsn

      • http://m.zoho.com Anand

        Structure:
        element B
        element A.

        I want to hardware accelerate element B

  • http://m.zoho.com Anand

    sorry i don’t have in online.
    I will just explain.element A overlaps over element B.element B is positioned absolute.how to hardware accelerate element B

    • olsn

      Element B will we hardware accelerated once you add one or all of the properties mentions in the article to it’s CSS style.
      However there might be some properties that you set, that may reverse this effect, that’s why I was asking for an version online, it’s hard to just say “do this, it will work” there is some kind of individual solution to almost any case.
      For example do use the “top” or “left” selector for any element? If so: remove those, those will cause a slow down…just as one example.

  • http://parrfolio.com ryan

    To fix the flickering issued I simply added overflow hidden to the top stack. The element with the highest z-index.

    • olsn

      Thanks for the feedback, that sounds very interesting! Do you have an example online somewhere? Because right now I’m not exactly sure how your setup looks and what elements do what.

  • http://truckingtruth.com Brett

    Ya know, I’m setting up a mobile version of my site and tried the settings you mentioned to various elements so they would always be hardware accelerated:

    -webkit-transform: translateZ(0);
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;

    But WOW, what a nightmare! The problem I encountered was with page zooming. If you simply scrolled and worked the menus and such – no problem. But try to zoom the page and it would either lock up for several seconds (up to 15 or 20 seconds sometimes) or crash the browser completely.

    At first I only did it with the menus so I could hide them initially and translate() them back into the page. Everything worked fine most of the time, except for zooming. Zooming would work once in a while, but inevitable it would lock up. When I finally discovered that this might be causing the problem, I eliminated that markup completely and the problem completely went away. Then I tested by adding that markup to various other divs to make sure it wasn’t the layering issue you mentioned, but the more elements I added that markup to, the worse the problem got. When I added that setting to all divs on the page, the browser crashed immediately every single time if you tried to zoom the page.

    So I eliminated that markup completely but I’m using transform() to move the menus into the screen. The initial setting just puts them left: -9999 and I transform() them in. Works great. No zooming issues, no problems.

    And interestingly enough, I tested this on iOS5 and iOS6 – the problem existed on both systems but was significantly worse on iOS5.

    That’s all I got :-)

    • olsn

      Hi,

      the zooming-issue is most likely caused by the ‘-webkit-perspective: 1000;’, you don’t have to use all 3 propeties to get hardware accelleration, the ‘-webkit-transform: translateZ(0);’ alone should to the job.

      When you use transform() to move in the menus, make sure you use it like: ‘-webkit-transform: translate(50px,50px)translateZ(0);’ and not only ‘-webkit-transform: translate(50px,50px);’

      Also try not to mix “top/left” with “translate()” this can cause some performance issues as well(in some cases really bad ones)

      Thanks for your feedback!
      cheers
      olsn

      • http://truckingtruth.com Brett

        Thanks! I really appreciate the help. I’m gonna give your suggestions a shot.

  • http://www.mobisle.org Pietro

    Regarding this point ” Avoid changes between ‘display: none;’ and ‘display: something;’ ” > are you saying that iOS handles the display property in a different way? That would have a great impact imo. For example, also the Jquery toggle() function wouldn’t be the best solution, for performance at least.

    I was wondering how did you find this out and if there anything in the Apple Doc about it.

    Thanks!

    • olsn

      Hi,

      A change between ‘display: none;’ and ‘display: something;’ is ALLWAYS (at least for webkit) combined with loading the element into the memory and rendering it, this is NOT specific to iOS – but desktop computer running chrome or safari are much more capable of handling those changes, so you won’t really have to worry about this on a desktop computer for the reason of having a better hardware!

      You can see this on a simple example: Bring up your webkit inspector(F12), choose any element and give it a style: ‘display: none;’ -> the following dom elements will be repositioned -> webkit touches more than just one element -> requires more resources to be rerendered
      I’m not saying that using a transition with extreme values to show or hide elements is very ‘pretty’ – but it is faster on mobile devices.

      You probably won’t find any documentation about this being “the right way to do it” – this is more of a ninja/pirate style, however with some projects I care more about performance than ‘clean’ or ‘correct’ code.

      • nathan

        Hi olsn,

        is this the same with the visibility and/or opacity properties?

        • olsn

          No, with any visibility and any opacity value the element will still be “rendered” … but with a different alpha.
          You can test this by making a div and add some other divs into it, then set the first child-div to ‘visibility: hidden;’ -> the element will be unvisible but all positions will remain as they are, if you set the child-div to ‘display: none;’ however, it is like removing the div, all positions of the following child-divs will change, possibly also the height of the parent and so on…

          • http://kiinoko.be Yoannis

            Does that mean that using the following code to hide an element (both visually and to screen-readers) also fixes point 3. (not changing ‘display’) ? Or have you experienced some other performance issues with ‘visibility:hidden’ (or any of the properties used below) too ?

            .hidden {
            visibility: hidden;
            height: 0;
            overflow: hidden;
            }

            .visible {
            visibility: visible;
            height: auto;
            overflow: visible; /* optional */
            }

          • olsn

            I cannot confirm that at the moment, but it might work – let me know when tested it (your fix would be without hardware acceleration, so if that works, great!). However, display-changes are only ‘hard’ on the performance if you have many elements in your DOM. If you don’t have as many elements, you won’t need to resort to ugly fixes like that one. ;-)

  • Pingback: Mobile Apps - Nativ vs. Hybrid oder: Wer billig kauft, kauft zwei mal

  • Pingback: CSS3 Transition 的背后 | CSS3, CSS3 animation, css3 transform, css3 transition, javascript, jquery, 教程, 翻译 HTML5 CSS3 jQuery 教程 资源 效果 设计 网页

  • Pingback: All you need to know about CSS Transitions

  • Pingback: スムーズなアニメーションを実装するコツ。CPUとGPUを理解しハードウェアアクセラレーションを駆使するのだ! | Ginpen.com

  • Pingback: jQuery.pep.js – Kinetic drag for mobile & desktop | Js Plugins

  • Pingback: jQuery.pep.js – Kinetic drag for mobile & desktop | Html Use