I made a React progress bar AND SVG circle! #nailedIt
I was recently tasked with building a progress bar to allow the users of the application I work on to visually see the progress of uploading a file. I had never made one before but after learning how simple they are, I also took an additional step forward and attempted a progress circle using the same thought process. Below I’ll show you the basic idea of what I came up with.
I want to keep this as basic as possible so this is the super stripped down version of what I came up with:
react-progress.jsx
import React, { Component } from 'react';
import styles from './react-progress.css';const ReactProgress = ({ percentage }) => {
return (
<div className='ReactProgress_wrapper'>
<div
className='ReactProgress_filler'
style={{ width: `${percentage}%` }}
/>
</div>
);
};export default ReactProgress;
react-progress.css
.ReactProgress_wrapper {
background: #F5F5F5;
position: relative;
height: 0.5rem;
width: 100%;
}.ReactProgress_filler {
background: #7FFFD4;
height: 100%;
transition: width 0.8s ease-in;
}
What’s going on?: Even if you’re new to React, it is pretty easy to see what is going here.
Think of ReactProgress_wrapper
as the background AND housing unit to ReactProgress_filler
. It’s getting a light grey background and is going to house the filler with the position: relative
set. Also, we have the height set and the width will take up the length of whatever we decide to put it in later. 👍
ReactProgress_filler
is going to be the part of the loader that will indicate progress to our user! For this example, I gave it a blue background. It will take up the height of the wrapper and smoothly transition to new percentages when updates happen.
The magic happens, however, from the passing of the percentage
prop on the ReactProgress
component. Every time a new percentage is passed to the ReactProgress
component, the percentage
prop will be passed to the ReactProgress_filler
div’s width
!
As you can tell, this was a pretty quick and easy learn so I thought I’d go a step further and try to create a progress circle with my new knowledge!
react-progress-circle.jsx
import React, { Component } from 'react';
import styles from './react-progress-circle.css';const ReactProgressCircle = ({ percentage, size }) => {
let appliedRadius;
let appliedStroke; switch (size) {
case 'xs':
appliedRadius = 10;
appliedStroke = 1;
break;
case 'sm':
appliedRadius = 25;
appliedStroke = 2.5;
break;
case 'med':
appliedRadius = 50;
appliedStroke = 5;
break;
case 'lg':
appliedRadius = 75;
appliedStroke = 7.5;
break;
case 'xl':
appliedRadius = 100;
appliedStroke = 10;
break;
default:
appliedRadius = 50;
appliedStroke = 5;
} const normalizedRadius = appliedRadius - appliedStroke * 2;
const circumference = normalizedRadius * 2 * Math.PI;
const strokeDashoffset =
circumference - (percentage / 100) * circumference;return (
<div id="react-progress-circle">
<svg height={appliedRadius * 2} width={appliedRadius * 2}>
<circle
className='ReactProgressCircle_circleBackground'
strokeWidth={appliedStroke}
style={{ strokeDashoffset }}
r={normalizedRadius}
cx={appliedRadius}
cy={appliedRadius}
/>
<circle
className='ReactProgressCircle_circle'
strokeWidth={appliedStroke}
strokeDasharray={circumference + ' ' + circumference}
style={{ strokeDashoffset }}
r={normalizedRadius}
cx={appliedRadius}
cy={appliedRadius}
/>
</svg>
</div>
);
};export default ReactProgressCircle;
react-progress-circle.css
.ReactProgressCircle_circle {
fill: transparent;
stroke: #7FFFD4;
}.ReactProgressCircle_circleBackground {
fill: transparent;
stroke: #F5F5F5;
transition: stroke-dashoffset 0.8s;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
What’s going on?:
The basics of what was discussed on the regular loading bar are the same here. The overlying concept is in the two SVG <circle />
elements. We have one, underlying circle that is basically the background for the other one.
I’m not going to get too in depth on what the attributes to the circle
each do but basically what is happening is I’ve provided a size
prop for the component user to take advantage of and not have to set radius
and stroke
on their own. After selecting either “xs”, “sm”, “med”, “lg”, or “xl”, the circumference of the circle is set.
(Totally had to look up how to get the circumference of a circle btw…) 🙃
The strokeDashoffset
is what sets the percentage that will be recognizable by the user.
Where I’m going to continue from here:
Feels like it should be pretty easy by this point to make an animated loading spinner right?!
RIGHT!