#235: Animations, Animations, Animations!

Robin: This week I’ve been thinking a lot about Framer Motion. It’s a library designed to make beautiful and complex animations on the web much easier than they would be otherwise. It’s also worth taking a look at the website because, just gosh darn, it is beautiful.

I haven’t built anything with it yet but some of my pals at Sentry have been redesigning our onboarding flows and they used Framer Motion to create some absolutely drop-dead gorgeous animations when combined with our ever-so-lovely illustrations. In fact, all this almost makes me mad because of how good it is.

A few elements appear when the page loads, while the top and bottom sections snap in from the sides. Once that’s done, the light in the corner begins to swing from side to side and then, once you click the button, everything flies up and away. On the next page, the title swishes into view and the text beneath it clicks up satisfyingly.

There’s so much going on with these animations and yet somehow it isn’t annoying at all. To me, these animations makes it feel as if I can trust Sentry— if folks are willing to spend time on the tiny details like this then I’m more likely to trust them with the product, too. Anyway, I didn’t work on this project at all but I was fascinated by how the team built this thing. Evan Purkhiser, the engineer at Sentry that built it, walked me through why he decided to use Framer Motion in the first place and what made it so helpful.

Chris did a video on Framer Motion with creator Matt Perry a while back.

Evan first pointed to the fact that it’s extremely difficult to tie animations together with CSS alone. A lot of the time when we trigger an animation we want another completely unrelated thing to fire at the same time. With CSS all we have are keyframe animations.

First you define the animation:

@keyframes pop {
  0% {
    transform: scale(0);
  } 100% {
    transform: scale(1)
  }
}

Then you apply it to an element:

.element {
  animation: pop 1s ease infinite forwards;
}

This covers a lot of animation use cases (and we even have a mini guide just about animations that goes into a lot more detail). But! What if we want to make another element start an animation once this one ends? Well, that’s where things get a bit bonkers in CSS — we’d have to add animation-delay all over the place and that becomes unmaintainable (although… you might wanna take a look at Mads Stoumann’s ideas with CSS custom properties and animations, which might open some doors here).

The magical thing about Framer Motion is that it’s relatively easy to make one animation begin when another one ends. Not only that, but you can control the animations of child elements and how they stagger via the parent element. This is something that’s just impossible with CSS today.

Here’s how you create a Framer Motion component. Let’s say you have a component in /components/Card.js that looks like this:

import { motion } from "framer-motion";

export default function Card() {
  return (
    <motion.div animate={{ scale: 1.1 }}>
      This is a card
    </motion.div>
  );
}

See that <motion.div> bit? That’s how you tell Framer Motion that this is an animatable element and also what HTML element you want to render. It looks weird, but you get used to it after a while. Also, that animate prop attached to it tells the Motion component to immediately scale by 1.1. But we could make it do that on hover or on tap instead:

export default function Card() {
  return (
    <motion.div whileHover={{ scale: 1.1 }}>
      This is a card
    </motion.div>
  );
}

I really like this syntax and it reminds me somewhat of Vue’s attributes. But anyway, I’m going to keep experimenting with Framer Motion and write a more in-depth post about how to get started, as well as how to tie different animated child elements together because that’s where the real magic lies.


Play and pause CSS animations

Speaking of animations, here’s how to play and pause CSS animations with Custom Properties. Letting folks pause the animations on our websites is always a good idea for accessibility reasons. But also this is a good reminder from Mads Stoumann:

Recently, while working on the CSS-powered slideshow you’ll see later in this article, I was inspecting the animations in the Layers panel of DevTools. I noticed something interesting I’d never thought about before: animations not currently in the viewport were still running!

Maybe it’s not that unexpected. We know videos do that. Videos just go on until you pause them. But it made me wonder if these playing animations still use the CPU/GPU? Do they consume unnecessary processing power, slowing down other parts of the page?

Inspecting frames in the Performance panel in DevTools didn’t shed any more light on this since I couldn’t see “offscreen”-frames. But, when I scrolled away from my “CSS Only Slideshow” at the first slide, then waited and scrolled back, it was at slide five. The animation hadn’t paused. Animations just run and run, until you pause them.


CSS Scroll-Linked Animations

Have you ever heard about CSS Scroll-Linked Animations? It’s a new API “for creating animations that are tied to the scroll offset of a scroll container.” That’s really hard to grap and so I think an example is required for it all to click. To do that you have to download Chrome Canary, head to chrome://flags/ and then turn on Experimental Web Platform features (phew!). Once that’s done you can finally check out how the API works with this Pen:

It’s a pretty remarkable and flexible API that I’m excited to start experimenting with soon.

Bramus has been doing other stuff with these scroll-linked animations. Here’s the start of a thread: