DEV Community

Cover image for I Found The CLEANEST Loading Animation EVER!
Technophile
Technophile

Posted on

I Found The CLEANEST Loading Animation EVER!

I recently noticed that Netlify has the most minimalist loading animation I have ever seen. So, I decided to build it myself and share it with you.

Looking at the loading animation in Netlify website, we can see that it uses 4 characters (|, /, -, and \) to create the illusion of spinning effect.

Original loading animation in Netlify

That means, we can store the characters in array and repeatedly cycle through each one of them. We also need to find the perfect sweet point for our animation timing and speed create that cool spinning effect.

Ok, let's get started.


First, let's setup the loader in HTML. It's just a div with an id of "loader":

<div id="loader"></div>
Enter fullscreen mode Exit fullscreen mode

Now, with CSS, let's center it in the screen and increase the size:

body {
  display: grid;
  min-height: 100vh;
  place-items: center;
  margin: 0;
  font-size: 5rem;
}
Enter fullscreen mode Exit fullscreen mode

Done. Now onto JavaScript.

First, let's get the loader element from HTML and store it in a variable. We also need to create an array to store the 4 characters for our animation. I'm using letters here to see the animation difference better.

const loader = document.getElementById("loader");
const characters = ["a", "b", "c", "d"];
Enter fullscreen mode Exit fullscreen mode

Now, whenever you need to animate something in JavaScript, you can use this thing called requestAnimationFrame(). Basically, it tells the browser that we want to perform an animation. I will explain how.

Let's create a function, called animate(). Inside, we run the requestAnimationFrame() and pass animate itself as an argument. And, finally, let's call the animate() function itself at the end. Because requestAnimationFrame() runs only once, with this technique we create an infinite loop.


const animate = () => {
  requestAnimationFrame(animate);
};

animate();
Enter fullscreen mode Exit fullscreen mode

Right now, nothing seems to be happening. But, animate() function is continuously running in the background.

Now, to see it, let's create a variable, called frame, set it to 0, and increment it every time animate() function runs.

If we console.log it, we can see the number incrementing. It's quite fast!

let frame = 0;

const animate = () => {
  console.log(frame);

  frame++;
  requestAnimationFrame(animate);
};

animate();
Enter fullscreen mode Exit fullscreen mode

frame variable incrementing in the console

Now, let's update our loader to show each character as animate() function runs.

const loader = document.getElementById("loader");
const characters = ["a", "b", "c", "d"];
let frame = 0;

const animate = () => {
  loader.innerHTML = characters[frame % 3];

  frame++;
  requestAnimationFrame(animate);
};
Enter fullscreen mode Exit fullscreen mode

Here, I'm using % sign (also known as modulo operator) to cycle through the array elements. I'm using 3, since the last index of the array is 3. That way, we always stay within the array.

Array Index

Looking at the output, it's working but a little too fast.

Do you remember, when we console.loged the frame variable, it was incrementing so fast? Basically, what is happening is that we're changing the loader character every frame. If, let's say, we manage to update it every 10 frame or 20 frame, it would slow down the animation speed.

const loader = document.getElementById("loader");
const characters = ["a", "b", "c", "d"];
let frame = 0;

const animate = () => {
  if (frame % 10 === 0) {
    loader.innerHTML = characters[frame % 3];
  }

  frame++;
  requestAnimationFrame(animate);
};

animate();
Enter fullscreen mode Exit fullscreen mode

Now, there'e an issue with our code. Because we're updating every 10 frames (10, 20, 30 and etc), characters[frame % 3] no longer cycles the array correctly.

So, I found a easier and better approach to solve this.

Let's create a separate variable, called index that track which character we're showing. And, if the index gets bigger than our array length, we reset it to 0.

const loader = document.getElementById("loader");
const characters = ["a", "b", "c", "d"];
let frame = 0;
let index = 0;

const animate = () => {
  if (frame % 10 === 0) {
    if (index >= characters.length) index = 0;
    loader.innerHTML = characters[index];
    index++;
  }

  frame++;
  requestAnimationFrame(animate);
};

animate();
Enter fullscreen mode Exit fullscreen mode

Now, we can freely chose when to update the content, and not worry about characters not showing up correctly.

Try different frame intervals to control the speed - higher values slow down the animation and lower values speed it up.

Now, if we change the letters back to characters, we get the same result as we saw with Netlify example.

const loader = document.getElementById("loader");
const characters = ["|", "/", "-", "\\"];
let frame = 0;
let index = 0;

const animate = () => {
  if (frame % 10 === 0) {
    if (index >= characters.length) index = 0;
    loader.innerHTML = characters[index];
    index++;
  }

  frame++;
  requestAnimationFrame(animate);
};

animate();
Enter fullscreen mode Exit fullscreen mode

Final result

And, that's it. Hope you learned something new.

As always, thanks for reading and I'll see you in the next one.

Top comments (5)

Collapse
 
jess profile image
Jess Lee

fun!

Collapse
 
kooiinc profile image
KooiInc • Edited

That's the way we used to make (command line) spinners back in the days (some 30 years ago ;)

But maybe you don't need javascript: here is a css only idea for you. Cheers!

Collapse
 
charlesloder profile image
Charles Loder

And you can still get the jagged looking effect like this using steps()

.spinnerBlock {
  position: absolute;
  border-left: 15px solid green;
  height: 100px;
  left: 50%;
  margin-top: 1rem;
  transform: translate(-50%);
  animation: spinner 1s steps(4, start) infinite;
}

@keyframes spinner {
  to {
    transform: rotate(180deg);
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
technoph1le profile image
Technophile

Didn't think about that one. I was wondering how to implement it. Thanks :)

Collapse
 
technoph1le profile image
Technophile

New ideas are always appreciated. I'm also thinking of doing it with Sass, perhaps store 4 characters in a map and loop through them. 🤔

Some comments may only be visible to logged-in visitors. Sign in to view all comments.