PERFORMANT web ANIMATIONS
Achieving 60FPS
Emily Hayman
- Awkward
- Unnatural
- Janky
- Slow
WHY?
prevent poor User experience
"Silky smooth" feels fast
- Frame rate measures a site's responsiveness
- 60fps is the target for a natural feel
- =16.7ms to achieve all necessary updates
- Caveat: More frames, more processing - might result in dropped frames
- Better to deliver lower frame rates more consistently (30fps)
What?
AIM FOR 60 FRAMES PER SECOND
On initial page load:
- Download and parse HTML, CSS, JS
- Evaluate JS
- Calculate element styles
Let's take a step back.
How is the browser generating pixels from your code?
- Generate layout of the page from the render tree
- Where elements are placed is correlated to other elements
- Forced synchronous layouts (layout thrashing) caused by multiple read / writes
- Re-layouts are expensive
Layout
geometry of the page
- Calculation of visual styles
- Re-paint will only occur once per frame - will only re-draw "dirty" elements
- Browser will attempt to consolidate re-paint regions in a single "dirty" rectangle - may need to promote layers to prevent side effects
- Re-paints are expensive
PAINT
FIlling in the pixels
- Default is to paint elements on a single memory layer
- Separating elements onto compositor layers allows non-destructive changes
- Only necessary work is to calculate the new position for each layer
- CPU draws the layers; GPU composites the layers
- GPU is very efficient at basic drawing operations
- Changes on GPU-composited layers are the least expensive
Composite
generating the layers
- Transform
- Opacity
- Seriously, that's it.
How do i take advantage of this?
only change properties that trigger compositing
Transform allows:
- Position (transform: translateX(5px))
- Scale (transform: scale(2))
- Rotation (transform: rotate(90deg))
- Skew (transform: skewX(50deg))
- Matrix (transform: matrix3d(...))
Get Creative
transform
Instead of:
Get Creative
transform
.box {
left: 10px;
position: absolute;
transition: .2s left;
&.active {
left: 20px;
}
}
Try this:
.box {
transform: translateX(10px);
transition: .2s transform;
&.active {
transform: translateX(20px);
}
}
Instead of:
Get Creative
Opacity
.box {
background-color: rgba(0,0,0,.9);
transition: .2s background-color;
&.active {
background-color: rgba(0,0,0,1);
}
}
Try this:
.box {
opacity: .9;
background-color: #000;
transition: .2s opacity;
&.active {
opacity: 1;
}
}
- GPU handles opacity changes by painting the element texture with a lower alpha value
- Must be on its own layer
Get Creative
Opacity
- Forcing promotion ensures layer is painted and ready
- Consider promoting expensive paint elements (position: fixed; overflow: scroll;)
- Will fix "flickering" or "shimmy"
What else can i do?
manually promote elements to their own compositor layer
-
backface-visibility: hidden;
- transform: translate3d(0,0,0);
-
"Tricks" the browser into promoting the element
the old way
"tricking" the browser
- Provides method for informing the browser what types of optimizations will be needed ahead of time
- Current support: Chrome + Firefox
The shiny, new method
will-change
- will-change: auto - default; standard optimizations
- will-change: scroll-position - indicates upcoming animation of an element's scroll position
- will-change: contents - indicates content is expected to change
- will-change: <property> - define property (for example, "transform")
The shiny, new method
will-change
- The browser already does its best to optimize - stronger will-change optimizations are resource heavy
- If animating non-GPU properties, browser forced to re-paint on the CPU and then upload to the GPU - can potentially be even more costly
- Using will-change implies element is always moments away from changing
as in all things, moderation
there can be too many composited layers
quick example
what not to do
![](https://s3.amazonaws.com/media-p.slid.es/uploads/312421/images/2261479/pasted-from-clipboard.png)
quick example
what to do
![](https://s3.amazonaws.com/media-p.slid.es/uploads/312421/images/2261482/pasted-from-clipboard.png)
imperative vs. declarative
how do i best accomplish my goal?
Two methods of creating web animations:
- CSS (Declarative)
- Javascript (Imperative)
declarative animations
utilize css
- Browser can make optimizations - it knows the end point of the operation
- Can run operations off the main thread
- Missing the expressive power of JS animations; may grow overly complex
- Recommend toggling a class using JS for basic user input actions
- Generally, the more performant option
Imperative animations
utilize Javascript
- Runs on the main thread - more likely to result in dropped frames
- More control - responsive to user input; start/ stop easily
- Generally, the less performant option
What if i need to use JS?
requestanimationframe
- The setTimeout of the future - native API for running an animation
- Typically called at 60fps; requests animation drawing at the next available opportunity - not a set interval
- Browser optimizes performance and groups into a single repaint (saves CPU cycles)
- IE10+ support
requestanimationframe
function doSomething() {
requestAnimationFrame(doSomething);
// Do stuff
}
doSomething();
What if i need to use JS?
avoid layout thrashing
- Read, then write
- Back and forth read / writing will cause reflows
- Browser tracks "dirty" elements and queues up changes until necessary; reading certain properties forces premature re-calculations
for(i = 0; i < el.length; i++){
var width = element.offsetWidth * 2;
el[i].style.width = width + 'px';
}
var width = element.offsetWidth * 2;
for(i = 0; i < el.length; i++){
el[i].style.width = width + 'px';
}
Bad
Good
upcoming optimizations
css containment
- "contain" property: indicate an element's subtree is independent from the rest of the page
- Lets the browser know it's safe to optimize an element
- Can indicate "layout", "style", or "paint" - or "strict" for all
- Ensure DOM updates in the subtree do not trigger reflows on parent document
testing animations
chrome developer tools
Rendering Tools:
- Enable paint flashing
- Show layer borders
- Show FPS meter
Timeline
Questions?
Performant Web Animations
By ehayman
Performant Web Animations
- 11,054