Fronteers Spring conference

Animation and perceived performance by Tobias Ahlin

This is the first talk in a set of three talks on Visual Performance delivered on April 1, 2016 at Fronteers Spring Conference in Amsterdam

How do we animate smoothly without laptop fans going haywire all around the world? It’s through animation that we make sense of the world: doors swing open, cars drive to their destinations, lips curl into smiles. Even the things that feel instantaneous, like lightning striking, or dropping a phone on your face while browsing in bed, happen over time, and it’s through that motion that we understand how objects relate and function; if they are light or heavy, rigid or loose, connected or separate, sticky or slippery.

In this talk Tobias digs deep into theory and code examples in the pursuit of trying to answer a seemingly simple question: How do we really create performant web animations?

Transcript

(presenter) So our first speaker today, Tobias Ahlin, is going to be talking about web animation.

So you may know Tobias already from the work that he's done at GitHub over the years.

He has also spent time at Spotify.

He works on, kind of, product strategy across those organizations, and does-- and does a number of courses on Hyper Island.

Today Tobias is going to be talking about making web animation more performant.

So please make him very welcome, Tobias.

All right.

Am I on? Perfect.

Thank you, Phil.

I'm so happy to be here today and talk about, before my web animation, to talk about performance-- I want to talk about animation and to talk about animation, I want to start to talking about motion.

All right.

So what is motion really? Motion is a change of an object over time.

And what would life really be without motion? It would be pretty boring.

We wouldn't have smiling faces or flowing rivers or falling leaves or silly walking or cats typing on keyboards or the celebration of our lifetimes or the sad moment we realize the CSS isn't working in Safari.

Or falling snow or that awkward handshake where you just want to melt through the earth.

So we have all of this thanks to-- yes, happy ending-- through motion, right? And as soon as something is moving in a way that we're not expecting it to, we instantly realize that something's wrong.

So motion is sort of the way that we make sense of the world.

Right? And so this is from Google's design guidelines.

Reading here.

"Just as the shape of an object indicates how it might behave, watching an object move demonstrates whether it's light, heavy, flexible, rigid, small, or large.

Motion describes spatial relationships, functionality, and intention with a beauty and fluidity."

So even, I think, if you can just read this and nod your head, and be like yes, this makes sense-- we have this weird habit of trying to create static mock-ups in Photoshop and Sketch.

And then we design everything, and then you click something, and then everything flashes to white.

And then we switch screen.

All right? And when we're left there hanging, trying to make sense of how every single object on this screen relates to the other screen.

And motion is a really good way to make sense of how things change, right? With that said, motion isn't always good, right? We can always communicate the wrong thing.

It's not very easy to always, even with motion, get things right.

But with that said, I think that we'll look at back at this time of static interfaces as a very weird time, an awkward time, where we think it's fine to sort of have static UIs in a very motion-full world.

So yes, we can create better experiences with motion.

But as soon as we are animating, we need those motions to be performant.

So let's talk about performance.

We're going to talk about some theory and some practical examples.

For example, animating border width, and things that can be difficult to actually get performance, and other stuff like box shadow, how to actually make those hardware accelerated.

So, got it? Great.

Short intro to me, why I'm standing here.

So, hi.

Hello.

This is me, this is where you can find me on the internet.

I'm from a town called Gothenburg on the west coast of Sweden, around here, where everyone is called Glenn.

And this is my favorite book, and this is my first album, John Hopkins.

I love waffles, but I more than that love soup waffles.

And yes, applause for that.

And yes, I-- my background is a designer, at Spotify.

So I was there and helped to build a design team, and then moved on to Github, and did a lot of CSS refactoring on github.com, and also

worked on Github pages and the Github clients for Mac and Windows.

Just to be between both design and development-- that's sort of what I'm trying to continue doing.

And this is sort of where I started animating.

This is an open-source project called SpinKit, with tons of snippets you can use in your animation.

So that's it.

That's me.

Let's talk about performance.

So animating with CSS.

Just to do a quick primary to get-- make sure we're on the same page.

You can animate in two ways with CSS, right? You've got transitions, and you've got animations, or animation blocks.

And so we can basically animate anything we want in CSS.

Right? Most of the things on the right-- so here-- we can change, we can animate that change over time.

So we've got things like opacity, we can do border, animations, we can animate the background color, letter spacing, margin, whatever.

Can Google just "animatable CSS properties," and you'll get to that.

We've got two ways of doing those animations, right? We've got transitions.

And typically you'd have a transition declared like this.

You could say, I want to animate the opacity.

300 milliseconds.

Timing is ease out.

And then, for example, if you add a class that changes the property of your opacity, you can animate out an element.

