The Muratorium

Hi, I am Muri. My actual name is Murat, so that is why it's called "Muratorium".

Made in Berlin, 2020.

Work in progress!

Recipe: How to animate lines in SVGs

Hi! This articles gives you a high-level overview of what you need to know to animate lines in an SVG without being forced to use any framework or library like React or D3. My goal is for you to be able to apply this technique to your custom stack.

If you are interested in applying this technique with React and D3 however, check out this video.

Ingredients

  • svg containing any other SVG element like path, rect or line
  • stroke-dasharray (an attribute on SVG DOM elements)
  • stroke-dashoffset (an attribute on SVG DOM elements)
  • a sprinkle of CSS
  • getTotalLength (a method on any SVG DOM element)

Let's get started!

SVG elements have 2 important properties: A stroke, and filling (or fill).

Lines line
Rectangles rect
Circles circle
Polylines polyline
Paths path

The stroke is what we want to animate, and the trick behind animating those lines is with the stroke-dasharray and stroke-dashoffset attributes, which you can put on any SVG element, such as line, circle, rect, polyline, path and even on the svg itself.

The magic trick

stroke-dasharray

The stroke-dasharray attribute determines how a stroke is dashed and is described with 2 numbers, like so: stroke-dasharray="10 2". The first number determines the length of each dash (line), while the second number determines the length of the following gap. This pattern (dash, gap, dash, gap ...) is repeated indefinitely.

// will render a dashed line (each dash being 10px)
// on an 300x150px svg (default dimensions)

<svg>
  <line
    x1="0"
    y1="0"
    x2="300"
    y2="150"
    stroke-dasharray="10 50"
    stroke="black"
  />
</svg>

stroke-dashoffset

The stroke-dashoffset attribute adds an offset to the stroke-dasharray pattern, and moves it along the stroke for a given length. If the stroke-dasharray is "100 100", the stroke will start with a 100px dash, which will be followed by a 100px gap. Now, if a stroke-dashoffset of "100" is added, the stroke will start with the gap instead, which then will be followed by the dash, and so on.

// will render a dashed line (each dash being 10px)
// on an 300x150px svg (default dimensions)

<svg>
  <line
    x1="0"
    y1="0"
    x2="300"
    y2="150"
    stroke="black"
    stroke-dasharray="100 100"
    stroke-dashoffset="0"
    style="transition: stroke-dashoffset 0.5s"
  />
</svg>

Now think of a stroke which has a total length of 100px, and you give it a dasharray of "100 100". This will result in a solid line (no dashes), because the first dash of the dasharray is as long as the entire stroke itself. The following gap is there and just as long as the dash, but is irrelevant, because it starts just where the stroke ends.

Adding a stroke-dashoffset of 100 will turn the tables. The dash (which occupied the entire length of the line) will be pushed out, and the gap from the dasharray pattern will take its place.

And this is the gist of the whole thing: If you have a stroke that is 100px long in total, all you need is dash line and a gap, each as long as the entire stroke itself (stroke-dasharray="100 100"). Then you make one push out the other with stroke-dashoffset in an eternal battle between light and darkness, 1 and 0, to be or not to be.

Try it out

stroke-dasharray: 10 10
stroke-dashoffset: 10

Hint: try maxing out everything, then reduce the offset.

Finding out the total length of a stroke

Now we know that to achieve the animation we want, we need the stroke-dasharray values and the stroke-dashoffset to be equal to the length of the stroke we want to animate.

To find out the total length of a stroke on any SVG element, you can use the getTotalLength method, which you can access on any SVG element (but not on the svg itself).

// access the line in any way possible
const lineElement = document.querySelector("#thatLine");
const totalLength = lineElement.getTotalLength();

// somewhere in your HTML or template 
<svg>
  <line
    id="thatLine"
    x1="0"
    y1="0"
    x2="300"
    y2="150"
    stroke="black"
    stroke-dasharray="false false"
    stroke-dashoffset="false"
    style="transition: stroke-dashoffset 0.5s"
  />
</svg>

Work in progress!