web
12th April 2023
by Sebastian Greifeneder

Creating an animatable version of a progress bar as an overlay which can be utilized on top of any child content.

Introduction

A radial progress bar can be used to visually represent progress towards completing a task or reaching a goal. It typically consists of a circular track, which represents the full amount of progress required, and a colored fill that represents the progress that has been made so far. The fill gradually fills up the track as the user gets closer to their goal.

If you're looking for a classic radial progress bar, you can easily find prebuilt packages and samples all over the web. However, for our current project, we required a slightly modified, animatable version of the progress bar. We call it a radial progress overlay, which can be utilized on top of any child content (see animation below).

In this blog post, we want to share the journey to the final result which we implemented using only CSS, and all the challenges we faced.

Radial Progress Animation

Overlays and the mix-blend-mode

Overlaying on top of child content can be done with the CSS mix-blend-mode property:

CSS mix-blend-mode is a property that allows you to specify how an element should blend with its background and neighboring elements. This property is used to create various visual effects such as transparency, color effects, and text effects.

We use mix-blend-mode: multiply to overlay the child with some shadowy progress. Our first draft included the progress states 25%, 50%, 75% and 100% which can be easily accomplished with rectangles:

Simple Progress States Overlay

Check out our codepen to see the full implementation.

Immediate progress steps

To make immediate progress, we are seeking a rotated overlay that can be used in conjunction with simple overlays. The simple overlays will serve to either fill in any missing gaps (addition) or remove any unwanted areas (subtraction).

The rotated overlay can be positioned with a mix of translate() and rotate() CSS functions and by specifying the anchor point via transform-origin. We will also use a custom CSS property called --ratio which will be used within the rotate function to derive the desired angle. Ratio should be a number between 0 and 1 to represent a progress between 0% and 100%.

Progress from 0% to 50%

Progress from 0% to 50% can be accomplished with the following steps (overlay colors are different for better visualization):

  • Start with the desired content
  • Overlay the complete area with the desired overlay color (turquoise)
  • Rotate a white overlay with the desired progress on top (pink)
  • Overlay the left half of the image with the desired overlay color, to remove the unwanted area from the rotated overlay (yellow)

Intermediate progress with subtraction

Progress from 50% to 100%

Progress from 50% to 100% looks a bit different:

  • Start with the desired content
  • Overlay the complete area with the desired overlay color (turquoise)
  • Rotate a white overlay with the desired progress on top (pink)
  • Overlay the right half of the image with a white overlay, to fill missing gaps from the rotated overlay (orange)

Intermediate progress with subtraction

Check out our codepen to see the full implementation.

Progress animation

While we can now hardcode a desired progress, there is still a missing part if we want to make the whole thing animatable. When the progress is between 0 - 50% or 50 - 100%, we need to switch from subtractive to additive overlays (as shown in the example above) during the animation. Although this would be a simple task with conditional statements (If/Else) in JavaScript, we attempted to find a solution using only CSS. However, since CSS does not have conditional statements, we implemented a workaround using an auxiliary variable. This variable will alternate between 0 and 1 (based on the ratio) and multiplied by the width/height to hide (multiply by 0) or show (multiply by 1) the overlay. We use 2 variables for that: --width-flip and --height-flip.

The change between 0 and 1 is done with implicit rounding using a CSS custom property with type integer explained here. Unfortunately, this doesn't work properly in all browsers, e.g. Firefox doesn't treat these properties as real integers and uses decimals instead, which falsifies the calculation. In the future we could switch to a native CSS rounding function, but most of the browsers don't support it yet. An alternative would be to omit the CSS solution and use JavaScript or calculate the value server-side.

Samples

See how the animation looks like in action by adjusting the --ratio value while hovering over the image on our Codepen examples. For these examples, the initially set ratio animates between 10% and 90%.

Overlay Overlay (inverted) Overlay (iOS App Style)
Radial Progress Animation Radial Progress Animation Radial Progress Animation