Beyond Keyframes – Modern web animation techniques



Beyond Keyframes – Modern web animation techniques

0 2


animation-talk


On Github elifitch / animation-talk

Beyond Keyframes

Modern web animation techniques

So, who is this guy?

¯\(º_o)/¯

Hi, I'm Eli

@elifitch

I work here.

recruiting

And I do a lot of front end development, animation, and...whatever this is.

Fun, frivolous, experiments like this guy

I'll be shreiking about

How to betterify your CSS animations

How the browser makes all your pretty pixels

When JS animation makes sense

How to write more performant JS animations

How JS animation libraries can help

This isn't going to be super pracitcal "this is how you use this neat library" type stuff, there's lots of information out there on the interwebs for that, I'm going to focus more on when and why they should be considered, and relative strengths/weaknesses

CSS is great for small state changes

This applies to most interactions, really.

Think changing the background on a ghost button, animating a button press to give the sense of 3d.

CSS makse such small changes really easy and quick to whip up, making carelessness easy too.

Most people have a good handle on this and just throw them in there willy nilly without regard for performance

Performance isfuckingimportant

Talk about the facebook study

Small % of users, 20-30 million

CSS properties are not created equal.

Hmm these appear to be the same but why is one choppy

WTAF is goon on here??

Browser rendering steps

Layout

Paint

Composite

Describe layering, composting, painting.

Layout is figuring out where everything is on the screen and how big it is.

Layout: Also if you're changing heights, widths etc can change the position of other elements it causes more layout changes and more and more and more and OH GOD

Mind your layout

Images hard to see but the orange blocks represent browser thinky time

Blue is GPU doing its thing

See how one is way easier on the browser than another?

This is b/c one prop triggers layout while the other does not

Paint is slow

Depends on size of paint area: ie fixed backgrounds cause janky scroll

Lets cheat.

Layers aren't free, layers aren't a magic bullet. They break sticky elements for example, b/c they fix relative to the layer, not relative to the viewport.

On older mobile devices, you can run out of GPU memory, and have a severe stutter as a result

Test baby test

Paint areas

Layer borders

Scroll bottlenecks

FPS

Gotta test. If you wait too long to start testing render performance you're totally fucked. Work with designers to make sure the cool stuff you guys dream up doesn't kerplode everything in a fireball of shit.

Property Breakdown

Layout

width/height

top/right/bottom/left

padding

font-size

Paint

color

background

box-shadow

border-radius

Composite

opacity

scale

rotate

translate

Avoid layout changes, and if you have to make one, if you can isolate that element, good

Avoid continually repainting large areas (parallax for example)

Leverage transforms and opacity in creative ways to make up for other shit

Why not use CSS to animate everything?

It can be a little limiting.

When you get crazy, so does your workflow

I know which properties give the most performance now, I can optimize my CSS animations, why not use for everything.

When you think in terms of what CSS will let you do you will never fulfil your potential

JS gives you the power.

Independent transforms

Complex easing (physics!)

Animating weird stuff

This, as we will discuss later, also applies to any javascript object property

This, as we will discuss later, also applies to any javascript object property

Animating along a bezier curve

Randomized animation

IE8/9 support! hahaha hah haa... .......

Moving on.

Wait, isn't JS animation slow?

Isn't shitty JS anim the whole reason CSS rose to prominence?

Myth Busting Time.

JS !== janky

jQuery $.animate is the devil.

JS isn't necessarily janky. Poorly WRITTIen JS anims are janky.

Folks falsly conflate JS and JQ anims

setInterval vs. requestAnimationFrame

With RAF "The browser can optimize concurrent animations together into a single reflow and repaint cycle

For example, JS-based animations synchronized with CSS transitions or SVG SMIL.

Also Animations in inactive tabs will stop, allowing the CPU to chill.

It's also synced with refresh rate so you won't drop frames

RAF isn't scary

							
var opacity = 0;

// runs every 16ms to try to achieve 60fps (1000ms/60 ~= 16ms).
var fadeIn = setInterval(function() {
	if(opacity < 1) {
		element.style.opacity = (opacity += 0.05);
	} else {
		clearInterval(fadeIn)
	}
}, 16);
							
						
							
// requestAnimationFrame: Attempts to run at 60fps based on
// whether the browser is in an optimal state.
var opacity = 0;

var fadeIn = function() {
       if(opacity < 1){
         window.requestAnimationFrame(fadeIn);
         element.style.opacity = (opacity += 0.05);
       }
}

fadeIn();
							
						
You can also get a start time and continue the animation until a certain amount of progress has happened if you want to have time based animations, or wrap it in a settimeout with the timing set as 1000/desiredFramerate If there's just one "complex" anim in a project (simple button hovers and shit but one rotating gradient), just roll your own JS animation However, if you have more than a handful of these, really really consider using a library.

Performance is more than properties

Just talking 2 easy things to have a big impact

JS performance could be a whole talk in and of itself

Remember how I said JS animation isn't janky, shitty JS animation is janky?

Theres some easy tips to improve your JS performance

As long as we're here, lets Not just talk anims, but also w/ events that trigger anims

Don't thrash

							
var h1 = element1.clientHeight;           // Read (measures the element)
element1.style.height = (h1 * 2) + 'px';  // Write (invalidates current layout)

var h2 = element2.clientHeight;           // Read (measure again, so must trigger layout)
element2.style.height = (h1 * 2) + 'px';  // Write (invalidates current layout)

var h3 = element3.clientHeight;           // Read (measure again, so must trigger layout)
element3.style.height = (h3 * 2) + 'px';  // Write (invalidates current layout)
							
						
							
// Readenzie
var h1 = element1.clientHeight; 
var h2 = element2.clientHeight; 
var h3 = element3.clientHeight; 

// Writenzie
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h1 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
							
						

especially bad w/ jQuery. Makes it so easy to read and write to the dom, that people do it willy nilly w/o regard for performance

Credit Wilson Page http://wilsonpage.co.uk/preventing-layout-thrashing/ be mindful of layout in JS too, CSS often triggered by JS Problem is, nobody actually writes code this way. Segue into workflow/library shit? You can use something like fastdom, or a dedicated anim library that includes batching as a performance enhancer

Debounce

							
var debounce = function(func, wait, immediate) {
  var timeout, result;
  return function() {
    var context = this, args = arguments;
    var later = function() {
      timeout = null;
      if (!immediate) result = func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) result = func.apply(context, args);
    return result;
  };
};

window.addEventListener('scroll', function(){
	debounce(function(){
		//cool animation
	},100)
},false)
							
						
Debouncing is a fancy pants way to say limit a function from firing too many damn times. Especially important when binding to resize or scroll. Why fire a function more times than the browser can even refresh? This belongs in JS section

Lets talk workflow

						
.logo {
	animation: myAnimation 2s linear forwards;
}
@keyframes myAnimation {
  0% {
    opacity: 0;
    transform: scale(0, 0);
  }
  25% {
    opacity: 1;
    transform: scale(1, 1);
  }
  50% {
    transform: translate(0px, 0);
  }
  100% {
    transform: translate(100px, 0);
  }
}
						
					

So what happens when somebody wants the translate to take an extra 400ms?

This, basically.

You have to add 400ms to the duration and go back and recalc all the percentages relative to the new time and agh

It sucks, we've all had to do it, but it doesn't have to be this way

donde esta la biblioteca

Velocity

Greensock/GSAP

Managing CSS keyframes, especially when they get complex, can be hard, it can be time consuming.

Impedes experimentation.

And in the end, when you want to get creative, CSS animation is limiting(no independent transforms, different steps with different easing etc)

Lib your life.

						
.logo {
	animation: myAnimation 2s linear forwards;
}
@keyframes myAnimation {
  0% {
    opacity: 0;
    transform: scale(0, 0);
  }
  25% {
    opacity: 1;
    transform: scale(1, 1);
  }
  50% {
    transform: translate(0px, 0);
  }
  100% {
    transform: translate(100px, 0);
  }
}
						
					
						
var el = document.getElementsByClassName('logo')

Velocity(el, {
    opacity: [1, 0],
    scale:[1, 0]
  },{
    duration:500,
    easing: "linear"
  }
);
Velocity(el, {
    translateY: '100px'
  },{
    duration:1000,
    delay:500,
    easing: "linear"
  }
);
						
					
						
