Today we’d like to share an interesting progress button effect with you. The effect is based on a very nice Dribbble shot called “Download” by xjw. The button starts as an icon with an arrow and once it’s clicked, it animates into a fun little wire and a label that indicates the download percentage. Take a look:

In this article, we’ll take a look at some aspects of what is needed to make an animation like this, some techniques, and some challenges we might face up.

If you would like to use this loader in your project, go to the Github page and follow the instructions.

Technology

The first thing we have to decide is which browser technology we will use to make and render the effect.

It would be possible for the most part to make this animation with CSS. The problem is that it doesn’t offer much flexibility – drawing a curve, for example, can be hard, if not impossible, to get it exactly how we want it to be. On top of that, it can be pretty hacky – we have to rely on faking shapes with clever tricks, and that can get confusing and ugly.

Canvas can be a good option. We can draw any shape we want on it, and the performance, if you’re careful, is very good, even on mobile. It can be difficult to use though, especially if you’re not using a library like Paper.js. Moreover, it is pretty much a black box in the eyes of the browser – it just sees it as an image and ignores its contents.

Our other option here is SVG. It does have its drawbacks: it’s not currently hardware accelerated, which makes the performance less than stellar. It’s also quite old and, as a result, full of quirks. However, it’s pretty flexible, easy to load on a project, and the browser treats each of its elements like a DOM object, which makes it better suited for interaction.

Besides, its drawbacks can be alleviated: The performance is not too bad if we are animating small objects, and many of the quirks are sorted out by GSAP, which is the animation library we are going to be using. Besides being a very good animation library, it provides tools to make it much easier to deal with SVGs in general. We’ll see more on that later.

Planning the SVG

When we create an SVG for animating, we have to make everything thinking on how we will animate it later. This, we have to take extra care with things like layer names, vertices, and so on.

This is how I organized the layers in this example. I’m using Adobe Illustrator, but it should be pretty similar in the editor of your choice.

The important thing to take out here, besides naming, is grouping. If you intent to animate certain elements together, you should collect them in the same layer to avoid having to run the same animation twice for different objects.

Now, since in this demo we are animating vertices individually, we should take care of them as well.

For the elastic animation we want to achieve, we need a line with a smooth curve point in the middle. However, we also want the round button to transform into the line, so we need a line that accommodates both shapes, with the lowest number of vertices possible, in order to make it easy to animate.

This is a not-perfect-but-good-enough-for-animation circle with 3 vertices, that we can open up to turn into our elastic thing.

We can turn that loose curve into its more tense shape by just keeping the bezier control points close to the vertices.

All this is just an example. Your requirements will vary depending on the animation you want to make. What’s important is, when you are making the graphics, to think about how you will animate them.

Manipulating

First, you need to load the SVG in a way you can manipulate it with JS. You can paste it directly into the HTML, you can load it using AJAX or a library like Snap.svg, or what have you.

Animating SVG elements with GSAP is pretty simple. After you select the element…

var circle= document .querySelector( "#background" );

…you can just animate properties like x , y , and scale that it will generate the proper transform .

TweenMax.to(circle, 1 ,{ scale: 0.1 })

For things that aren’t transform , you just use the attr property:

TweenMax.to(circle, 1 ,{ attr:{ "stroke-width" : 4 } })

For our animation, something that will come in handy is GSAP’s elastic easing, which is configurable by its easeParams property.

See the Pen GSAP’s Elastic easeParams demonstration by Lucas Bebber (@lbebber) on CodePen

Another very useful thing that it does is to smooth out browser inconsistencies on transformOrigin for SVGs.

TweenMax.to(circle, 1 ,{ transformOrigin: "50% 50%" , scale: 0.1 })

Since I’m talking about browser problems: as of now, Firefox throws an error if you try to animate or set a property, like transformOrigin , of a hidden object. Be sure to set the object’s display property to inline instead of none before changing anything else.

Animating Vertices

There are a number of tools you can use to select and animate individual SVG vertices, like Snap.svg I mentioned before. In this demo, I used svg-pathdata. It receives an SVG path string – the d attribute of a path element, which contains information about the path’s shape – and returns an array of commands , which are the the individual points. You can then change the values as you like, then parse the array back to an SVG path string and apply it back to the d attribute.

It looks a bit confusing, but it’s pretty simple once you wrap your head around it. Here’s an example:

var line= document .querySelector( '#line path' ); var d=line.getAttribute( 'd' ); var pathData= new SVGPathData(d).toAbs(); var points=pathData.commands; var middlePoint=points[ 1 ]; TweenTo(middlePoint, 1 ,{ y: 0 , y1: 0 , onUpdate:updatePath, onComplete:updatePath }); function updatePath () { line.setAttribute( 'd' ,pathData.encode()); }

To animate multiple vertices of the same path, in order to avoid updating the path multiple times, keep the onUpdate function on a separate tween.

Tween.to(points[ 0 ], 1 ,{ x:- 100 }); Tween.to(points[ 1 ], 1 ,{ x: 0 }); Tween.to(points[ 2 ], 1 ,{ x: 100 }); Tween.to( this , 1 ,{ onUpdate:updatePath }) ...

Interaction

If you want to make a button like this, you might want to avoid having the whole SVG, including the empty areas, clickable – you want only the button to be a button. Fortunately, the solution is easy.

For the container of the SVG, you set the following CSS property:

pointer-events : none;

Then, for the SVG element you want to be clickable, you set:

pointer-events : fill;

That can be done by JS, by the setAttribute function, or straight in the SVG file itself, either as a CSS rule, an attribute of the element, or on its style attribute.

A nice technique is to keep a separate transparent object on top of everything else only to be used as a hit area. That way, you can animate the objects beneath it with no problem.

Accessibility

Since this is not a proper button nor a proper progress bar, it’s invisible to screenreaders and everything else that relies on semantics. There are a couple of things we can do to mitigate this:

Set the container’s role attribute to button .

attribute to . To make it accessible with a keyboard, set the container’s tabindex attribute to 0 or greater and add a keyboard event that triggers click with the space and return keys.

attribute to 0 or greater and add a keyboard event that triggers click with the space and return keys. Even though in this demo the button visually turns into a progress bar, we should not change the role of an element. So, we create a separate progressbar element, put it out of the screen to make it invisible, and update it together with our animation.

Now that you have had some insight into the process of creating an animation like this, have a look at the whole source code and check out the instructions for integrating a button like this.

We hope you enjoyed this progress button and find it useful!