# Creating Keyframe Animations with Framer Motion

Sometimes a single movement is simply not enough. Sometimes you want to emphasize an action, draw attention, or even build complex animations with multiple components. If you want to really sell the effect, an animation sequence is just what you need. 

Simple animations might go ***shwoop*** 💨

But with sequences, they can go ***shwoop BAM!*** 💨💥

And who doesn't want that? Am I right?


## See for yourself
Below is a button to open and close a menu. This simple action can feel a lot smoother if we use a sequence. Click on the orange "open" text in the CodeSandbox below to see what I mean.

<iframe src="https://codesandbox.io/embed/keyframes-intro-1xz7ip?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="keyframes-intro"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

Our simple menu button became a joy with just a few lines of code. Feel free to look at the code by swiping the handle on the left. By the end of this article, you will understand precisely how it all works. 


## Let's get started!
In Framer Motion, there are several ways to create such animation sequences. All methods work differently, and each method has its pros and cons. Today we will dive into the first method: **keyframes**. 

I will be using [variants](https://blog.noelcserepy.com/how-to-animate-svg-paths-with-framer-motion#heading-variants), [transitions](https://blog.noelcserepy.com/how-to-animate-svg-paths-with-framer-motion#heading-transitions), [looping](https://blog.noelcserepy.com/how-to-animate-svg-paths-with-framer-motion#heading-transitions) and [easing functions](https://blog.noelcserepy.com/how-to-animate-svg-paths-with-framer-motion#heading-transitions) liberally in this article. Click the links if you don't know what they are or need a quick refresher. They will take you to my [previous article](https://blog.noelcserepy.com/how-to-animate-svg-paths-with-framer-motion), where I explain those concepts in more detail. 


## What are keyframes?
This concept will be familiar if you are acquainted with animation or video editing. Keyframes are used to define the most essential or most extreme poses and positions in an animation. Using keyframes as start and end points, animators would add additional *in-between* frames to create smooth transitions. 

Framer Motion also allows us to use keyframes to define an animation sequence. They are a fantastic tool for creating looping animations and short movements. They can be integrated into other sequences and have a clever trick up their sleeve for switching between animations.


## Using keyframes
Typically when we use the `animate` prop, we provide a single value for the property we wish to animate, e.g. `x: 10`. This will act as the destination. 

To create a keyframe sequence, we can instead provide an **array of values**.

```jsx
import { motion } from "framer-motion";

const variants = {
  slide: {
    x: [0, 200, 0]
  }
}

export default function Loader() {
  return (
  <motion.div 
  variants={variants}
  animate="slide" />
  )
}
```

Framer Motion will automatically detect that we are using keyframes and animate our `x` value from `0` to `200` and back to `0`. This method works without specifying anything else because of the convenient default values Framer Motion uses. 


## Basic examples
Luckily, we don't need to draw any *in-between* frames by hand to create smooth transitions. Framer Motion handles that for us. Let's look at some examples and see how we can use the `transition` options for different effects.

<iframe src="https://codesandbox.io/embed/keyframes-l6regq?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="keyframes"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

### Default
Only the keyframes array and `duration` has been set here. The keyframes are spread evenly over the total duration. Each transition between keyframes takes the same amount of time to complete. 

```jsx
transition: {
	duration: 1
}
```


### Times
If we want more control over the transition timing, we can use the `times` option. `times` is an array of numbers ranging from `0` to `1`. One number for every keyframe. Each number defines when the corresponding key frame should be reached in the animation. 

>Important: The length of the `times` array has to be equal to the length of the keyframe array.

In the example, the component starts at `x: 0`. It reaches `x: 200` after 0.8 seconds and then returns to `x: 0` by 1 second. If we double our `duration` to 2 seconds, these times would also double.

```jsx
transition: {
	times: [0, 0.8, 1]
}
```


### Ease
Defining a single value such as `ease: "easeIn"` will apply that easing function to all transitions between keyframes. Not setting an `ease` value will default to `ease: "easeInOut"`.  

If we add an array of easing functions, we can assign a different easing function to every transition. Note that the length of our easing function array must equal the number of **transitions**. That means the number of keyframes minus one. 

```jsx
transition: {
	ease: ["easeIn", "easeOut"]
}
```


### Null
In all of our previous examples, triggering the animation forces `x` to snap to the first keyframe, regardless of its current position. That means if we begin a new animation while the component is already moving, it snaps instantly to the starting position of the new animation. This can look jarring in certain conditions. 

When we set the first keyframe to `null`, it will act as a wildcard value. **`null` will be replaced by the component's current value** when the animation starts. This lets us transition smoothly between animations. This technique is convenient when we implement drag animations or other gestures.

```jsx
transition: {
    x: [null, 200, 0]
}
```


## Advanced examples

<iframe src="https://codesandbox.io/embed/loader-dji5j4?fontsize=14&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="loader"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

### Jumping ball
I like this example because it shows us a clever and concise way of using keyframes.

The ball jumps up and down using only `y: [0, -70]`. The ball looks like it is being affected by gravity because we apply `ease: "easeOut"`. This will slow down the ball on the way up. But that is only half of the animation. What about the rest?

Now comes the clever bit. We are repeating this animation with `repeatType: "reverse"`. This will play the animation backwards. The ball slows down on the way up, reaches the apex and then speeds up on the way down. This also demonstrates what the different values for `repeatType` do. If we had used `"mirror"`, the ball would descend quickly at first and slow down before it reached the ground. 

This animation demonstrates another aspect of keyframes. Namely, we can store a whole animation sequence within one variant. You can switch between two animation sequences if you click on the button. This is useful when we want to pack interesting interactions into a small footprint.

You can fork the CodeSandbox and try changing the `ease` and `repeatType` options to better understand this. 


### Tumbler
In the second example, I use almost all of the abovementioned techniques. My goal here was to make a domino that keeps falling forward. It is a simple loading animation, yet it shows some of the most significant limitations when using keyframes. 

The animation starts with the box standing tall in the middle of the platform. I set `originX: 1` and `originY: 1` to move our [transform origin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin) to the bottom right. This allowed me to rotate the box around that corner to mimic falling. 

Because the box is 80px tall and 20px wide, tipping it to the side twice will move it by 100px. Thus, I use `x: -100` to slide the box to the left to keep it in the same place. 

Because the shape tips on its side, the transform-origin is now on the **bottom left**. Yet, to tip the shape upright again, I need to rotate it around the **bottom right**. All the movements I have done thus far have been in relation to my original transform-origin. Moving the transform-origin is problematic because it will change all my previous transformations.

My solution was to **swap the component** for a new one using `opacity`. The new component starts lying down and has its origin on the bottom right. I can then tip the new component to its vertical position and swap it back to complete the loop.

After all this, you are probably wondering where the loop even began. Even if you look at the code, you might think, "😨 My goodness! This looks frightfully hacky!"

And you would be right!

Let's talk about it.


## Drawbacks of keyframe sequences
Although keyframes can create great-looking sequences, there are some concerns about usability. 


### Useless keyframes
In many cases, we want to animate multiple properties of our component (e.g. `opacity` and `x`). We can simply add the keyframes for both properties. 

```jsx
variant: {
	opacity: [0, 1],
	x: [100, 0],
}
```

But the whole point of a sequence is that things happen one after the other. ***Shwoop BAM!*** 💨💥, remember? Meaning, not all properties are animating at the same time. 

If we want to animate `opacity` **before** `x`, we need to add useless keyframes to every property we are not currently animating.

```jsx
variant: {
	opacity: [0, 1, 1],
	x: [100, 100, 0],
}
```

This forces us to duplicate keyframes. This isn't a problem for simple animations but adds up quickly when we are animating many properties with many keyframes.


### Composition drives complexity
Managing multiple components with keyframes is a pain. The reason is **timing**. 

The `times` option uses fractions of the total duration. This is handy if we animate one component, but what about two? Suppose we try to start two animations from different components simultaneously. In that case, we need to calculate the start time by multiplying the total duration by the `times` value of the given keyframe. Confusing, right?

I altogether avoided that in the *Tumbler* example by using the same duration for both components and matching the `times` values. But this brings us back to adding useless keyframes. 

And what if we want to change the timing? We need to go into every property array and every `times` array to update values. Keeping track of things like this turns into a nightmare in no time.


### Adding pauses is painful
To add a pause, you must duplicate all properties' keyframes and then define a new `time` value. 

```jsx
variant: {
	x: [100, 0, 0, 100],
	transition: {
		times: [0, 0.4, 0.6, 1]
	}
}
```

Needless to say, this is not ideal, especially if you have many properties. 


## The better way
This article covered creating keyframe animations and using the various `transition` options. With the help of examples, we discussed keyframe animations' uses and limitations. This ended the article on a rather glum note. But what if I told you there was a better way?

Join me in part 2, where we will explore `useAnimationControls()` and solve some of the issues we have with keyframes. 