var el = document.getElementsByClassName('logo')

TweenLite.fromTo(el, .5, {
    opacity:0,
    scale:0,
    ease:"Linear.easeNone"
  },{
    opacity:1,
    scale:1,
    ease:"Linear.easeNone",
    onComplete: function(){
      TweenLite.to(el, 1, {
        delay:.5,
        y:100,
        ease:"Linear.easeNone"
      });
    }
  }
);
						
					

Expressed in terms of time, not percents more intutive

Performing multi anims on 1 element is easy, b/c instead of writing 1 super complex keyframes, you can just chain velocity or GSAP calls (come in, drop down)

Both of these examples show how you can feed in start values for the element different from its resting CSS which can be useful

Easier than wrangling with keyframes, and yeah, probably easier than rolling your own RAF loops.

Other nice-to-haves

Reverse / yoyo

Sequencing

Granular control

TweenLite.from

R/yy makes it super easy to go back to the original position, w/ CSS you have to make the anim go to 50%, and build it back to orig yourself, its unintuitive

Sequencing: instead of specifying a huge keyframes you can just chain anims together. If you want to change one, you can change the ONE. Doesn't screw up the maths for the whole thing

Last two only GSAP

If you're making a super super complex animation(10+ seconds long), storing it in a timeline means you can scrub to different parts to experiment, rather than waiting until the end every time you iterate

Allows you to easily breathe life into static elements

Workflow is important

Easier changes

More experimentation

Better work

Animation by its very nature is iterative and experimental

Performance

Global timing loop for all animations

Minimizes layout thrash

Won't animate anything visually imperceptible

1) too many timers firing at once reduces frame rates due to the browser’s overhead of maintaining them

2) improperly marking the time at which your animation begins results in dropped frames (usually you have the animation start when you click the button, but due to a myriad of factors like CPU overhead, other JS logic at the same time, etc) can cause a delay in when you start to see the anim, dropping early frames.

Caching property values across chained calls in order to minimize the occurrence of DOM querying (which is the Achilles' heel of performant DOM animation).

Caching unit conversion ratios (e.g. px to %, em, etc.) across sibling elements in the same call.

Kind of like a debounce, function only runs when nec. Skipping style updating when updates would be visually imperceptible.

You could write these optimizations into your own hand crafted animation code, but then A. your code becomes harder to manage, and B. you end up creating an animation library yourself.

Another dependency

le UGH!

Velocity ~35kb TweenLite & CSSPlugin ~60kb

Don't be a stupid.

Weigh the costs and benefits.

le OMG I love libraries

Which one is raddest?

Greensock

More options and capabilites

Insane timlineing and control

A tad better performance

Big KB hit

Licensing

Best for 1 pagers, rich motion design, webGL/canvas/games/experiments

It can do more. A lot more. It can tween any property of any javascrpit object. BIG distinction. Means that it's not just for UI animation, but for WebGL, canvas, games, and other shit my puny human brain can't even dream up

Timlining both good for workflow and make it easier to have animations overlap, sequence, etc

I'm unsure as to why, but it performs a smidge better than velocity in ultra high stress situations

TweenLite & CSSPlugin ~60kb

Have to pay to use it in a product

For the fun and the frivolous

Velocity

UI only

Lighter KB hit

More approachable API

Same syntax as jQuery

100% open source & free

Plays extra well with promises

Best for UI animation in applications

It's a UI animation tool, well suited for it, and w/o the bloat/features of GSAP

~30kb, all in

At least to me, it makes a bit more sense. Take sequencing for example, you just follow it with another .velocity, don't need to mess around w/ callbacks

this is huge. The examples we showed thusfar was to how to use velocity w/o jquery. However, if you use jquery you can literally just include velocity, and do a find/replace for .animate, and you've gotten a massive, free performance boost

Julian is a great guy, loves PRs, easy to get involved in community

Each velocity call returns a promise object

TLDR:

Be aware of how your code impacts browser rendering

Test render performance as you work

Avoid $.animate and setInterval

Debounce & don't thrash

JS libs make sense in a certain set of circumstances

Conclusion