React SVG Animation (with React Spring) #1
Tags: react, svg, animation, reactspring
Published: 2020-11-21 23:05:45

Are you a video learner? This blog post is also available in a video form on YouTube:

{% youtube fnzlniSEFN4 %}

Look at the animation below 👇

![animation we are going to build](https://dev-to-uploads.s3.amazonaws.com/i/udsww2fx0d8ipnk62pr3.gif)

You'll learn how to build it using React and `react-spring` library.

# Table of Contents
1. [How to Find an SVG to Animate?](#chapter-1)
2. [How to Include an SVG in React?](#chapter-2)
3. [How to Prepare the SVG for Animation?](#chapter-3)
4. [How to Animate the SVG Elements?](#chapter-4)
5. [How to Make the Animation Look Good?](#chapter-5)
6. [How to Create More Advanced Animations?](#chapter-6)

<a name="chapter-1"></a>
<br />
# How to Find an SVG to Animate?

The first step you should take to create an animated SVG is to pick an SVG image you want to animate. If you're a skilled illustrator, you can create your own. But if you're like me, websites like [undraw.co](http://undraw.co) will do the trick. However, the SVG we're animating is no longer there at the time of writing this article but you can still [find it here](https://raw.githubusercontent.com/CerebralFart/UndrawMirror/47cc82983bedebf12eaa0d0ab5a6cc607cd599cf/images/Mailbox.svg).

<a name="chapter-2"></a>
<br />
# How to Include an SVG in React?

There are two ways of including an SVG in React code. 

The first one is using `<img src="kitten.svg" alt="kitten" />` just like with .jpg or .png images. However, this approach is **not** suitable for animations as you lose control over the individual parts of the SVG you want to animate.

The second approach is using "inline SVG". This means putting the SVG code right into React (JSX)! 

To do that, view the source code of [this image](https://raw.githubusercontent.com/CerebralFart/UndrawMirror/47cc82983bedebf12eaa0d0ab5a6cc607cd599cf/images/Mailbox.svg), and copy it. Then, head over to [https://svg2jsx.com](https://svg2jsx.com/) and paste the copied SVG code into the editor on the left. This tool will convert the SVG to JSX. (It will close every unclosed SVG elements and convert attributes such as  `stop-color` to `stopColor`)

Now copy the code from the editor on the right-hand side and paste it into your editor. The SVG will appear in your browser! 

You can view the code for this section in the embedded CodeSandbox below. 👇

{% codesandbox elegant-leftpad-m6g1m %}

(I decreased the `width` and `height` of the original `svg` so that it fits in the CodeSandbox embed)

<a name="chapter-3"></a>
<br />
# How to Prepare the SVG for Animation?

To be able to animate specific parts of the SVG image, you should extract them to individual React components so that it's a bit easier to work with. (Remember, you can treat the SVGs just like HTML and split it into many components.)

Because we want to animate the envelopes let's create a component for each envelope. Now comes the hard—finding out which of almost 300 lines of SVG code create the envelopes. 

Open the browser devtools and try to locate the envelope using the element picker. If you locate a part of the SVG which you think is a part of the envelope, you can try deleting it (using the `delete` key) and see if the envelope (or its part) actually gets removed. (Use `ctrl + z` or `cmd + z` to bring it back).

![devtool with located SVG](https://dev-to-uploads.s3.amazonaws.com/i/hphiytkfo6tmv6brpo2k.png)

After playing with the devtools for a bit, you can identify that each envelope is composed of two `path` elements followed by a `g` element. 

Now, you can head over to your editor, create `<Envolope1 />`, `<Envolope2 />`, `<Envolope3>`, and `<Envelope4 />`. You should use a `g` element as the parent element in your `Envelope` components to group the items in it. (Think of the `g` element as an SVG alternative to the `div` element.)

The last steps in this part is to create an array of the `Envelope` component on top of your main component and include it to where the envelopes where in the `svg`:

```jsx
function Icon() {
  const envelopes = [
		<Envelope1 key="envelope1" />,
		<Envelope2 key="envelope1" />,
		<Envelope3 key="envelope1" />,
		<Envelope4 key="envelope1" />,
	]

	return (
		<svg>
			// ...
			// ...
			{envelopes}
		</svg>
	)
}
```

The image itself should look exactly the same as at the end of the previous section.

You can view the code for this section in the embedded CodeSandbox below. 👇

{% codesandbox infallible-tesla-6zgon %}

<a name="chapter-4"></a>
<br />
# How to Animate the SVG Elements?

Finally, here comes the fun part! 🎉 

First, you need something to trigger the animation. Let's use a simple `button` which will toggle a variable. Based on the value of that variable, the envelopes will arrive or go away.

```jsx
import React, { useState } from 'react'

function Icon() {
	const [toggle, setToggle] = useState(false)
  // ...
  // ...

	return (
		<>
			<button type="button" onClick={() => { setToggle(!toggle) }}>
				Toggle animation
			</button>
			<svg>
				// ...
				// ...
			</svg>
		</>
	)
}
```

Now that you have a `toggle` variable to trigger the animation you can start animating. 

For animating an array of elements with `react-spring` you should use the `useSprings` hook. ([Read the documentation](https://www.react-spring.io/docs/hooks/use-springs) to learn more about it.)

To create the animation effect the `transform: translate3d(...)` and `opacity` are used. The value passed to the `translate3d(...)` function or `opacity` is going to change based on the `toggle` variable.

The `useSprings` hook will return an array of animated values which we can map over. 

```jsx
import { useSprings } from 'react-spring'

function Icon() {
	// ...
	// ...

	const envelopes = [
    // ...
    // ...
  ];

	const springs = useSpring(
	  envelopes.length, // the number of springs to create
		envelopes.map(() => {
			return {
				transform: toggle
					// toggle === true -> initial position
          ? `translate3d(0px, 0px, 0px)`
					// toggle === false -> move the envelopes to the left
          : `translate3d(-400px, 0px, 0px)`,
				opacity: toggle ?
					// toggle === true -> full opacity
					1
					// toggle === false -> full transparency
					: 0
			}
		})
  )
  // ...
  // ...

	return (
		<>
			// ...
			// ...
		</>
	)
}
```

After creating the springs you are now ready to map over them! The `springs` variable is an array of so-called "animated values" which you pass to the `style` property of elements wrapped in `animated.g` from `react-spring`. 

Let's see the code excerpt with comments!

```jsx
import { useSprings } from 'react-spring'

function Icon() {
	// ...
	// ...

	const envelopes = [
    // ...
    // ...
  ];

	const springs = useSpring(
	  // ...
	  // ...
  )
  
	const animatedEnvelopes = springs.map((animatedStyle, index) => (
    // use `animated.g` instead of `g` as the `animatedStyle`
    // is a special react-spring flavour of the `style` property
    <animated.g
      key={index} // YOLO
      style={animatedStyle} // apply the animated style
    >
      {envelopes[index]}
    </animated.g>
  ));

	return (
		<>
			// ...
			// ...
			<svg>
				// ...
  			// ...
				{animatedEnvelopes} // use `animatedEnvelopes` instead of `envelopes`
				// ...
				// ...
			</svg>
		</>
	)
}
```

That wraps up this section! You now have the envelopes moving as you press the "Toggle animation" button.

You can find all the code for this section in the CodeSandbox 

{% codesandbox naughty-night-eg602 %}

<a name="chapter-5"></a>
<br />
# How to Make the animation look good?

You can probably see that the animation we created doesn't really look so good.

There are at least 3 problems:

## Problem 1: The Trajectory Is Too Short

The animation trajectory seems to be a bit too short. That's because you can't see the envelopes when they go past the `viewBox` of the `svg`. 

Fortunately, the fix is quite easy. You just need to manually adjust the `viewBox`. 

(The `viewBox` specifies what points are visible in the `svg` element. It's used like this: `viewBox="x0 y0 width height"` where `x0` specifies the left-most point, `y0` specifies the top-most point and `width` and `height` specify the width and height. 🙃)

We need to give the `svg` some extra space on the left, therefore adjust the viewBox from `0 0 733.51 728.97` to `-200 0 933.51 728.97`. (Change the first number to create some space on the left and the third number to **not** crop the right-hand part of the image).

## Problem 2: Envelopes Don't Animate in a Natural Direction

Even though the envelopes are slightly rotated we only animate them in one direction (left-right). It would seem more natural to animate in two directions (left-right + top-bottom). 

How to find out how much vertical movement we should add? You could either fine-tune the `translate` property manually or use a bit of trigonometry. Let's do the latter. 😉

First, let's find out what angle the envelopes are rotated at. You can use the devtools to find that.

![rotation angle rotated in devtools](https://dev-to-uploads.s3.amazonaws.com/i/yj9n87awwgi7cpt91j3q.png)

So the rotation angle is around `83deg`. And the horizontal part of the animation is `400px`. We get the vertical part of the animation as: vertical = `400px * cos(83deg)` which yields around `49px`.

So change the `translate3d(-400px, 0px, 0px)` to `translate3d(-400px, -49px, 0px)` and you'll be good to go.

## Problem 3: Envelopes don't animate individually

Envelopes animate as a group which doesn't really look natural. Creating a staggered effect would make the animation seem much more natural.

Fortunately, it's two lines of code in `react-spring` as we can specify `delay` for each animated value.

```jsx
const springs = useSprings(
    envelopes.length,
    envelopes.map((_, i) => {
      return {
        // ...
        // ...
				// as we map over the envelopes, increase the delay
				// first envelope -> delay: 0ms
				// second envelope -> delay: 100ms
				// etc.
        delay: i * 100,
      };
    })
  );
```

(It's worth noting that you could also use the `useTrail` hook instead of `useSpring` with delay to create a staggered effect.)

Yay! 😲The animation now looks much better! You can find the source code in the CodeSandbox below:

{% codesandbox empty-firefly-lllum %}

<a name="chapter-6"></a>
<br />
# How to Create More Advanced Animations?

This is just the beginning—animating the using the `translate3d(...)` property is quite similar in SVG and HTML. However, things get more tricky if you try to animate `scale`, `rotate` properties.

If you're interested, you can follow me here or/and [check out the YouTube channel](https://www.youtube.com/channel/UCE7h4of6ywpAG87KXHV6UrQ) where I'll be posting the tutorials.