Image may be NSFW.
Clik here to view.
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:
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
We can turn that loose curve into its more tense shape by just keeping the bezier control points close to the vertices.
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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:
// select the path
var line=document.querySelector('#line path');
// get the d attribute
var d=line.getAttribute('d');
// get the pathdata (toAbs() converts the points coordinates from relative to absolute)
var pathData=new SVGPathData(d).toAbs();
// get the vertices
var points=pathData.commands;
// pick a point
var middlePoint=points[1];
// animate
TweenTo(middlePoint,1,{
y:0,
y1:0, // bezier control point
// updating using GSAP`s onUpdate and onComplete to keep it synchronized
onUpdate:updatePath,
onComplete:updatePath
});
function updatePath(){
// converts the points array back to svg path string
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
})
...
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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 tobutton
. - 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. - 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 separateprogressbar
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!
Elastic Progress was written by Lucas Bebber and published on Codrops.