From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
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.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
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…
<span class="hljs-keyword">var</span> circle=<span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#background"</span>);
…you can just animate properties like x
, y
, and scale
that it will generate the proper transform
.
TweenMax.to(circle,<span class="hljs-number">1</span>,{
scale:<span class="hljs-number">0.1</span>
})
For things that aren’t transform
, you just use the attr
property:
TweenMax.to(circle,<span class="hljs-number">1</span>,{
attr:{
<span class="hljs-string">"stroke-width"</span>:<span class="hljs-number">4</span>
}
})
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,<span class="hljs-number">1</span>,{
transformOrigin:<span class="hljs-string">"50% 50%"</span>,
scale:<span class="hljs-number">0.1</span>
})
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:
<span class="hljs-comment">// select the path</span>
<span class="hljs-keyword">var</span> line=<span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#line path'</span>);
<span class="hljs-comment">// get the d attribute</span>
<span class="hljs-keyword">var</span> d=line.getAttribute(<span class="hljs-string">'d'</span>);
<span class="hljs-comment">// get the pathdata (toAbs() converts the points coordinates from relative to absolute)</span>
<span class="hljs-keyword">var</span> pathData=<span class="hljs-keyword">new</span> SVGPathData(d).toAbs();
<span class="hljs-comment">// get the vertices</span>
<span class="hljs-keyword">var</span> points=pathData.commands;
<span class="hljs-comment">// pick a point</span>
<span class="hljs-keyword">var</span> middlePoint=points[<span class="hljs-number">1</span>];
<span class="hljs-comment">// animate</span>
TweenTo(middlePoint,<span class="hljs-number">1</span>,{
y:<span class="hljs-number">0</span>,
y1:<span class="hljs-number">0</span>, <span class="hljs-comment">// bezier control point</span>
<span class="hljs-comment">// updating using GSAP`s onUpdate and onComplete to keep it synchronized</span>
onUpdate:updatePath,
onComplete:updatePath
});
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePath</span><span class="hljs-params">()</span></span>{
<span class="hljs-comment">// converts the points array back to svg path string</span>
line.setAttribute(<span class="hljs-string">'d'</span>,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[<span class="hljs-number">0</span>],<span class="hljs-number">1</span>,{
x:-<span class="hljs-number">100</span>
});
Tween.to(points[<span class="hljs-number">1</span>],<span class="hljs-number">1</span>,{
x:<span class="hljs-number">0</span>
});
Tween.to(points[<span class="hljs-number">2</span>],<span class="hljs-number">1</span>,{
x:<span class="hljs-number">100</span>
});
Tween.to(<span class="hljs-keyword">this</span>,<span class="hljs-number">1</span>,{
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:
<span class="hljs-attribute">pointer-events</span><span class="hljs-value">: none;</span>
Then, for the SVG element you want to be clickable, you set:
<span class="hljs-attribute">pointer-events</span><span class="hljs-value">: fill;</span>
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 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!
Outstanding. Finally somebody implemented that dribbble.
I’ve never expected someone would be able to implement it exactly when I first saw it months ago!!! Keep up the good work. 🙂
That’s so freakin’ cool! Man you guys are awesome!
Incredible!! Thanks!
P.S – Broken link on paper.js
OMG!! This is sooooooo cool. Great work!! Thanks for sharing.
Awesome!!
Awesome!
Excellent!!! Thank!
Wow, it looks just incredible! 🙂 Thank you Lucas!
I think this’s beyond awesome! It looks so beautiful. Thanks a lots!
Awesome work!
Cool as always
Very, very, very nice work. GSAP is shaping up to the the goto lib for SVG animation. Interesting to see if someone else steps up and beats them in performance and capabilities.
Clever Lucas. Love it. Before the end of the year I anticipate something cooler to compete with snap.svg and projects alike \o/
Hahah I can’t see myself taking a project of that scale for now, but who knows! Glad you like it 🙂
Pure awesomeness, great work!
coll!
Great job !
It’s realy fluid & close to physics reality !
It would be great to also have more icons(a right arrow maybe). Or to have an option to disable the initial animation because progress bars are not used just for upload or download (for example wait for a server-side computation).
Ooh that’s a good idea. Will try to do that!
Such a great an interesting work
this is pretty cool…
Thanks a lot. Great Work
Great work Lucas! Love the physics and animation it’s all really smooth, I particularly like how you did the ‘failed’.
One word. Awesome!!!
amazingggggggggggg………………
God Job !
This is lovely, if you’re a Greensock club member they’ve just released a morphsvg plugin which could be perfect for this kind of animation.
Someone is stealing your work!
http://codecanyon.net/item/wp-elastic-progress-bar/13036925
Awesome work, man!
This is Awesoome man,Thank youuuuu
This is awesome
love this site..! 🙂
Great work!
just want to say great work
Parabéns, Lucas! Aqui é HUE BR também.
Todos os teus posts são incríveis. E o site do Garden Estúdio então, nem se fala…
Valeu por compartilhar.
Great work man.
Hi,
Is there any config option to show progress popup below progress bar ? by default its above progress bar.
Abhi
One of the best daily inspirational quotes is: If you cannot do great things, do small things in a great way- Napoleon Hill (source- http://decentquotes.com/inspirational). This progress animation is simply incredible and fun to watch. This animation will glue you to your screen as you wait for a task to complete. I like this amazing article because it does not quote prices but explain the functionality of this amazing progress animation.
Hi – Can someone tell me, how to make gif images like the displayed in this tutorial?
Thanks in advance.
WoW. Nice Work
That was cool!
great inspirations..
Totally lost. Doesn’t really explain at all how to animate this. Not even sure what is causing the animation to happen or how it’s working. This just sort of jumps from #line to #background and has no explanation why or what is doing what. The SVG-PATHTO comment was extremely confusing too. No direction. Just spent 3 hrs reading this and trying to understand ‘how’ to actually create this animation. And once I got to the end, I realized, this was just an article full of a few tips. The only useful thing I got from this is how to design the svg shape in Ai. I am so appalled I can’t even believe I just wasted so much time trying to figure this out..And the Git project doesnt exlplain how to implement the svg. The demo has HTML that doesnt even include any svg’s, Seriously what is HAPPENING? Nothing makes ANY SENSE.
The SVG is built into the elastic-progress.js file then displayed as a DOM element I believe.
Fantastic.