Elastic Progress

An elastic SVG progress loader based on the Dribbble shot “Download” by xjw and implemented with SVG and TweenMax.

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:

Dribbble shot 'Download' bt xjw

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.

elasticprogress

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.

ai-layers

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.

circle

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.

vertices

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

tense

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…

<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.

elasticprogress2

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
})
...

elasticprogress3

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.

elasticprogress4

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.
  • 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 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!

Tagged with:

Lucas Bebber

Lucas is a developer and illustrator at Garden Estúdio in Brazil. Check out his work on Codepen.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 44

Comments are closed.
  1. I’ve never expected someone would be able to implement it exactly when I first saw it months ago!!! Keep up the good work. 🙂

  2. 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.

  3. 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 🙂

  4. 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).

  5. Great work Lucas! Love the physics and animation it’s all really smooth, I particularly like how you did the ‘failed’.

  6. 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.

  7. 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.

  8. Hi,
    Is there any config option to show progress popup below progress bar ? by default its above progress bar.

    Abhi

  9. 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.

  10. Hi – Can someone tell me, how to make gif images like the displayed in this tutorial?

    Thanks in advance.

  11. 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.