AngularJS From Beginning: Animation Performance Optimization - Part 20

I am here to continue the discussion around AngularJS. Today, we will discuss how to perform performance optimization in animation using AngularJS. Also in case you have not had a look at our previous articles of this series, go through the following links:

We have already discussed in the previous articles, how to create different animations using AngularJS, CSS,and JavaScript, but we must always be concerned with the user experience. All animations in separate samples are great, and performance is not an issue. However, when we are dealing with big single-page applications with heavy processing, a lot of CSS and a big DOM, we should focus on keeping our web app fast and the animations smooth; this is the topic of this article.

The display and the frame rate

Each device and monitor display has a regular refresh interval. A usual monitor frequency is 60 Hz, which means that the display will refresh 60 times per second, and a new frame will be displayed approximately every 16 ms. JavaScript animations can use etInterval or setTimeout using 16 ms (60 frames per second), as this is a commonly used refresh rate. It's not a good idea to use this hardcoded value, as different devices have different refresh rates and the timer precision is not reliable. We are able to work around this vulnerability using the requestAnimationFrame function. It receives callback functions like the setInterval and setTimeout functions do, but requestAnimationFrame only calls the callback function when the browser is going to produce a new frame before the next repaint. To support older browsers, some vendor-specific code can be used as well, as described in the following code: 
  1. windowwindow.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || 
  2. window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;  
Index.html
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <title>Performance</title>  
  5.     <link href="performance.css" rel="stylesheet" />  
  6. </head>  
  7. <body>  
  8.     <h1>Animation with javascript</h1>  
  9.     <!--There is a click listener for this button -->  
  10.     <button id="jsBtn">Click here to move with JS</button>  
  11.     <div id="jsanimation">  
  12.         This block will be moved  
  13.     </div>  
  14.     <script src="performance.js"></script>  
  15. </body>  
  16. </html>  
Performance.css
  1. #jsanimation {  
  2.     position: relative;  
  3. }  
  4.   
  5. #cssanimation {  
  6.     position: relative;  
  7.     -webkit-transition: all 2s ease-in-out;  
  8.     transition: all 2s ease-in-out;  
  9. }  
  10.   
  11. .move-to-right {  
  12.     -webkit-transform: translate(100px,0);  
  13.     transform: translate(100px,0);  
  14. }  
Performance.js
  1. windowwindow.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || 
  2. window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;  
  3. var jsAnimationElement = document.getElementById('jsanimation');  
  4. var jsAnimationBtn = document.getElementById('jsBtn');  
  5. //This global variable holds the position left of the div  
  6. var positionLeft = 0;  
  7. function moveToRight() {  
  8.     positionLeft += 10;  
  9.     /* Set position left of the jsanimation div */  
  10.     jsAnimationElement.style.left = positionLeft + 'px';  
  11.     if (positionLeft < 100) {  
  12.         requestAnimationFrame(moveToRight);  
  13.     }  
  14. }  
  15. jsAnimationBtn.addEventListener('click', function moveBtnClickListener() {  
  16.     requestAnimationFrame(moveToRight);  
  17. }, false);   

This sample uses Request Animation Frame (RAF); so, the callback function that moves the element 10 px will be called before each repaint until the element has moved 100 px to the right. If we set a hardcoded time interval using the setTimeout function, we might lose a repaint. Losing repaints gives the user a feeling of lag. Although RAF helps improve the animation quality, we still have to keep in mind that a new frame is often generated every 16 ms, so the browser has 16 ms to compute the JavaScript, manipulate DOM, execute the layout, paint and anything else, otherwise you will lose a frame and the animation smoothness might be degraded. As CSS animations' execution is separated from the JavaScript execution, it's easy to see that modern browsers can improve the animation's smoothness. The animation frame is ready independent of the JavaScript execution. If you execute a heavy loop and it keeps processing in JavaScript, your JavaScript animation might fail (freeze), as the thread will already be busy processing the loop. This is why we should always prefer CSS animations instead of JavaScript animations, in my humble opinion. In the following topic, we will learn how to find out whether our animations are losing frames.

Finding performance bottlenecks using Chrome DevTools

