60 frames per second – approx 10-16 ms per frame
Making a frame
HTTP GET request
Server sends HTML
Browser parses HTML into DOM – PARSE HTML
DOM and CSS – RECALCULATING STYLES
Render tree (only visible elements)
Change list of nodes into vector boxes on a page – LAYOUT
Change vector into raster – PAINT
Painting images – IMAGE DECODE + RESIZE
Painting can make multiple layers and paint to them individually in CPU COMPOSITE LAYERS
At the end CPU handles data to GPU and content is painted
Layout and Paint
- JS/CSS Style Layout Paint Composite (margin, width)
- JS/CSS Style Paint Composite (color, background)
2.. JS/CSS Style Composite (cursor, transform, opacity)
CSS Triggers
RAIL
- Response
- Animations
- Idle
- Load
Load
- Should be done in 1 sec
- Above the fold content
Idle
- Waiting for user to interact
- 50 ms long
- Load all non critical assets (Images, Videos, Comments …)
Animate
Response
Javascript Compilers
Just In Time
Modern Browsers recompile JS code to run in Engine. Avoid Micro Optimizations.
Execute JS as early as possible every frame.
requestAnimationFrame
function animate() {
requestAnimationFrame(animate)
}
requestAnimationFrame(animate);
requestAnimationFrame Polyfill https://gist.github.com/paulirish/1579671
Using web workers
Create a web worker
var myWorker = new Worker('scripts/worker.js');
Send data to web worker
myWorker.postMessage({"imageData":imageData, "type": type});
Code of web worker
this.onmessage = function(e) {
console.log('Message in the worker');
var imageData = e.data.imageData;
var type = e.data.type;
//
postMessage(imageData);
} catch (e) {
function ManipulationException(message) {
this.name = "ManipulationException";
this.message = message;
};
throw new ManipulationException('Image manipulation error');
postMessage(undefined);
}
}
Getting data back from web worker
// Get data from web worker
myWorker.onmessage = function(e) {
var image = e.data;
};
JS Memory management
var x = { };
When garbage colector works everything else pauses.
See the memory spikes
If memory doesn’t get to 0 when garbage is collected, we have a memory leak.
GC for Garbage Collection
CSS
The cost of recalculate styles scales linearly with the number of elements.
Selector matching
- Block
- Element
- Modifier
.box–three
Class matching is preferreed
Reduce Affected Elements = fewer changes to render tree
Reduce Selector Complexity = use fiwer tags + class names to select elements
Create an array of DOM nodes to use with forEach()
Batch your style changes and avoid running layout as much as possible.
function getDomNodeArray(selector) {
// get the elements as a DOM collection
var elemCollection = document.querySelectorAll(selector);
// coerce the DOM collection into an array
var elemArray = Array.prototype.slice.apply(elemCollection);
return elemArray;
};
var divs = getDomNodeArray('div');
# sample 1
var newWidth = container.offsetWidth;
divs.forEach(function(elem,index,arr) {
elem.style.width = newWidth;
})
# sample 2
if (elem.offsetHeight < 500) {
divs.forEach(function(elem,index,arr) {
elem.style.maxHeight = '100vh';
})
}
Compositing and Painting
To see which portions of the screen need to be repainted turn on Paint flashing in Performance (Esc) – Rendering – Paint Flashing
Paint Profile
Record performance and deep dive into Paint on Main timeline
Compositing
Don’t use paint when possible.
Use layers and translate them.
Managing layers
Demo https://www.html5rocks.com/static/demos/parallax/demo-2/demo.html
See if an element already has it’s own layer
Performance (Esc) – Rendering – Layer Borders
### Make a layer for the element
.circle {
/* for older browsers */
transform: translateZ(0)
will-change: transform; /* left top width height */
}
Number of layers
- Open Performance and record interaction
- Select item in Frames
- Layers Tab becomes available and shows a 3D environment
Using CSS to show/hide
.story-details {
width: 100%
height: 100%
left: 100%;
top: 0;
z-index: 2;
display: flex
position: fixed;
overflow: hidden
transition: transfotm 0.3s;
will-change: transform;
}
.story-details.visible {
transform: translateX(-100vw)
}
.story-details.hidden {
transform: translateX(0);
}