All right? Easy.

You're not limited to one of those-- you can have two things.

So for example, here, we are doing all changes for our properties, and we're adding a transform, and we're scaling it out.

And then, if we're at that class, we'll add another effect to that.

So then we've got animations as well.

So what's the difference between animations and transitions? Animations is really a set of key frames that you can stretch during any period that you want.

So if a transition is an animation from point A to point B, an animation is a set of key frames that animate from point A to point B with as many in-the-middle steps as you want.

So this spinner, for example, is made up of three key frames.

Right? So we've got the declaration for the animation up here, and then we've got the name here, with animation block, and we're defining three key frames.

And then it's just looping through those three.

So we've got this thing happening.

And A looks the same as B.

So it just loops infinitely.

Great.

So performance.

Right.

We're always striving for 60 FPS.

Right? This is the magic number.

But of course, this is sort of a spectrum rather than just a hard number that we're looking for.

It's really hard, when we're talking about web performance, to predict the performance of a certain device or certain user, right? So we were striving towards this direction, and keeping away from this direction, or worse, a very jaggery, a laggy experience, right? So the bad news is that all of these properties-- nah.

Like, forget them.

You can't animate them.

All right? So this is sort of the limitation that we're working around, that a lot of things that we want to do we can't simply do.

So the good news is that there's a set of properties that are very versatile that we can use to create, sort of, anything, and if we just keep ourselves to using them specifically, and solely, we'll have, generally, GPU, hardware-accelerated animations and they perform-- they will perform great.

So the list of things is opacity and transform.

And transform is very versatile, right? So we've got rotation, scale, translate, and even filter is GPU-accelerated.

And this is generally cross-browser.

And compatible advice.

Pretty much every browser works the same.

So if we keep to these, we'll have great performance.

So let's quickly review what we've got.

We've got opacity-- we can fade things in and out.

We can rotate things, scale things up and down, and move things around.

Right.

So with these, we minimize paints and layouts during animations.

So if we do not keep to these, we do something else, like animate the width of something, or height, would trigger a paint or a layout, a re-layout, on every single frame.

Or one of those at least.

So this is the set.

And so for example, you can't use width.

Use scale x instead.

Don't use height.

Use scale y instead.

Right? So the list goes on.

Don't use position absolute and top-bottom and animate those properties.

You should use translate.

And then, margin, the same.

Do translate.

Padding, et cetera, et cetera.

You get the point.

So I think this concept is easy to grasp but difficult to apply.

All right? It is a very simple rule, but then you will get requests, or you will wish to do something that is like animating the border, or animating box shadow.

So I think in that sense, it's sort of like Sudoku-- with a very simple set of rules you can explain to someone immediately, but you will get stuck with difficult issues.

So let's explore some concrete examples of how to actually work around this.

So how do we animate the background color if we can't animate the background color? Same goes for border width and box shadow.

So let's go through those three.

So background color.

Typically maybe you'd want to do something like this.

You have a background color, and then on hover state, you change the background color, and you want to animate it.

All right? Creating an effect sort of like that.

An easy way to work around that is actually to just not set it, that color, on hover, but with a pseudo-element instead.

Right? So this is sort of the box, but from an angle.

All right.

So we'll create another element, and containing the background color yellow, right? And now we can animate the opacity, which is hardware-accelerated, and then we're limiting ourselves to the opacity, but we can fade colors, right? So then we can control-- when you're hovering or doing something with a parent element, we can control the pseudo-element, and then actually transition between those two colors.

So the same sort of principle can be applied to border width, for example.

We've got this effect going on here, with the border expanding.

So we're doing the exact same thing.

We're adding a pseudo-element to link, here, and we're setting it to be the exact same size as its parent.

And then we're scaling it down to become the border at the bottom.

Right? And then when you hover the parent, you can scale it up again, and then you've got a GPU-accelerated border.

And you can animate that in any way you want.

So then how do we do box shadow? Right? So this is a popular effect-- for example, in the Google material guidelines.

Scaling up an element and also sort of animating the box shadow of that element to show that it's popping out.

And typically, or what you might-- or get a feeling for doing, is just animating the box shadow like this.

And this is very expensive, right? So you should avoid doing this.

And the solution is, again, similar to what we just did before with those other elements.

We can have a box shadow on-- a very small, subtle box shadow-- on the parent, and then we can create a very big box shadow on the-- with a pseudo-element.

And then we just hide that.

And then on hover, we scale up the parent, and we also fit in the box shadow, creating an effect like this.

So the visual-- the visuals is sort of identical.

It's very hard to differentiate.