We just learned the benefits of using requestFrameRate and CSS animations. However, if we want to improve our animations' performance, how can we find out what is slowing things down? Nowadays, we have tools that help us find these bottlenecks, and one of them is Chrome DevTools, which is a collection of tools that comes with the Chrome browser. To open Chrome DevTools, just press F12 or click on any part of a page,and then click on Inspect element. This tool is very familiar for web developers. Now, we will learn how to analyze the timeline frames mode. We need to record the execution, so Chrome will show us a real-time report with the frame rate, as shown in the following screenshot:

 
The top-right timeline has lines for 30 fps and 60 fps, and the height of each vertical bar on Frames View represents the time it took to complete a frame and send it to the screen. So, if we have vertical bars above the 60 fps horizontal line or above the 30 fps line, we are skipping frames. We can focus on a specific time range in order to analyze what happened in between. Usually, a frame is displayed after the JavaScript execution, Style and layout, Paint, and Compositing Layers. The paint phase is the biggest one, so we should be careful with it; this is a common scrolling bottleneck. Chrome DevTools separate the steps of a frame and how long each step takes, as shown in the following screenshot:
Measuring browser layers and Jank on Chrome

We just saw that using Chrome DevTools helps us find performance bottlenecks. Now, we are going to see how to find paint layers and enhance an animation just by replacing some CSS properties. For this sample, we will create a sliding menu such that when we click on a button and it slides from the left menu items, the page content slides to the right too. When we click on the button again, both the menu and page content slides to the left until the menu vanishes from the viewport. For the above concept, please write down the below code and check in the chrome development tools.

Index.html
  1. <!DOCTYPE html>  
  2. <html ng-app="myApp">  
  3. <head>  
  4.     <title>Performance</title>  
  5.     <link href="bootstrap.css" rel="stylesheet" />  
  6.     <link href="performance2.css" rel="stylesheet" />  
  7. </head>  
  8. <body>  
  9.     <div id="menu" data-ng-show="showMenu">  
  10.         <nav class="navbar-collapse bs-navbar-collapse collapse in">  
  11.             <ul class="nav navbar-nav">  
  12.                 <li><a>menu item 1</a></li>  
  13.                 <li><a>menu item 2</a></li>  
  14.                 <li><a>menu item 3</a></li>  
  15.                 <li><a>menu item 4</a></li>  
  16.                 <li><a>menu item 5</a></li>  
  17.                 <li><a>menu item 6</a></li>  
  18.                 <li><a>menu item 7</a></li>  
  19.             </ul>  
  20.         </nav>  
  21.     </div>  
  22.     <div id="content">  
  23.         <button ng-click="showMenu = !showMenu">Menu</button>  
  24.         <h1>Main content</h1>  
  25.         <p>  
  26.             Lorem ipsum text Lorem ipsum text Lorem ipsum text Lorem ipsum text Lorem ipsum text  
  27.             Lorem ipsum text Lorem ipsum text Lorem ipsum text Lorem ipsum text Lorem  
  28.             ipsum text Lorem ipsum text Lorem ipsum text Lorem ipsum text  Lorem ipsum text  
  29.     </div>  
  30.     <script src="angular.min.js"></script>  
  31.     <script src="angular-animate.min.js"></script>  
  32.     <script>  
  33.         angular.module('myApp', ['ngAnimate']);  
  34.     </script>  
  35. </body>  
  36. </html>  
Performance.css
  1. #menu.ng-hide-add {  
  2.     transition: transform ease-in-out 0.3s;  
  3.     transform: translateX(0px);  
  4. }  
  5.   
  6. #menu.ng-hide-add-active {  
  7.     transform: translateX(-150px);  
  8. }  
  9.   
  10. #menu.ng-hide-remove {  
  11.     transition: transform ease-in-out 0.3s;  
  12.     transform: translateX(-150px);  
  13. }  
  14.   
  15. #menu.ng-hide-remove-active {  
  16.     transform: translateX(0px);  
  17. }  
  18.   
  19. #menu {  
  20.     position: absolute;  
  21.     width: 130px;  
  22.     background-color: white;  
  23.     transform: translateZ(0);  
  24. }  
  25.   
  26.     #menu.ng-hide + #content {  
  27.         transform: translateX(0px);  
  28.     }  
  29.   
  30. li {  
  31.     box-shadow: 1px 1px 1px black;  
  32. }  
  33.   
  34. #content {  
  35.     width: 80%;  
  36.     transition: transform ease-in-out 0.3s;  
  37.     transform: translateX(150px);