Optimizing Animation Rendering

About two months ago I was working on an enterprise level front-end application that ran on AngularJS. Luckily the project was very forward-thinking and modern, especially given the large corporation behind it. What that meant practially was that we were using cutting edge technologies and design patterns; ie SCSS, heavy use of complex pure CSS elements (as opposed to images), and plenty of CSS animations. That made the project a lot of fun to work on, but it also presented a challenge at every step because this was an enterprise application meaning that each screen needed to present a large amount of data alongside multiple levels of navigation and several actionable items. At times grappling with these two opposing requirments meant coming up with creative solutiosn with both markup and styling to accomplish something. For instance there were several screens which needed massive scrollable tables to present data. However, some of these tables needed sections and groups within the overall dataset. This eventually called for multiple sibling tables encapsulted within a scrollable container. Bear in mind that this was just one complex UI element of several that concurrently populated the screen. Add to that the aniamtions and complex styling that was being used and you get a picture of how much the browser was dealing with on any given screen.

Around two weeks after starting on the project a strange UI bug appeared only in Chrome. On certain screens with tables we would have icons that indicated an object's status or state. One of these icons was animated using a simple CSS animation and whenever it was enabled something strange would happen to the borders of table rows. The previously thin solid grey borders would become a mixture of multiple colors (black, red, green, yellow, etc), jagged, and slightly larger than they should have been. After some debugging we determined that it was a rendering bug in the version of Angular we were using. An update to the latest version of Angular seemed to fix the problem.

Fast forward about a month and a new defect was created in the project tracker for this bug. When I investigated I found the problem had returned seemingly at random. This time I was determined to find the issue and fix it. Unfortunately I couldn't find anything online that matched the problem we were experiencing so I had to do it the old fashioned way. I first started back at my previous conclusion which was that it was a rendering bug. There were two things pointing this direction: first off it only happened when an element on the page was being animated and second the strange lines/colors looked very simlar to screen tearing that you might see in a video game or during GPU overload. Using the amazing Chrome Dev Tools I began to do a couple of things. First I used the Rendering tab to enable the FPS meter and to show composited layer borders. The first thing I noticed was that whenever an element on screen was animated, every element after it was being turned into a composited layer. Next I looked at the Timeline tab within the Dev Tools and noticed that there was an absurd amount of painting and rendering happening while animations were active on the page.

Once I discovered that I had a better idea of what I was looking at and was able to find this amazing blog post that went into detail about composited layers in Chrome and also talked about a neat little trick to force composited layers. Thanks to all of this I was able to figure out exactly what was happening. Something about the markup and styling of the screens with these tables meant that every time an element was animated, Chrome would not only convert that element into a composited layer but every other element that followed as well. And because we're talking about tables with large datasets that meant potentially hundreds of composited layers. Obviously that's more than the browser can adequately handle and as a result the borders of these layers (most noticeably the table row borders) were experiencing tearing and rendering glitches.

So I figured out what was happening but unfortunately wasn't able to nail down the exact cause. For whatever reason, our markup and styling just happend to cause Chrome specifically to render these elements oddly. So the solution was to use the translate-z hack and force composited layers on these animated elements. The reason this worked to fix the problem was because the bug was specifically a part of the layer conversion process, ie when an animation began and Chrome then attempted to convert the layer on the fly. By forcing the layer conversion during the intial render of the page, the problem was fixed and the strange UI glitches dissapeared. After that I began use these same tools and fix to further optimize all of the animations throughout the site and as a result the site became buttery smooth.

The moral of the story is that whenever making use of animations it's prudent to check your paints and renders to optimize your page before you run into strange UI glitches.

comments powered by Disqus