SVG Paths

2015-07-16 procedural-generation 5 min read

In modern HTML you can embed SVGs directly into your DOM. This allows for pretty visuals, using the powers of both css and svg. One use that I have had on multiple occasions is path animations.

Here is a mockup of what it looked like:


Doing something like this is actually quite easy. The basic idea is to use a very long dashed stroke that will be animated. In practice this means setting stroke-dasharray to 100% and animating stroke-dashoffset from 100% to 0% or the other way around.

This can be done in multiple ways, I will show two that I happened to have used:

CSS Animation

CSS animations are powerful and perfect for this kind of animation. To use them, we need to prepare our SVG a little. For the example above I created the following file in Inkscape:

SVG FILE

Next, we need a little CSS inside the SVG file. For that, add the following styles into the <defs></defs> of your SVG:

<style>
.hiddenPath {
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
}

.animatedPath {
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
    animation: dash 1s linear forwards;
}

#DP1 {
    transform-origin: 102px 114px;
}

#DP2 {
    transform-origin: 473px 145px;
}

#DP3 {
    transform-origin: 345px 287px;
}

#DP4 {
    transform-origin: 132px 322px;
}

@keyframes dash {
    from {
        stroke-dashoffset: 1000;
    }
    to {
        stroke-dashoffset: 0;
    }
}

.hiddenInfoBubble {
    transform: scale(0);
}

.animatedInfoBubble {
    transform: scale(1);
    transition: transform 0.35s ease-in-out;
}
</style>

To make use of these styles set the class of all bubbles to hiddenInfoBubble and the class of all paths to hiddenPath. Also set the IDs of all bubbles to the values from the stylesheet. Of course, the transform-origins will differ for you.

Controlling these animations is done using JavaScript. The code below is what is used in the example above and should give you a rough idea what needs to be done.

var state = 0;
function next_action()
{
    var svg = document.getElementById("svg-container").contentDocument;
    switch(state)
    {
        case 0:
            svg.getElementById("DP1").setAttribute("class", "animatedInfoBubble");
            state++;
            break;
        case 1:
            svg.getElementById("DP1-DP2").setAttribute("class", "animatedPath");
            setTimeout(function(){
                svg.getElementById("DP2").setAttribute("class", "animatedInfoBubble");
            }, 350);
            state++;
            break;
        case 2:
            svg.getElementById("DP2-DP3").setAttribute("class", "animatedPath");
            setTimeout(function(){
                svg.getElementById("DP3").setAttribute("class", "animatedInfoBubble");
            }, 240);
            state++;
            break;
        case 3:
            svg.getElementById("DP3-DP4").setAttribute("class", "animatedPath");
            setTimeout(function(){
                svg.getElementById("DP4").setAttribute("class", "animatedInfoBubble");
            }, 240);
            state++;
            break;
        case 4:
            svg.getElementById("DP1-DP2").setAttribute("class", "hiddenPath");
            svg.getElementById("DP2-DP3").setAttribute("class", "hiddenPath");
            svg.getElementById("DP3-DP4").setAttribute("class", "hiddenPath");
            svg.getElementById("DP1").setAttribute("class", "hiddenInfoBubble");
            svg.getElementById("DP2").setAttribute("class", "hiddenInfoBubble");
            svg.getElementById("DP3").setAttribute("class", "hiddenInfoBubble");
            svg.getElementById("DP4").setAttribute("class", "hiddenInfoBubble");
            state = 0;
            break;
    }
}

A few points to note about the code:

Another article about css/svg animations can be found on tympanus.net.

JavaScript

In some cases, CSS animations are unfortunately not enough. One example is, if you want to move an object along with the path. For this, some JavaScript is required:

var path = ...;
var obj = ...;

var path_length = path.getTotalLength();
function frame(t) {
    // Animate path
    path.style = "stroke-dashoffset: -" + (t * path_length);

    // Animate object
    var pos = path.getPointAtLength(t * g.length);
    obj.x = pos.x;
    obj.y = pos.y;
}

This style of animation is used for the wires at the top ot this page. A demo can be found here.