Welcome to the Widget of the Week series, where I take gifs or videos of awesome UI/UX components, and bring them to life with code.

This week is the turn for a progress indicator or “Wizard” component, that can help you with the on-boarding process of your app. The inspiration comes from this uimovement submission and looks like this:

We now have a button that has a hover animation that swaps colors between the green background and the white font. Also notice the 25px border radius property, it is going to be really important when we need our button to become a circle.

The interaction logic

Before we initialize the Vue instance, I’d like to check what are the states of the button. We can ignore the hover because that’s already solved by our CSS, that leaves us with three states: clicked, loading and loaded. To handle those we can start with something like this:

You might ask “Why three booleans and not a single string or number with the 3 values?”, and the reason is because they’re not mutually exclusive, in other words, the button can be ‘clicked’ and also ‘loading’ at the same time.

The click interaction

In preparation for the click animation we need first to create a CSS rule for the button, when it is clicked it transforms into a circle, to be precise a 50px by 50px circle (remember the 25px border radius?). The problem is that it already has a padding declared, and also we need to compensate for the border of the button, so we will need a little bit of math:

The loading

Just after our button transforms into a circle we need to put on top our SVG circles, why?, because HTML borders can’t be animated the way we need them to, but SVG can!
Now let’s match the position of the circles with this CSS:

We need to know when the button animation ends so we can start the loading animation, according to MDN web docs we can use the ‘transitionend’ event.
To add a listener to that event in Vue, we need to have a reference to the submit button, let’s add this line to our button HTML:

That will set the loading flag to true and remove the previously added listener.

Animating the green circle

For the next part we will use a SVG animation trick using the stroke-dasharray and stroke-dashoffset properties.
For the trick to work, the stroke-dasharray must have as a value the circumference of the circle, to calculate it we can go back to our geometry class notebook and see that the formula is pi times the diameter of the circle.
Ours is 50px width, so it will be 3.1416 * 50 = ~157. Also we will bind the stroke-dashoffset to a new Vue data variable:

After doing that, we can start animating the loader with TweenLite.
We use the TweenLite.to() method to interpolate the loaderOffset property from its initial value to zero in two seconds.
When it finishes animating, the onComplete hook will execute the completeLoading method where we set the loading and loaded properties: