CSS Animations and Transitions CSS Animations and Transitions

Layout Animation FLIP Technique

Learning Paths:
Check out a free preview of the full CSS Animations and Transitions course:
The "Layout Animation FLIP Technique" Lesson is part of the full, CSS Animations and Transitions course featured in this preview video. Here's what you'd learn in this lesson:

David implements the FLIP technique which is an acronym for first, last, invert, and play. Rather than directly animating expensive properties like x, y, width, and height, the FLIP technique calculates the first and last position/size of an element and uses transform properties to create the animation.

Get Unlimited Access Now

Transcript from the "Layout Animation FLIP Technique" Lesson

[00:00:00]
>> So we talked about states, and now we're going to move into something a little bit different. So these next two sections are getting into more advanced CSS animations. These are things that you might not use too frequently, but it's also very, very useful to know regardless. So the first one is layout animations.

[00:00:24] And so a layout animation is actually one of those things that we talked about at the beginning where you want to avoid doing. Remember we want to avoid doing things that are going to do what's called triggering layouts. And so this means avoiding manipulating height, width, margin, padding, border, things like that, and only using transform and opacity.

[00:00:52] But sometimes we actually do wanna do animations where we do change the width or we change the height. So how exactly do we do this? There's something called called the FLIP technique that helps us with this. And so, let me just search this there. It's a pretty old technique.

[00:01:12] And it was first introduced by Paul Lewis at Google, and this is all the way back in 2015. But the FLIP technique stands for First, Last, Invert and Play. And so the basic idea behind this is that instead of animating the width and height of something, you animate an element to its final position.

[00:01:36] And then you just sort of like a rubber band, you sort of inverse the transition so that you're shrinking it using transforms, and then you're playing it so that it transforms back to zero. So I know that might have been a little bit confusing, I actually have a visual explainer right over here.

[00:02:01] Okay, so let's say that you have a box that's right over here, how do we animate it to somewhere like over here, especially when we don't know exactly where it's going to be? So we don't know how many pixels or how far it's traveling down, but we want to do it in a smooth way.

[00:02:21] So the first step, and this is the F in FLIP, is you want to get the initial dimensions of the elements. Now, we could use that by elements.getboundingclientrects, which we'll show you in a minute. And then secondly, you want to get the dimensions after it changes. So let's say that you're doing something in JavaScript, you click a button, something is changing, and you need to basically change the layout.

[00:02:51] So this isn't just x and y position but it could also be width and height. So, when you get the dimensions after the change, now you have two rectangles, the first one and the last one. So, now what you want to do is you want to invert it.

[00:03:07] So you calculate the delta, which is this starting x in this final x, and also the starting y and the final y, and you're going to move that element back to its original position. So right now, this is actually translated like a negative x value and a negative y value from its final position.

[00:03:29] So it looks like it's in the first position, but really it's in the last position, but inverted so that it's back where it originally was. And then after that, you just animate everything to zero. So, I know that was a lot, but if you look on dribble and even on mobile apps, layout animations are pretty much everywhere.

[00:03:58] So, let's talk about how we could do this with just JavaScript and CSS. So in order to do this, we are going to be looking at the layout animations exercise. So I'm going to do the first part with you, and then hopefully you could do the second part on your own.

[00:04:18] So here's the layout animation that I want us to do. It's this little box here, it says Animation. And so when we click it, it's going to get bigger and then we're gonna have an animated text showing up on the right hand side. And so we could just go back and forth between that.

[00:04:39] And this is also using that same data state attribute that we talked about in the previous lesson. So the question becomes, how do we animate between this and that? Because they're in two separate positions, we can't just translate it. This could be a completely different layout, and actually it is.

[00:05:01] So, over here we have a figure, and this figure is just showing the box over here, but now it's a different layout where we have some padding and we also have CSS grid. So again, we can't just add a translate and call it a day, we have to figure out a way to smoothly animate this box to be in the other position.

[00:05:25] So, let's go ahead and do that. We're going to be in index.html over here, and we have a few things set up. First we have the figure elements and so that's going to be this blue box over here. We also have the caption elements which is going to be the text over here.

[00:05:46] That's not important right now, that's gonna be for you to do. And we have the app. So we're listening for clicks on the app, so we're not gonna use anything from that. And all we're doing right now, is changing the state from collapsed to expanded. All right, so FLIP technique, what do we do first?

[00:06:07] So the first thing we do is we want to capture the first rectangle of the figure. So const first, and I'm gonna call this firstrect = figureEL.getBoundingClientRect(). So let's console.log, what that firstRect is. All right, so when we look in the console, and we click, you're gonna see this DOMRect object.

[00:06:41] So this has a few properties. The top left, which is also x and y or y and x respectively, and the bottom, the height, the width. So basically all of the dimensions of this rectangle relative to the viewports. So this is going to be really useful for us.

[00:07:04] And so I'm gonna just call this first. And so this is the change that happens. We have app.dataset.state, and we're doing something over here and now we want to capture what the rectangle is after this change happened. Now in order to let the documents read paint, read layouts and do everything, it's better to do this in a request animation frame.

[00:07:33] Which remember, we were talking about those 60 frames per second in which the browser renders things. So if a change like adding this collapsed attributes to the dataset.state is happening, then we want to make sure that the browser has a chance to actually apply that. So it's better to read it in the next animation frame where we could pretty much guarantee that that change is being made.

