Creating an animatable version of a progress bar as an overlay which can be utilized on top of any child content.
Creating an animatable version of a progress bar as an overlay which can be utilized on top of any child content.
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.
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:
Check out our codepen to see the full implementation.
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% can be accomplished with the following steps (overlay colors are different for better visualization):
Progress from 50% to 100% looks a bit different:
Check out our codepen to see the full implementation.
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.
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) |