Animated Progress Bar With Only SVGs
Tags: animation, svgs, scss, react
Published: 2021-06-06 20:33:41

Have you ever wondered how to make [one of those neat little progress bars](https://dribbble.com/shots/3198381-Progress?utm_source=Clipboard_Shot&utm_campaign=Volorf&utm_content=Progress&utm_medium=Social_Share&utm_source=Clipboard_Shot&utm_campaign=Volorf&utm_content=Progress&utm_medium=Social_Share) you see in so many loading interfaces?

A little while back I talked about how we can build the other category of loading animations, a spinner. Today, I want to show how you can use the power of SVGs yet again to create the simplest progress bar you've ever seen to date. No double layered divs filling, no glitches. Plain ol' React and scss.

What would be needed for something like this? Much like the loading spinner we need some data to wait on. I'll fake that part for tutorial purposes. While we're waiting, we can launch a keyframe animation to fill the svg bar. Is the waiting done? Put a 'completed!' message of your choosing. 

Ok, so what does this look like in code speak. For the structure we have:

```javascript
import React, { useState, useEffect } from 'react';

export const Progress = () => {
  const [loading, hasLoaded] = useState(false);

  useEffect(() => {
    const timer = setTimeout(() => {
      fetch('https://jsonplaceholder.typicode.com/posts')
        .then((response) => response.json())
        .then((json) => {
          hasLoaded(true);
        });
    }, 1550);

    return () => clearTimeout(timer);
  }, []);

  return (
    <div className={`progress ${loading ? "progress-success" : "progress-failure"}`}>
      {loading ? (
        <p className="ending-msg">Loaded!</p>
      ) : (
        <svg role="alert" aria-live="assertive">
          <rect x="1" y="1" className="border" rx="15" ry="15" />
          <rect x="1" y="1" className="filling" rx="15" ry="15" />
        </svg>
      )}
    </div>
  );
};
```

Brief overview of what's happening:
1. We create a state called `loading` to check whether we are waiting for data or the waiting has ended. This only need be a boolean, because either the progress bar is loading or not. It initially starts off as false, because we haven't requested any data yet.
2. `useEffect` is where we can check the loading state and show our progress bar animation if the loading state is true. The sample fetch request is just there to illustrate this point, as you will likely be waiting on some event when using a progress bar. The `setTimeout` is just to mock the waiting time, in reality you would not need this since the program would naturally be waiting on whatever it is you need the bar for.
3. If you do end up using the setTimeout, you might be wondering why I choose 1550 milliseconds. This number depends on however long the `fillBar` animation is running for, which we will see in the styles. Basically, it needs to be at least 50 milliseconds longer than the `fillBar` animation. You can update this or remove the `setTimeout` entirely.
4. In terms of what we end up rendering, we have one div that holds everything. Inside, we have a conditional for our two states: If the content has loaded already, print an ending message. If the content has not yet loaded, then keep the progress bar animation going.
5. The bar is split into two rectangle svgs, one defining its outline and the other defining its border. We also set the radius to make it a little more curved and define attributes like `height` and `width`. Everything else will be handled in the styles.

Speaking of which, let's make the magic happen:

```scss
.progress-success {
  .ending-msg {
    color: green;
  }
}

.progress-failure {
  .ending-msg {
    color: red;
  }
}

.progress {
  .border {
    width: 200px;
    height: 30px;
    stroke-width: 2px;
    stroke: blue;
    fill: transparent;
  }
  
  .filling {
    width: 200px;
    height: 30px;
    fill: blue;
    animation: fillBar 1.5s ease-in-out 0s normal 1 forwards;
  }
  
  @keyframes fillBar {
    0% {
      width: 0;
    }
    30% {
      width: 80px;
    }
    70% {
      width: 120px;
    }
    100% {
      width: 200px;
    }
  }
}
```

Here we are doing a good amount of setting colors, border widths, etc. But what's actually making the animation run is the `fillBar` keyframe we have running on the svg with the className of `.filling`. In `fillBar`, we define the progression of how we want to fill the progress bar by simply increasing the width of the svg, which by being inside of the svg with a class name of `.border` gives the illusion that it's filling the bar. The actual speed and transition is defined by the way we call the keyframe, seen in the `animation` property.

Now let's see it completed. Click on the 'rerun' button on the bottom right to see it again.

{% codepen https://codepen.io/singhshemona/pen/jOBxYgR %}

Now you know how to make a smooth progress bar with just a bit of smart svg manipulation. Play around with timings and styles to really jazz up your animation!