[00:07:58] So we're going to then get the second rectangle, so const secondRect= figureEl.getBoundingClientRect. And let's console.log, that second one. Okay, so we click here. Now, we have the first one and the second one. And already you could tell that the values are a little bit different, x is 354 in the first one, and in the second one x is 169.

[00:08:35] So you could tell that it moved to the left. This is all really useful information that we could use. So, now what do we do now that we have these two rectangles? We have to find the delta between, we'll start with x and y, so we're gonna say const dx = firstRect.left minus lastRect.

[00:09:08] What did I call this, secondRect? Let's call it lastRect.left. And so we're going to be using this to basically invert the position. And so the results of this or at least the intermediate result is we want the last rectangle to look like it's in the spot of the first one.

[00:09:35] So I'm gonna console.log, let's get that dx value. And then we're going to be setting a CSS variable. So figureEL.style.setProperty. And now, we're going to set dx to that dx variable. And we're going to be using that in a bit. So bear with me. When we click here, we had dx of 185.

[00:10:05] And if we look at the elements, we can see that we have that CSS variable applied. So what's cool about this is that we could actually say that we want to transform this such that we're gonna calc that variable times 1 pixel, sorry if you can see that, where'd it go?

[00:10:30] All right there this. Okay, so now we're using that dx variable to basically move it to its initial spot. And so, we're doing that with CSS. And so if we look at the styles over here, where we're going to be adding it to here. So let's just do the same thing over here, so transform, translate and we're gonna calc dx times 1 pixel.

[00:11:07] Okay, so we click here. Notice that this is supposed to be in the second position but at least as far as the x value goes, it's still in the first position, so we know that our dx is working. And so now, let's do it for dy as well.

[00:11:29] So I'm just gonna copy this dy, top, and we're gonna set it right over here, dy. Okay, now we just have to add the style for that. So this is gonna be dy. And so now hopefully, when we click this, this rectangle should look like it didn't move, at least as far as the x and y position.

[00:12:01] So now we clicked, it's still there. And so basically what we do from here is, we are going to take this, in fact, we could do it over here. And I am going to first add a transition, and this transition is going to be for transform over, let's say, 1 second.

[00:12:27] So watch, if I do transform: none. Notice how it's sliding to the place where it's supposed to be. So the way we could do this is by adding some more data attributes. So you see everything is being tied together over here. So we have dx and we have dy, and so we are going to add, yeah, here we go, figureEL.dataset.flip = invert.

[00:13:06] So we got the first rectangle, we got the last rectangle which is the FL of FLIP. Now, we're doing invert, and so in the invert style, which I'm gonna add here, this is going to be & data-flip=invert. We are going to be doing that transform. So I'm just gonna move this over here.

[00:13:35] And also, notice how we're not doing any transition here just yet. And that's because I want to also add a data-flip= play and transform that to none. And so here's what we're gonna do just to demonstrate. We're gonna set a timeout and after let's say one second, we're gonna say that this flip should be play.

[00:14:03] So it's gonna change state after one second. So let's see what happens. We click here, it's in the original position and then it snaps back. So now, if you're probably thinking what I'm thinking, just add a transition, and transform, 0.3 seconds. Now let's see what happens. We click here, so actually we don't really need that setTimeout.

[00:14:33] So now, I'm going to remove the setTimeout, but there's a problem over here. We have flip invert and flip play, so this flip invert will actually really not have an effect. And we're gonna see it just snap immediately to play, which isn't really what we want. We want some sort of a delay so that the browser knows, okay, I'm here, and I need to move it over here.

[00:14:58] So we could just put this in another animation frame. Okay, so now you see it moves back and forth. But there is one missing piece, and so that is the width and the height. So let's just add those real quick, const dw equals, we're going to get basically the percentage or the ratio of the first width and the last width.

[00:15:34] So firstRect.width divided by lastRect.width, and, oops, same thing with the height. So dh height lastRect.height. So now we could set all these over here. And then we have to add a little bit of styling too. So, now we're not just translating, but we also have to scale it according to the width and the height.

[00:16:07] So this is gonna be scale[var--dw|, var(-- dh)], okay. That's wrong [LAUGH] That happened the wrong way, so let me just see what I did wrong. So what's cool about this is that sometimes it's just, okay, the numbers should be the other way around but let me just check over here because I did do it yeah so we have var dw, var dh.

[00:16:44] Let's see, just checking what I did wrong, I have this dy over here, that's not good, it should be dh. [LAUGH] Okay, cool. So now when we click, you could see that it stretches and it does everything. And it's working pretty well. Okay, so you might be looking at this and thinking, okay, why can't I use a JavaScript library for this?

[00:17:14] Which I mean, Greenstock sort of has something that allows you to do this. If you're using React, then framer motion has this idea of layout animations, too. And so there's a few reasons why I want to show you this. Not to explain that, this is super easy to do.

[00:17:31] Although you could just put this in the function and then use it in your app without having to install any libraries, but really, it's to sort of introduce you to request animation frame and just doing advanced animations without fear. So basically the combination of using data set, just getting the bounding client rect of the elements you and using request animation frame.

[00:17:55] And also using CSS variables allow you to do a whole range of styling which you might not have been comfortable doing before. Or you might have said, okay, I should really reach out for an animation library or reach out for this custom thing that someone made to do my specific animation.