Animating Vectors with SVG

It is almost 2014 and fifteen years ago the W3C started to develop a web-based scalable vector graphics (SVG) format. As web technologies go, this one is pretty old and well entrenched.

See the Pen yJflC by Drew McLellan (@drewm) on CodePen


Embed not working on your device? Try direct.

Unlike rasterized images, SVG files will stay crisp and sharp at any resolution. With high-DPI phones, tablets and monitors, all those rasterized icons are starting to look a bit old and blocky. There are several options to get simpler, decorative pieces to render smoothly and respond to various device widths, shapes and sizes. Symbol fonts are one option; the other is SVG.

I’m a big fan of SVG. SVG is an XML format, which means it is possible to write by hand or to script. The most common way to create an SVG file is through the use of various drawing applications like Illustrator, Inkscape or Sketch. All of them open and save the SVG format.

But, if SVG is so great, why doesn’t it get more attention?

The simple answer is that for a long time it wasn’t well supported, so no one touched the technology. SVG’s adoption has always been hampered by browser support, but that’s not the case any more. Every modern browser (at least three versions back) supports SVG. Even IE9.

Although the browsers support SVG, it is implemented in many different ways.

SVG in HTML

Some browsers allow you to embed SVG right in the HTML: the <svg> element. Treating SVG as a first-class citizen works — sometimes. Another way to embed SVG is via the <img> element; using the src attribute, you can refer to an SVG file. Again, this only works sometimes and leaves you in a tight space if you need to have a fallback for older browsers. The most common solution is to use the <object> element, with the data attribute referencing the SVG file. When a browser does not support this, it falls back to the content inside the <object>. This could be a rasterized fallback <img>. This method gets you the best of both worlds: a nice vector image with an alternative rasterized image for browsers that don’t support SVG. The downside is that you need to manage both formats, and some browsers will download both the SVG and the rasterized version, becoming a performance problem.

Alexey Ten came up with a brilliant little trick that uses inline SVG combined with an SVG <image> element. This has an SVG href pointing to the vector SVG representation and a src attribute to the rasterized version. Older browsers will rewrite the <image> element as <img> and use the rasterized src attribute, but modern browsers will show the vector SVG.

<svg width="96" height="96">
    <image xlink:href="svg.svg" src="svg.png" width="96" height="96"/>
</svg>

It is a great workaround for most situations. You will have to determine the browsers you want or need to support and consider performance issues to decide which method is best for you.

So it can be used in HTML. Why?

There are two compelling reasons why vector graphics in the form of icons and symbols are going to be important on the web. With higher resolution screens, going from 72dpi to 200, 300, even over 400dpi, your rasterized icons are looking a little too blocky. As we zoom and print, we expect the visuals on the site to also stay smooth and crisp.

The other main reason vector graphics are useful is scaling. As responsive websites become the norm, we need a way to dynamically readjust the heights, widths and styles of various elements. SVG handles this perfectly, since vectors remain smooth when changing size.

SVG files are text-based, so they’re small and can be gzipped nicely. There are also techniques for creating SVG sprites to further squeeze out performance gains. But SVG really shines when you begin to couple it with JavaScript. Since SVG elements are part of the DOM, they can be interacted with just like any other element you are used to.

The folks at Vox Media had an ingenious little trick with their SVG for a Playstation and Xbox One reviews. I’ve used the same technique for the 24 ways example. Vox Media spent a lot of time creating SVG line art of the two consoles, but once in place the artwork scaled and resized beautifully.

They still had another trick up their sleeves. In their example, they knew each console was line art, so they used SVG’s line dash property to simulate the lines being drawn by animating the growth of the line by small percentage increments until the lines were complete.

This is a great example of a situation where the alternatives wouldn’t be as straightforward to implement. Using an animated GIF would create a heavy file since it would need to contain all the frames of the animation at a large size to permit scaling; even then, smooth aliasing would be lost. canvas and plenty of JavaScript would be another alternative, but this is a rasterized format. It would need be redrawn at each scale, which is certainly possible, but smoothness would be lost when zooming or printing.

The HTML, SVG and JavaScript for this example is less than 4KB! Let’s have a quick look at the code:

<script>
var current_frame = 0;
var total_frames = 60;
var path = new Array();
var length = new Array();
for(var i=0; i<4;i++){
	path[i] = document.getElementById('i'+i);
	l = path[i].getTotalLength();
	length[i] = l;
	path[i].style.strokeDasharray = l + ' ' + l; 
	path[i].style.strokeDashoffset = l;
}
var handle = 0;

var draw = function() {
   var progress = current_frame/total_frames;
   if (progress > 1) {
     window.cancelAnimationFrame(handle);
   } else {
     current_frame++;
     for(var j=0; j<path.length;j++){
	     path[j].style.strokeDashoffset = Math.floor(length[j] * (1 - progress));
     }
     handle = window.requestAnimationFrame(draw);
   }
};
draw();
</script>

First, we need to initialize a few variables to set the current frame, the number of frames, how fast the animation will run, and we get each of the paths based on their IDs. With those paths, we set the dash and dash offset.

path[i].style.strokeDasharray = l + ' ' + l; 
path[i].style.strokeDashoffset = l;

We start the line as a dash, which effectively makes it blank or invisible.

Next, we move to the draw() function. This is where the magic happens. We want to increment the frame to move us forward in the animation and check it’s not finished. If it continues, we then take a percentage of the distance based on the frame and then set the dash offset to this new percentage. This gives the illusion that the line is being drawn. Then we have an animation callback, which starts the draw process over again.

That’s it! It will work with any SVG <path> element that you can draw.

Libraries to get you started

If you aren’t sure where to start with SVG, there are several libraries out there to help. They also abstract all browser compatibility issues to make your life easier.

You can also get most vector applications to export SVG. This means that you can continue your normal workflows, but instead of flattening the image as a PNG or bringing it over to Photoshop to rasterize, you can keep all your hard work as vectors and reap the benefits of SVG.

About the author

Brian Suda is a master informatician working to make the web a better place little by little everyday. Since discovering the Internet in the mid-90s, Brian Suda has spent a good portion of each day connected to it. His own little patch of Internet is http://suda.co.uk, where many of his past projects and crazy ideas can be found.

Photo: Jeremy Keith

More articles by Brian

Comments