- If there's any kind of visual change onscreen, from scrolling to animations, the device is going to put up a new picture, or frame, onto the screen for the user to see.
- Most devices today refresh their screen 60 times a second, which we measure in hertsz.
- So to much that we need to have 60 frames to put up. Most of the time we'll refer to this as 60 frames a second, or fps.
- People are expecially good at noticing when we miss one of these frames.
- If the browser is taking too long to make a frame, it will get missed out. The frame rate will drop and users will see stuttering. If it is really bad, then the whole screen can lock up, which is the worst.
- 1000ms / 60 frames => 16 ms / frame
- Realistically, you have somewhere in the region of 10 to 12 milliseconds to get everything done and make sure the frame arrives on time.
-
The browser makes a GET request to the server.
GET / HTTP/1.1
-
The server responds by sending some HTML
-
The brower parses the HTML and creates the DOM.
-
DOM + CSS => Render Tree
- Combining DOM + CSS is "Recalculate Styles" in DevTools
-
in Render Tree, there will be only nodes that will actually be displayed on the page.
- e.g.
head
will not be in there but the following will be in there.
section h1:after { content: "I <3 pseudo elmz"; }
- e.g.
-
Once the browser knows which rules to apply to an element, it can begin to calculate layout. Or, in other words, how much space elements take up and where they are on the screen. Making CSS to boxes is refered as Layout or reflow.
-
The web's layout model means that one element can affect others. So, for example, the width of body will affect all the way down the tree. (a lot of process for the browser)
-
Paint
-
Rasterizer
-
The next step in the process is to talk about vector to raster. Vectors will be mapped into pixels (raster).
- draw bitmap
-
Rectangles, some text, shadow, shite line, a picture.
-
Image Decode + Resize
- You may have noticed in that previous list that one of the calls was called draw bitmap. What we normally do is we send things like JPEGs, or PNGs, or GIFs down the wire to our page.
- What the browser has to do is decode these into memory.
- Image Decode
- potentially, we are doing something like 'resposive web design'. and so the image may also need resizing.
-
Sometimes, browsers make multiple surfaces called layers, or composite layers and it can paint into those individually.
- layers can be composite. It means that one layer can go behind the others.
- handling these layers is referenced as Composite Layers in the DevTools.
-
Layout
- On Layout, the scope is Whole document, the entire DOM. Possibly, we can limit the layout scope but it is very difficult. It is called Layout Boundary.
-
The typicall flow (pipeline) of the Layout change
JavaScript
->Style
->Layout
->Paint
->Composite
- First, we can use JavaScript, like jQuery, to change the layout. OR, CSS Animation, Web Animation API also work. So, those can be the alternate to this 'JavaScript'.
-
The changes we make won't necessarily trigger every part of the pipeline. In fact, there are three ways the pipeline normaly plays out for a given frame.
-
Three different pipelines
JavaScript
->Style
->Layout
->Paint
->Composite
- (JavaScript) You make visual changes either with CSS or JavaScript, the browser must recalculate styles of the elements that were affected.
- (Style) Few changes the layout property, so that changes the elements geometry, like 'width', 'height', or 'position' with relation to another element, like 'left' or 'top'.
- (Layout)And the browser has to check all the other elements and reflow the page.
- (Paint) Any affected areas will need to be repainted
- (Composite) and the final painted elements will need to be composited back together
JavaScript
->Style
->Paint
->Composite
- when you change your paint-only property, like background-image, text color, or shadows.
- (JavaScript) the above change
- (Style) the styles are calculated
- (Layout) we don't do this since we didn't change the geometry and any elements.
- (Paint) We do this.
- (Composite) We do this.
JavaScript
->Style
->Composite
- when changes something that requires neither layout nor paint, just compositing.
- compositing is where the browser puts the individual layers of the pages together. This requires layer management to ensure we have the right place and in the correct order.
- No, actually not quite. Focus on the things that matter to your end user
-
there are 4 different areas, RAIL
-
RAIL stands for
Response
,Animation
,Idle
, andLoad
-
Load
- initial load done in 1 second. after loading, it is normaly 'Idle', waiting for the user to interact.
-
Idle
- 50ms.
- This Idle time is our opportunity to deal with things that we defered.
-
Response
- This is not a sense in the size. This is responsive in the sense of that it reacts to the user input without delay.
- The study shows that there is a limit of 100ms
- This is 10th of 1 second after someone presses something on screen and before they notice any lag. So, if you can respond to all user input in that time, you are good to go.
-
Animation
- That is great if the thing they did was to say, toggle a checkbox or tap a button. And you show a single change, like a selected state. But there is another version of this which is more challenging.
- which is that the user does something that requires animation.
- The most challenging performance issue always come out of the need to hit 60 fps. which is either interaction that sticks to the user's finger or transitions and animations.
- For this we have a limit of 16ms (1 sec / 60). In reality, we have around 10 - 12 ms only, because the browser has overhead.
-
https://speakerdeck.com/paullewis/making-a-silky-smooth-web?slide=122
-
Paul could not get the 60 fps for the card animation on the dev summit page.
-
He came up with a new strategy to achieve it. that is called "FLIP".
- He tried working backwards. He took the advantage of the browser don't need hard work at the initial point to do animation. So, he precalculated the expensive work.
-
He measured all the elements positions before and after that they meant he knew how far everything needs to move all the page and if the opacity changed, he also knew that as well too. (He did the heavy calculation in the response phase of 100ms.)
-
First
- e.g. where the card starts.
-
Last
- e.g. where the card finished
-
Invert
- e.g. invert the animation
- using the information of first and last to apply transform and opacity changes to reverse the animation with a little bit of extra work with clipping it was like the card had never moved.
- e.g. invert the animation
-
Play
- e.g. everything is ready to play.
- Recalculate Styles
- Combining DOM + CSS. Creating the Render Tree.
- Layout / Reflow
- Calculate how much space elements take up and where they are on the screen.
- Paint
- draw calls.
- Rasterizer converts vectors to raster (draws them into pixels).
- Image Decode
- Decode things like JPEGs, or PNGs, or GIFs into memory.
- Resize
- Resize things. (responsive web design requires this a lot.)
- Composite Layers
- The process of handling composite layers
- Modern JavaScript engines recompile your code to something that can run more quickly. It is done through a JIT (Just In Time) compiler.
- JIT comiler will optimize the JavaScript bit by bit as it runs, and it is a brilliant but extremely complex engine.
- For us as developers, what this means is that there is no way to look at JavaScript and know exactly what code will run in the engine.
Micro-optimizations
come about when you try to write code that you think would be a little bit faster for a browser to run. Like, "which is faster?". We don't know how the piece of code will be treated in the engine. so there is no point guessing. Any micro-optimization should be a last resort once you have exhausted all your other options.
for (var i=0; i < len; i++)
or
while(++i<len)
- requestAnimationFrame should be your go-to tool for creating animations
- the browser has to render the frame at 60 frames a second. 1 frame has 16ms/frame (1000ms/60frames).
- realistically, you have 10 - 12 ms because the brower has housekeeping stuffs too.
- JavaScript part of your frame should typically be kept around 3 to 4 ms at most because there is going to be all the work like style calculations, layer management, and compositing will come afterwards.
- requestAnimationFrame schedules your JavaScript to run at the earliest possible momennt in each frame. By this way, browser will not be interfered from other tasks. so that it will run fastest.
- A lot of codes around the web that is used for animation uses
setTimeout
orsetInterval
because back in the day, that is all there was.- jQuery still uses setTimeout for its animations today.
- The Problem of using these functions is that the JavaScript engine pays no attention to the rendering pipeline scheduling these.
- they are good functions to use when you want to wait sometimes to elapse, or, do some repeated tasks every so often. NOT good for Animations
- How to use?
function animate() {
// Do something for your animation
requestAnimationFrame(animate); // Scheduling the next one
}
requestAnimationFrame(animate);
}
- IE9 NOT supported. Use polyfill which would use setTimeout. it is not ideal for fallback but it will allow you at least to use requestAnimationFrame in your code, and not worry about the compatibility.
-
Web Worker provides interface for spawning scripts to run in the background.
-
Normally, websites run in a single thread running on the operating system.
-
Web Workers allow you to run JavaScript in a totally different scopes than the main window and on a totally separated operating system thread.
-
Whatever happens in the main thread in main window won't affect or be affected by the worker thread and the opposite is also true.
-
However, the two threads can send messages back and forth. This means you can isolate long-running JavaScript inside a worker thread and allows the main thread to run free unimpeded.
-
Web Workers will not create any janks on the main thread
-
More about Web Worker from: http://www.html5rocks.com/en/tutorials/workers/basics/
- There are 2 types of web workers in the specification, 'Dedicated Workers', and "Shared Workers". However, this page talks only about Dedicated workers, so I follow. Therefore, 'web workers' or 'workers' will refer to dedicated workers throughout. http://www.whatwg.org/specs/web-workers/current-work/
- Create a new object in your main page using the constructor like this:
var worker = new Worker('task.js');
- If the file exists, the browser will spawn a new worker thread, which is downloaded asynchronously.
- The worker will not begin until the file has completely downloaded and executed.
- In case of 404, the worker will fail silently.
- After creating the worker, start it by calling the
postMessage()
method():
worker.postMessage(); // Start the worker.
- Communicating with a Worker via Message Passing
- Communication between a work and its parent page is done using an event model and the postMessage() method.
- Depending on your browser/version, postMessage() can accept either a string or JSON object as its single argument.
- The latest versions of the modern browsers support passing a JSON object.
(in Main script)
var worker = new Worker('doWork.js');
worker.addEventListener('message', function(e) {
console.log('Worker said: ', e.data);
}, false);
worker.postMessage('Hello World'); // Send data to our worker.
(in doWork.js (the worker))
self.addEventListener('message', function(e) {
self.postMessage(e.data); // Sends the data back to the main.
}, false);
-
the message passed into the worker is copied, not shared.
-
To stop a worker:
worker.terminate()
from the main page orself.close()
inside of the worker itself
-
To Transfer objects
- Copy may take hundreds of milisec if the object is large. In this case, use the same function,
postMessage()
with different signature:
- Copy may take hundreds of milisec if the object is large. In this case, use the same function,
worker.postMessage(arrayBuffer, [arrayBuffer]); // first arg: data, the second arg: list of items that should be transferred.
window.postMessage(arrayBuffer, targetOrigin, [arrayBuffer]);
worker.postMessage({data: int8View, moreData: anotherBuffer}, int8View.buffer, anotherBuffer); // the first arg can be JSON obj.
- when you don't optimize the order of the pcocesses, you are forcing the layout occurs before style.
- JavaScript -> Layout -> Style -> Layout(2nd) -> ...
- for example,
var paragraphs = document.querySelector('p');
var greenBlock = document.getElementById('block');
for (var p = 0; p < paragraphs.length; p++) {
var bllockWidth = greenBlock.offsetWidth; // Changing layout
pragraphs[p].style.width = blockWidth + 'px'; // Changing the style after chaning layout!
}
- Orange boxes brought by checking the option,'Show layer borders' in devTools. They are the elements that own their own composite layer.
will-change
css property available in chrome and firefox.- The browsers will still need to paint.
- The benefit of this property is that creating a new layer is expensive. Creating new layers could be expensive because they need to be created and then painted and doing that on-the-fly is expensive.
- This will tell the browser that we intend to change the elements transform at some point. Such prepare for that it creates a new layer.
.circle {
will-change: transform; // Let the browser know that we are going to
transform: translateZ(0); // In production, not all browsers support will-change. This forces the browser to create a new layer. Hacky-way.
}
- this
transform: translateZ(0)
is called "null transform" hack. Not so many browsers support thewill-change
so you might need to have them both in production. This forces the browsers to do 3d transform pushing the element in z space.will-change
is a hint the browser can ignore and this brings the browser more options.