60 FPS

Web Applications

Hello!

My name is Alex Bardanov.

I'm  a front-end developer in GlobalLogic Ukraine.

GitHub: dnbard

Skype: dnbard

Contents

  • What is 60 fps and why they are so important ?
  • Imperative and Declarative animations.
  • What browser are doing between frames?
  • Layers
  • Benchmark first, test later

What is 60 fps ?

I heard that 24 frames per second are enough!

24 fps / 41.67 ms

60 fps / 16.67 ms

up to

84 fps / 11.9 ms

60 frames per second

only 16.67ms to prepare

next frame to paint

Imperative vs Declarative Animations

Imperative Animations

( javascript )

  • using JavaScript
  • using the Browser's main thread (which is already busy with other JavaScript, style calculations, layout and painting)
  • animating in JavaScript does give you a lot of control: starting, pausing, reversing, interrupting and cancelling are trivial

Imperative Animations: requestAnimationFrame

  • requestAnimationFrame should be used instead of setTimeout, setInterval
  • it will run the callback in sync with refresh rate of your display (usually 60fps)
  • decouple animations from scroll, resize, mouse and keyboard events(don't change anything in event handler; redraw all changes in requestAnimationFrame callback)
  • parameter passed into the callback function is a high resolution timestamp

Declarative Animations

( transitions and animations in CSS )

  • browser can optimize CSS animations
    • additional layers are going to be created
    • some actions are going to be executed off main thread
  • lack the expressive power of JavaScript animations
  • very hard to combine properly with Imperative animations

What browser are doing between frames?

Pixel Conveyor

16.66 ms

Pixel Conveyor - JavaScript

  • Imperative Animations
  • Any changes that are done with help of JavaScript (data sorting, adding elements to the page, element's class name changes etc)

Pixel Conveyor - Style

  • Declarative Animations
  • Applying styles to elements according CSS selector rules

Pixel Conveyor - Layout

  • Calculated size and position of element
  • Size and position could affect layout of child elements so layout of these elements could be changed as well 

Pixel Conveyor - Paint

  • Pixels are going to have own colors at this point
  • Next core elements are affected:
    • text (font, color, style etc)
    • background (color)
    • images
    • borders
    • shadows
    • any other visual elements
  • Paint can be done on several Layers

Pixel Conveyor - Composite

  • Layers are drawn in different position with correct order

Example: Full Cycle

If you would change any property that would involve any change in Layout (geometry, width, height, positioning) then full cycle of Pixel Conveyor would be involved.

Example: Full Cycle

Styles that affects Layout:

width	       height
padding	       margin
display	       border-width
border	       top
position       font-size
float	       text-align
overflow-y     font-weight
overflow       left
font-family    line-height
vertical-align right
clear	       white-space
bottom	       min-height

Example: Paint & Compose

Changing an element may also trigger painting. Depending on how the elements in your app are grouped into layers, other elements besides the one that changed may also need to be painted.

color	        border-style
visibility	background
text-decoration	background-image
background-position background-repeat
outline-color	outline
outline-style	border-radius
outline-width	box-shadow
background-size

Example: Composite Only

4 things that Browser can animate fast:

Position - transform: translate(x, y);
Scale    - transform: scale(n);
Rotation - transform: rotation(deg);
Opacity  - 0...1;

Paint vs Composite

Guidelines for animation:

  • Use CSS keyframe animation or CSS transitions, if at all possible
  • Use requestAnimationFrame if needs to be it’s JavaScript-based animation. Avoid setTimeout & setInterval
  • Avoid changing inline styles on every frame if you can, declarative animations in CSS can be optimized by the browser
  • Using 2D transforms instead of absolute positioning will provide better FPS

TL;DR:

Only transform & opacity;

never top & left!

Layers

Layers

transform: translateZ(0);

Hack to create a new layer.

New Layer is created if element:

  • is <svg /> or <input />
  • display isn't inline or inline-block
  • height isn't percentage, implicit or auto
  • overflow equal to scroll, auto, hidden
  • not descendant of <table />
  • position equal to absolute, relative, fixed

Layout trashing

Layout Thrashing occurs when JavaScript writes, then reads, from the DOM, multiple times causing document reflows.

// Read
var h1 = element1.clientHeight;

// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';

// Read (triggers layout)
var h2 = element2.clientHeight;

// Write (invalidates layout)
element2.style.height = (h2 * 2) + 'px';

// Read (triggers layout)
var h3 = element3.clientHeight;

// Write (invalidates layout)
element3.style.height = (h3 * 2) + 'px';

Layout trashing: quick fix

// Read
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;

// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';

// Document reflows at end of frame

Solution for bigger project

var h1 = element1.clientHeight;

// Write
requestAnimationFrame(function() {
  element1.style.height = (h1 * 2) + 'px';
});

// Read
var h2 = element2.clientHeight;

// Write
requestAnimationFrame(function() {
  element2.style.height = (h2 * 2) + 'px';
});

Use requestAnimationFrame!

Solution for bigger project

Benchmark first, fix later

Benchmark first, fix later

Benchmark first, fix later

Canvas? WebGL?

Yes!

Update

Rendering

Update

Update and rendering cycles

requestAnimationFrame

Images

Combine similar images into the texture.

This are going to improve performance, loading time and memory consumption.

 

Pre-load all assets (images, fonts etc) before usage.

Shaders

Shaders

Explore WebGL shaders @ https://www.shadertoy.com/

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 uv = fragCoord.xy / iResolution.xy;
	fragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0);
}

Or learn them @ https://aerotwist.com/tutorials/an-introduction-to-shaders-part-1/

Thanks!

60 FPS

By Alex Bardanov

60 FPS

  • 1,173