Serial indie maker. I’m creating 10 startups in 10 months. Follow my journey on Twitter!


Animating SVG paths is probably one of the most challenging technical tasks I’ve ever found, and, what I thought was a trivial thing became an exasperating business.

It shouldn’t be so goddamn difficult. Please let me save you the hours I wasted researching and trying to understand SVG animations and give you the tools to achieve nice, good-looking animations. Jump straight to the solution if you’re not interested in the technical details and alternatives.



The backstory: I wanted to animate a number of SVG paths for a personal map project. In my mind, the animation was pretty clear and looked exactly like the GIF above, which is precisely the end result. I call it a “fading ghost trail effect”.

But when it came up to actually making it happen, I just couldn’t. I searched on Google for hours, tried to follow every single technical blogpost. It looked like what I was trying to accomplish was just impossible.

But, you know, there’s nothing impossible (specially in Computer Science) – everything is just a matter of time. And so, I had to come up with a new technique for animating SVG paths.

First, let us define the end result.

The end result

A SVG Path, animated by segments, leaving a ghost trail.

Constraints

The project that led me to this animation was based on geospatial data (geo tracks from a GeoJSON plotted on top of a map), and so, it might not work well with every scenario. I found more precisely that it only works under the following constraints:

  • SVG paths with straight lines only (no bezier curves yet, sorry!)
  • Only one / a small number of SVG paths simultaneously being animated on screen
  • SVG paths have to have a large number of points (a huge “d” property)

While the current dasharray technique I’m about to explain fits well most scenarios, it didn’t work at all with the paths generated from my data.

The current technique

The most famous technique I found when researching online was the one based on the dasharray and dashoffset properties.

It’s actually a pretty nice hack. Its better explained here, but it goes like this:

SVG allows you to define a dashing longitude for your paths:

It also allows you to define an offset for the dash. Let’s mix it all together and set a dash longitude of exactly the longitude of the path (so every “white part” in the dash pattern has the same length as the path itself).

The path looks exactly the same because the dash starts right where the path ends.

Now let’s set the dash offset to the length of the path, so the white part ends exactly where the path ends and starts exactly where the path starts.

Wow. Such white. That’s why the path is entirely “hidden” with its dash. Now, if we slowly bring the offset closer to 0 again…

Go ahead and play with the live example in this great explanation. You can learn how to implement it there, too.

The reason I’m covering this technique is because it’s universal. Mine doesn’t work yet with curves and it’s very memory and cpu consuming. On the flip side, my alternative looks much cooler, specially on maps 😎

Fading trails – my approach

Main idea: convert a path into multiple lines, then animate those lines separately.

I seriously thought it would be much easier. I have spent more time trying to come up with this than I’m willing to admit.

I tried CSS backgrounds, the dash technique… nothing really worked.

Until I realised what a SVG path actually was.

Anatomy of a SVG path

It’s actually just a looong string of instructions condensed in a single property called “d”.

I’m not going to get into much detail, let just say it tells the drawing cursor where to move and draw.

Example:

<path d="M 400 300 L 400 350"></path>

Where M 400 300 L 400 350 just means “move the cursor to the point (x, y) = (400, 300) and then draw a line from there to the point (400, 350).

This constitutes a single long line that we can’t cut up. Or can we?

What if we could break that line into chunks

There’s another SVG element called “line”. Pretty straight forward, it draws a line from (x1, y1) to (x2, y2):

<line x1="400" y1="300" x2="400" y2="350"></line>

Would yield the exact same result as the path above.

And so, I wrote a function to convert a SVG path into its line equivalent.

    function convertToLines(my_path){ 
      var current_x = 0;
      var current_y = 0;
      var dest_x = 0;
      var dest_y = 0;

      // We're just using D3 to create a SVG group to put our lines into
      var linegroup = d3.select("g").append("g");

      for (var segment_i = 0 ; segment_i < my_path.pathSegList.numberOfItems; segment_i++) {

        var item = my_path.pathSegList.getItem(segment_i);

        switch (item.pathSegType) {

          // If matched thing is a Path "move pointer" command
          case SVGPathSeg.PATHSEG_MOVETO_ABS:
            current_x = item.x;
            current_y = item.y;

          // If matched thing is a Path "draw line" command
          case SVGPathSeg.PATHSEG_LINETO_ABS:
            dest_x = item.x;
            dest_y = item.y;

            // Create the actual line
            linegroup.append("line")
            current_x = item.x;
            current_y = item.y;
        }
      }
      // Once we have the path converted to a group of lines, there's no need to keep the path
      my_path.style.display = "none";
    }

It’s using deprecated SVG stuff, so let’s just sprinkle some polyfills on top of our project to get the code working. Add and reference pathseg.js and path-data-polyfill.js and you’ll be good to go.

The function just creates a <g> (a SVG group) to put our lines into. When it detects a line, it creates the correspondent <line> element and inserts it into our <g>.

Putting it all together & add animations

If we add some nice animations to each segment:

    function animatePath(my_path){ 
      var current_x = 0;
      var current_y = 0;
      var dest_x = 0;
      var dest_y = 0;

      // We're just using D3 to create a SVG group to put our lines into
      var linegroup = d3.select("g").append("g");

      for (var segment_i = 0 ; segment_i < my_path.pathSegList.numberOfItems; segment_i++) {

        var item = my_path.pathSegList.getItem(segment_i);

        switch (item.pathSegType) {

          // If matched thing is a Path "move pointer" command
          case SVGPathSeg.PATHSEG_MOVETO_ABS:
            current_x = item.x;
            current_y = item.y;

          // If matched thing is a Path "draw line" command
          case SVGPathSeg.PATHSEG_LINETO_ABS:
            dest_x = item.x;
            dest_y = item.y;

            // Create the actual line
            linegroup.append("line")
              .style("stroke", "#f11f50")
              .style("stroke-width", "1.5px")
              .attr("stroke-linecap", "butt")
              .attr("x1", current_x)
              .attr("y1", current_y)
              .attr("x2", dest_x)
              .attr("y2", dest_y) 
              .style("opacity", 0)
              .transition()
              .duration(20)
              .delay(function(){ return segment_i*10; })
              .style("opacity", 0.8)
              .ease("cubicIn")
              .transition()
              .duration(10)
              .delay(function(){ return segment_i*13; })
              .ease("cubicOut")
              .style("opacity", 0.2)

            current_x = item.x;
            current_y = item.y;
        }
      }
      // Once we have the path converted to a group of lines, there's no need to keep the path
      my_path.style.display = "none";
    }

Want to animate multiple SVG paths? Just loop over your paths and apply the function to each one.

But be careful! It’s very memory and CPU consuming with large, complicated paths and can cause sloppy animations very easily. That’s the downside of this technique.

Play around with the numbers – you can get longer tails, quicker paths… just play around and see what you like best 😉

SVG path fading ghost trail animation final result

Final thoughts

SVG is a huge pain in the ass.

SVG shouldn’t be so complicated.

SVG animations should be as trivial as CSS animations.

SVG is evil.

Designers, animators, frontenders – let’s unite and hate SVG together.

Oh, and by the way!

If you wanna keep up to date with my new posts and public launches and all that stuff (fuck, no, I won't send spam, why would I do that) please consider subscribing for free:

You can obviously unsubscribe anytime with a single click.

Comments

comments