The Easiest Way to Animate a Loading Spinner
Shemona Singh
Posted on February 28, 2021
While on a quest to learn how to build some of the most commonly requested animations by designers, the loading spinner seems like a rite of passage.
This time around, I wanted to see if I could use the awesome power of svgs to draw out a circle then animate that circle. This could be a lot cleaner than attempting to animate borders or place rotating circles on top of other circles in CSS.
We will be building today's spinner here with React. Thinking in terms of states, there are two main ones. We are either:
- Waiting for something - show the spinner
- That something has happened - no longer show the spinner
To make this feel more realistic, we will have the spinner wait for a response from the Fetch api. There are plenty of open apis for us to request from for the sake of this tutorial.
Take a look at the set up for our component.
import React, { useState, useEffect } from 'react';
import './Loading.scss';
export const Loading = () => {
const [loading, hasLoaded] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((json) => {
hasLoaded(true);
});
}, 1100);
return () => clearTimeout(timer);
}, []);
return (
<div className="spinner-container">
{loading ? (
<p>Content has loaded!</p>
) : (
<svg className="spinner" role="alert" aria-live="assertive">
<circle cx="30" cy="30" r="20" className="circle" />
</svg>
)}
</div>
);
};
Let's walk through what's going on here.
- First, we set up the two states I mentioned at the beginning. The only two states the spinner can be in: either we are waiting for something to happen, or it has already happened. A simple boolean does the trick.
- The handy
useEffect
hook is where we can handle what it is that we're waiting for. It's likely you'll be waiting for some data to return, so I've set up a sample fetch request. You may also notice I have it wrapped inside of asetTimeout
. This is because the information comes far too fast for us to see the spinner in action, so for the purposes of delaying our response I've added asetTimeout
that you're welcome to adjust in order to see the spinner for longer. I have it set to 1100 milliseconds so that we can see the spinner for at least a second. In reality, you might not need asetTimeout
since the data you'll be requesting will likely take it's own time. - In the return of the
useEffect
, I clean up thesetTimeout
like the responsible developer I am. 😇 - Now for the actual component. We have one
div
that holds everything. Inside, we set our two states: If the content has loaded already, print something that tells us this. If the content has not yet loaded, this is where we render our animated spinner. - The spinner is a simple
circle
tag wrapped inside of ansvg
tag. We define some attributes like height and width, as well as those that will make it accessible likearia-live
androle
.
Ok! We have the skeleton of a spinner. But, there's nothing to see yet. The styles are where the actual magic happens. Let's take a look at them:
.spinner-container {
.spinner {
transform: rotate(-90deg);
width: 60px;
height: 60px;
.circle {
stroke-linecap: round;
stroke-dasharray: 360;
stroke-width: 6;
stroke: blue;
fill: transparent;
animation: spin .6s ease-in-out 0s normal infinite;
}
}
@keyframes spin {
from {
stroke-dashoffset: 360;
}
to {
stroke-dashoffset: 40;
}
}
}
Now, let's walk through the styles.
- We have the
.spinner-container
wrapped around everything. Pretty straightforward. - The
svg
gets a class of.spinner
with height and width specified as well as the rotation that will occur while being animated. - The
.circle
class is where I first define some stylistic qualities to the actual circle and then theanimation
property is the key to it's movement. I set it to be the keyframe animation namedspin
, which is simply pulling the filling of the circle forward.
Here is what it all looks like in action. Be sure to hit the 'rerun' button on the bottom right.
Voila! Just a few lines of scss to make the spinner come to life. Years ago before I knew this handy tactic of manipulating and animating the fill of svgs, I had built a different spinner. It used bulky, confusing styles to make the drawing of the border for a circle div seem fluid.
Makes you question coding patterns you might be unconsciously following now that could be done more efficiently. 🤔
Posted on February 28, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.