Libraries for SVG Drawing Animations

Libraries for SVG Drawing Animations

In 2013, Jake Archibald introduced this cool trick of animating an SVG path to look like it’s drawing itself. It’s 2020 now, and the trick is still popular. I’ve seen it on a lot of websites I’ve visited recently. I, too, feature an animated SVG loader on my website using one of the libraries I’ll introduce below.

In a previous article, Chris Coyier wrote about how SVG path animations work under the hood, using the CSS stroke-dasharray and stroke-dashoffset properties. In this article, I want to introduce you to four JavaScript libraries that can be used to create SVG path drawing animations with fewer lines of code, like this cool example. Why a library? Because they’re ideal for complex animations involving two or more SVGs with multiple paths.

To get started, l’ll first secure an SVG to demo. Let’s use this castle from svgrepo. The castle SVG downloads as an SVG image. But, since we’re dealing with path animation, what we need is the code format of the SVG. To get this, I’ll import the file into Figma and use the “Copy as SVG” feature (Right Click → Copy/Paste → Copy as SVG) to grab the SVG code.

To successfully animate an SVG path, the SVG shape should have a fill of none and each individual SVG path must have a stroke (we’ll set it to #B2441D) and a stroke-width (set to 2px).

The animation effect we want to create is to first draw the outline (or stroke) of the SVG and then fill in the different colors. In total, there are six different fill colors used throughout the SVG, so we’ll remove the fill color from each path and give paths of the same color the same class name.

  • #695A69: color-1
  • #B2441D: color-2
  • #DFDOC6: color-3
  • #C8B2A8: color-4
  • #DE582A: color-5
  • #AO8A8A: color-6

After all the modifications, here’s what the SVG code looks like:


<svg id="svg-castle" width="480" height="480" viewBox="0 0 480 480" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M231.111 183.761V150.371C231.111 149.553 231.774 148.889 232.592 148.889H24 7.407C248.225 148.889 248.889 149.552 248.889 150.371V183.761L258.342 206.667H271.111 V135.556H240H208.889V206.667H221.658L231.111 183.761Z" stroke="#B2441D" stroke-width="2px" class="color-6" /> <path d="M311.111 420H288.889V455.556V468.889H311.111V455.556V420Z" stroke="#B2441D" stroke-width="2px" class="color-1" /> <path d="M191.111 420H168.889V455.556V468.889H191.111V455.556V420Z" stroke="#B2441D" stroke-width="2px" class="color-1" /> <path d="M168.889 220V228.889V237.778H222.222V228.889H212.487L221.658 206.667H208.88 9H169.524L177.778 220H168.889Z" stroke="#B2441D" stroke-width="2px" class="color-2"/ > <!-- etc. -->
</svg>

That’s all the SVG preparation we need. Let’s look at how to achieve the desired animation with the different libraries.

Library 1: Vivus

Vivus is a lightweight JavaScript class (with no dependencies) that allows you to animate SVGs like they’re being drawn. The library is available using any of these options. To keep things simple, we’ll use a CDN link:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vivus/0.4.5/vivus.min.js" integrity="sha512-NBLGIjYyAoYAr23l+dmAcUv7TvFj0XrqZoFa4i1o+F2VvF9SrERyMD8BHNnJn1SEGjl1AouBDcCv/q52L3ozBQ==" crossorigin="anonymous"></script>

Next, let’s create a new Vivus instance. It takes three arguments:

  1. The ID of the target element (the SVG)
  2. An options object with a dozen possible values
  3. A callback function that runs at the end of the animation

Looking back at our SVG code, the SVG ID is svg-castle.

new Vivus('svg-castle', { duration: 200, type:'oneByOne'
});

Now, let’s write a callback function that fills the paths with the different colors we’ve defined:

function fillPath(classname, color) { const paths = document.querySelectorAll(`#svg-castle .${classname}`); for (path of paths){ path.style.fill = `${color}`; }
}

The fillPath function selects all paths in the svg-castle element with the supplied classname, loops through and fills each path with the specified color. Remember in a previous step, we removed the fill from each path and gave each path a same fill class (color-1, color-2, etc.).

Next up, we call the fillPath function for the six different classnames and their corresponding colors:

function after() { fillPath('color-1', '#695a69'); fillPath('color-2', '#b2441d'); fillPath('color-3', '#dfd0c6'); fillPath('color-4', '#c8b2a8'); fillPath('color-5', '#de582a'); fillPath('color-6', '#a08a8a')
}

That’s the callback function passed to the Vivus instance. See Pen for full implementation.

Library 2: Walkway.js

Walkway is a light-weight SVG animation library for path, line and polygon elements. To start using it, we can either add the library using npm, yarn, or with a CDN link like we did with Vivus. We’ll go with the CDN link once again:

<script src="https://cdn.jsdelivr.net/npm/walkway.js/src/walkway.min.js"></script>

With Walkway, we create a new Walkway instance, passing an options object as an argument. Then, we call the draw method on the new instance and pass in an optional callback function which will be run at the end of the draw animation. Again, very much like Vivus.

We’ve already written the after callback function in the previous example, so the rest should be a piece of cake:

const svg = new Walkway({ selector: '#svg-castle', duration: 3000,
}); svg.draw(after);

Library 3: Lazy Line Painter

Lazy Line Painter is a modern JavaScript library for SVG path animation. It requires minimal code to setup. However, if a GUI is more of your thing, you can use the Lazy Line Composer which is a free online editor for SVG path animation from the same makers. The SVG will be exported as an animated SVG file that can be used directly anywhere.

The basic setup for Lazy Line Painter is similar to what we’ve already done in the other examples. First, get the library using either npm or a CDN link. Just like the previous examples, we’ll use a CDN link:

<script src="https://cdn.jsdelivr.net/npm/[email protected].4/lib/lazy-line-painter-1.9.4.min.js"></script>

Then, we initialize a new LazyLinePainter instance, which accepts two parameters — a selector (the ID of the target SVG element) and a config object. Let’s call the paint method on the new instance:

// select the svg by id
let svg = document.querySelector('#svg-castle') // define config options
let options = { strokeDash: '2, 2',
}
// initialize new LazyLinePainter instance
let myAnimation = new LazyLinePainter(svg, options) // call the paint method
myAnimation.paint()

A full list of config options are available in the library docs. Unlike the previous libraries, we don’t pass a callback function to the paint method. Instead, we’ll listen for the complete:all event handler on the animation and then pass in the callback function.

myAnimation.on('complete:all', (event) => {after()});

We can also control when the paint method runs using event listeners like we’ve have done in the following codepen demo. Click on the castle to re-run the animation.

Library 4: Framer Motion

Framer Motion is a bit different from other libraries we’ve covered. It’s a production-ready open-source animation library for React components with tons of possible animation types. And, yes, this is from the same team behind the popular Framer prototyping tool.

First up, we’ll install the library with npm in the terminal:

npm install framer-motion

For SVG path drawing animations, Framer Motion provides a motion.path component that takes four props:

<motion.path d={pathDefinition} initial={{ pathLength: 1, pathOffset: 0 }} animate={{ pathLength: 0, pathOffset: 1 }} transition={{ duration: 2 }}
/>

To use it, we’ll simply convert our SVG paths to motion.path, like this:

import React from 'react';
import { motion } from "framer-motion";
const AnimatedCastle = () => { return ( <svg id="svg-castle" width="480" height="480" viewBox="0 0 480 480" fill="non e" xmlns="http://www.w3.org/2000/svg"> <motion.path d="M311.111 420H288.889V455.556V468.889H311.111V455.556V420Z" stroke="#B2441D" stroke-width="2" className="color-1" initial={{ pathLength: 1,fill:"none", opacity:0, }} animate={{ pathLength: 0,fill:"695A69", opacity:1 }} transition={{ duration: 2 }} /> <motion.path d="M191.111 420H168.889V455.556V468.889H191.111V455.556V420Z" stroke="#B2441D" stroke-width="2" className="color-2" initial={{ pathLength: 1, fill:"none", opacity:0, }} animate={{ pathLength: 0, fill:"#b2441d", opacity:1}} transition={{ duration: 3 }} /> <!-- etc. --> </svg> )
}

This has to be done for each SVG path. See this demo for full implementation:

There’s a caveat though: the castle SVG has over 60 paths, which is a lot. Going through them was quite daunting for me, and I found the process to be repetitive and prone to errors. For that reason, I don’t recommend Framer Motion but I would say that it is well suited for SVGs within React components with no more than five paths. For anything more than that, go with any of the previous libraries we covered.

That’s a look at four JavaScript libraries we can use to get hand-drawn SVG effects.

Why didn’t we cover a CSS-only solution? While it’s possible to do, it involves a lot of code repetition. For example, it means finding the total length of each path using JavaScript or with this cool trick that sets each path length to 1, and then sets the stroke-dasharrray and stroke-dashoffset of each path to its path length.

After that, we still need to define keyframes to animate the stroke-dashoffset to zero. Then, those keyframe animations will be added to each path and with an animation-delay to offset things a bit. We also have to write six different keyframe rules to fill the paths with their respective colors. Considering that the castle has over 60 individual paths, that’s over 100 lines of CSS! Not exactly the most efficient or straightforward approach.

Leave a Reply

Your email address will not be published. Required fields are marked *