But the performance gain is huge, right? So what we've got here is paints and layouts, right? And FPS.

This is Safari or Chrome, I can't remember.

But-- so here is traditionally if you would animate box shadow.

Right? This is just hovering this box, as we just saw.

And this is what we get if we do it with a pseudo-element.

All right.

So the performance gain is massive.

And if you just animate-- Google "animate box shadow," you can get some more details on this technique.

So the general principle is that if you are stuck and want to animate something that you can't animate, extract those properties to a new object.

You don't have to do a pseudo-element, it's just very handy.

Right? You could be any div or element.

Whatever you want.

But extract those styles to a new element and animate that element instead with those few sets of styles that we have-- opacity, transform, and filter.

So raise of hands.

How many have heard of will-change? Yes.

There's a bunch of you.

That's good.

So I'm not going to go into the intrinsic details of will-change, but I'm going to talk about what will-change is not.

Right? It's not magic.

It's not something that you can sprinkle on things to make things suddenly very performant.

Just like translate zed just doesn't make the GPU do magic.

So for example, we were used to preventing flickering in Safari by using translate zed, right? So this is another conf-- sorry, smart web.

But up here, you can see things scrolling and popping between different rendering modes.

Right? And this is the layer-- this is the object moving to a new layer and then back.

So going from CPU-rendered to GPU-rendered.

And that change in itself is expensive, right? So we used to fix this, for example-- or you can trigger this by only using, or triggering a layer on hover.

For example, here.

Translate 3-D. And

we traditionally could fix this by sprinkling some of this on the default state.

Right? So the idea is really that it's not a replacement, but part of it is replacing this.

Right? So it's informing the browser and giving it hints that maybe you'll need, actually, a separate layer, so it can prepare for that.

But it doesn't mean that with this, if you sprinkle these, you can animate the width and the height and any other thing that you really want to animate, all right? It's not a way to get to GPU, it's a way to prepare the browser for a change.

Right? So it usually affects the start and the end of an animation.

And trying to give the browser a hint of something expensive.

So I've got a last tip here, which is beautiful animations with delays, and also performant animations with delays.

So I think this is a bit of an unconventional tip, because it's more about designing around the problem of performance.

But that's sort of why I like it.

So we have an effect like this, for example, in iOS-- and probably a lot of mobile phones, right?-- where all the icons land at different-- with different timing.

Right? So those in the core land first, and then we've got those around the edges landing a second later or so.

And then this creates a very soothing effect, right? This looks very nice.

Tumblr got something similar here, but translating things on the y-axis instead.

And it's just a very nice effect.

And this is something that designers like to sprinkle on a lot of things.

And we can easily create this with CSS for example, right? We can just loop through an array of icons, or something, and produce incremental delays, like this, for a few objects.

Right? The really good thing about this, that it's actually-- we're doing the same amount of animation work for the browser, just over a longer period of time, which means that it's less intensive.

Right? So if we look at these rules, for example-- if we're playing an animation here that has a duration of 300 milliseconds, that means that while these three are playing, when the first one finishes, the fourth one starts.

So at any given point in time, we'll only have three animations playing in total.

This is actually a really clever way of creating a nice effect, but also spreading out the amount of work you're doing to create better-performing animation.

A word of caution, though.

This is traditionally-- you should probably prefer to do this with JavaScript if you have timing-sensitive things that you're doing.

So Safari, especially, has an issue dealing with timing.

So if you have a very time-sensitive animation like this-- like four blocks, here, folding, and if one blocks fold before the other, the folding effect here will be completely destructed, right? But this is Safari in many page reloads.

It sort of takes your delays as a suggestion.

And then it's like, yes, I'll treat them as I want to.

So if it's sensitive, prefer to do things with JavaScript.

So I think we started a few minutes early, so I actually think I'm on time.

So to wrap things up-- animate only transform, opacity, and filter if you want performant web animations.

When you can't do that, animate-- and want to animate some other property, extract that property to a new object and animate that object instead.

Right.

And use will-change to prepare the browser for an animation.

And then you can use incremental delays for groups of objects.

So two articles that I want to share with you.

No, sorry, first two are actually inspirational sites.

So either take up your phones and take a picture of this, or you can find the notes later.

These are two great collections of tons of UI inspiration and animation.

And they're both really good resources if you're just looking to get inspired, and start animating more.

Then if you want to read more about performant animation, Paul Lewis has a really good article called FLIP, which talks more about how to avoid animating top, bottom, and left and right if you want to move objects.

And Sarah has a really good article on will-change on the Oprah blog.

So that's it.

I'm done.

Thank you.

[APPLAUSE] (presenter) That's Tobias.

Post a comment