Building a Progress Ring, Quickly
On some particularly heavy sites, the user needs to see a visual cue temporarily to indicate that resources and assets are still loading before they taking in a finished site. There are different kinds of approaches to solving for this kind of UX, from spinners to skeleton screens. If we are using an out-of-the-box solution that provides us the current progress, like preloader package by Jam3 does, building a loading indicator becomes easier. For this, we will make a ring/circle, style it, animate given a progress, and then wrap it in a component for development use.
Step 1: Let’s make an SVG ring
From the many ways available to draw a circle using just HTML and CSS, I’m choosing SVG since it’s possible to configure and style through attributes while preserving its resolution in all screens.
Inside an
radius = (width / 2) - (strokeWidth * 2)
These means that if we increase the stroke to 4, then the radius should be 52.
To complete the ring we need to set fill to transparent and choose a stroke color for the circle. See the Pen SVG ring by Jeremias Menichelli (@jeremenichelli) on CodePen.
The next step is to animate the length of the outer line of our ring to simulate visual progress. We are going to use two CSS properties that you might not have heard of before since they are exclusive to SVG elements, stroke-dasharray and stroke-dashoffset .
This property is like border-style: dashed but it lets you define the width of the dashes and the gap between them.
With those values, our ring will have 10px dashes separated by 20px. See the Pen Dashed SVG ring by Jeremias Menichelli (@jeremenichelli) on CodePen.
The second one allows you to move the starting point of this dash-gap sequence along the path of the SVG element. Now, imagine if we passed the circle’s circumference to both stroke-dasharray values. Our shape would have one long dash occupying the whole length and a gap of the same length which wouldn’t be visible. This will cause no change initially, but if we also set to the stroke-dashoffset the same length, then the long dash will move all the way and reveal the gap. Decreasing stroke-dasharray would start to reveal our shape. A few years ago, Jake Archibald explained this technique in this article, which also has a live example that will help you understand it better. You should go read his tutorial.
What we need now is that length which can be calculated with the radius and this simple trigonometric formula.
circumference = radius * 2 * PI
const circle = document.querySelector('.progress-ring__circle'); const radius = circle.r.baseVal.value; const circumference = radius * 2 * Math.PI;
circle.style.strokeDasharray = `$ $`; circle.style.strokeDashoffset = circumference;
Step 3: Progress to offset
With this little trick, we know that assigning the circumference value to stroke-dashoffset will reflect the status of zero progress and the 0 value will indicate progress is complete. Therefore, as the progress grows we need to reduce the offset like this:
function setProgress(percent) < const offset = circumference - percent / 100 * circumference; circle.style.strokeDashoffset = offset; >
One particular thing about stroke-dashoffset , its starting point is vertically centered and horizontally tilted to the right. It’s necessary to negatively rotate the circle to get the desired effect.
Putting all of this together will give us something like this. See the Pen vegymB by Jeremias Menichelli (@jeremenichelli) on CodePen. A numeric input was added in this example to help you test the animation. For this to be easily coupled inside your application it would be best to encapsulate the solution in a component.
Now that we have the logic, the styles, and the HTML for our loading ring we can port it easily to any technology or framework. First, let’s use web components.
class ProgressRing extends HTMLElement window.customElements.define('progress-ring', ProgressRing);
This is the standard declaration of a custom element, extending the native HTMLElement class, which can be configured by attributes.
Inside the constructor of the element, we will create a shadow root to encapsulate the styles and its template.
constructor() < super(); // get config from attributes const stroke = this.getAttribute('stroke'); const radius = this.getAttribute('radius'); const normalizedRadius = radius - stroke * 2; this._circumference = normalizedRadius * 2 * Math.PI; // create shadow dom root this._root = this.attachShadow(); this._root.innerHTML = ` circle `; >
You may have noticed that we have not hardcoded the values into our SVG, instead we are getting them from the attributes passed to the element. Also, we are calculating the circumference of the ring and setting stroke-dasharray and stroke-dashoffset ahead of time. The next thing is to observe the progress attribute and modify the circle styles.
setProgress(percent) < const offset = this._circumference - (percent / 100 * this._circumference); const circle = this._root.querySelector('circle'); circle.style.strokeDashoffset = offset; >static get observedAttributes() < return [ 'progress' ]; >attributeChangedCallback(name, oldValue, newValue) < if (name === 'progress') < this.setProgress(newValue); >>
Here setProgress becomes a class method that will be called when the progress attribute is changed. The observedAttributes are defined by a static getter which will trigger attributeChangeCallback when, in this case, progress is modified. See the Pen ProgressRing web component by Jeremias Menichelli (@jeremenichelli) on CodePen. This Pen only works in Chrome at the time of this writing. An interval was added to simulate the progress change.
Web components are great. That said, some of the available libraries and frameworks, like Vue.js, can do quite a bit of the heavy-lifting. To start, we need to define the view component.
const ProgressRing = Vue.component('progress-ring', <>);
Writing a single file component is also possible and probably cleaner but we are adopting the factory syntax to match the final code demo. We will define the attributes as props and the calculations as data.
const ProgressRing = Vue.component('progress-ring', < props: < radius: Number, progress: Number, stroke: Number >, data() < const normalizedRadius = this.radius - this.stroke * 2; const circumference = normalizedRadius * 2 * Math.PI; return < normalizedRadius, circumference >; > >);
Since computed properties are supported out-of-the-box in Vue we can use it to calculate the value of stroke-dashoffset .
Next, we add our SVG as a template. Notice that the easy part here is that Vue provides us with bindings, bringing JavaScript expressions inside attributes and styles.
template: ` `
When we update the progress prop of the element in our app, Vue takes care of computing the changes and update the element styles. See the Pen Vue ProgressRing component by Jeremias Menichelli (@jeremenichelli) on CodePen. Note: An interval was added to simulate the progress change. We do that in the next example as well.
In a similar way to Vue.js, React helps us handle all the configuration and computed values thanks to props and JSX notation. First, we obtain some data from props passed down.
class ProgressRing extends React.Component < constructor(props) < super(props); const < radius, stroke >= this.props; this.normalizedRadius = radius - stroke * 2; this.circumference = this.normalizedRadius * 2 * Math.PI; > >
Our template is the return value of the component’s render function where we use the progress prop to calculate the stroke-dashoffset value.
render() < const < radius, stroke, progress >= this.props; const strokeDashoffset = this.circumference - progress / 100 * this.circumference; return ( ); >
A change in the progress prop will trigger a new render cycle recalculating the strokeDashoffset variable. See the Pen React ProgressRing component by Jeremias Menichelli (@jeremenichelli) on CodePen.
The recipe for this solution is based on SVG shapes and styles, CSS transitions and a little of JavaScript to compute special attributes to simulate the drawing circumference. Once we separate this little piece, we can port it to any modern library or framework and include it in our app, in this article we explored web components, Vue, and React.
Circular Progress Bar using HTML and CSS
Circular Progress Bar is a popular web element that is mainly used on business or personal websites. If you want to create a circular progress bar using HTML and CSS, then this article will help you. Here I am going to show you how to make a simple CSS circle progress bar. When you load the page, this animation will go from zero to your assigned meaning. A percentage of text is used here, but no animation is used in this text. It is made in a very simple way. ✅ Watch Live Preview 👉👉 Circular Progress Bar In this article, I will show you step by step how I made this circular progress bar design.To make it, you need to have an idea about basic HTML and CSS.
Step 1: The basic structure of Circular Progress Bar
I have used HTML and CSS to create the basic structure of the Circular Progress Bar. I have created a small circle on the webpage. The width and height of this circle are 150 px. The background color of the circle is white and margins are used to place it in the middle.
class="circle-wrap"> class="circle">
body font-family: "Roboto", sans-serif; background:#d2eaf1; > .circle-wrap margin: 150px auto; width: 150px; height: 150px; background: #fefcff; border-radius: 50%; border: 1px solid #cdcbd0; >
Step 2: Half of the simple CSS circle progress bar
As I said before, the animation in this circle is divided into two parts. This means that the animation has been divided into two parts up to the customer value. I have made the first part of those two parts. Width and height 150 have been used to make this animation equal in size to the Circular Progress Bar. Similarly, border-radius 50% has been used to make it round. Here I have set the background-color to blue. If you want to play an animation in another color, you can use that color here.
class="mask half"> class="fill">