Layout projection: A method for animating browser layouts at 60fps
Animating layout is hard. Prohibitively so; how many ambitious designers have provided dev teams dazzling videos of app-quality UI animations, only to be rebuffed?
If you're a web developer, what would your reaction be if asked to realise this kind of App Store-style interaction, where an item opens into a full-screen view when clicked:
Chances are, the two static layouts would be implemented without even an attempt at building the transition. The rationale would be numerous and legitimate: The time constraints versus the sheer complexity of implementation, or the ongoing maintenance of a codebase left in a state of late-game Jenga.
Maybe even a gut feeling that this kind of animation is literally impossible on the web, no less at a smooth 60 frames per second?
These reasons (and perhaps more) would burst spiralling out of your weather-worn spider senses, straight into the ears of yet another deflated design team.
Designers: This isn't a lack of matching ambition. Layout animations on the web present a range of formidable technical and practical barriers.
In this short series of posts, I'll provide an overview of the layout animation problem and some of the solutions attempted to date.
Then, I'll present layout projection, a new method of animating layouts using the performant CSS
transform property. An early implementation of it already powers Magic Motion in Framer and the layout animation features in Framer Motion.
Framer Motion presents this complex functionality behind a minimal, declarative API. In the opening App Store demo, all the layout animations were created with this bit of markup:
<motion.div layout />
Making the implementation of layout animations easy to write, read and maintain means it's more likely they'll be implemented at all. If a feature is successful you won't be saddled with a delicate codebase. If it isn't successful then you've wasted far less time on polish.
layout prop is also great for creating state-driven layout animations, like reordering the items in a list and automatically animating changes in the output:
A common request is being able to animate
height: auto as content is added/removed. Better to do it with
We can even use this simple API for shared element transitions. By replacing
layout with a unique
layoutId, we can animate new elements from old ones, as if they were one continuous element:
isSelected && <motion.div layoutId="underline" />;
The user-facing API for layout animations is minimal. But internally, too, the layout projection technique is abstracted to a simple viewport-relative bounding box.
We'll see how this viewport box abstracts the complexities of layout projection so practically and mentally we can concentrate on using it to create advanced animations like shared element transitions and drag-to-reorder. We'll also learn about explorative work to leverage this viewport box to incorporate SVG and WebGL into our performant layout animations.
Framer Motion is a React library, but in the final part we'll do a technical dive into the key beats of a functional implementation. By the end, I hope to share enough of an understanding of layout projection that a reader could implement their own library around this technique.
First, let's take a closer look at layout animations, why they're so difficult to perform, and some of the solutions attempted so far.