Manipulating and Animating SVG with Raw Javascript
When I first started building the Hanzi Writer library, I assumed that in order to manipulate and animate javascript that I’d need to rely on an SVG library of some sort. I settled on SVG.js as it was the leanest library I could find. But even then, adding in minified SVG.js adds 67 KB to the bundle size! Even velocity.js, which only handles animation, adds 48 KB minified to your bundle sizes. For reference, the entirety of Hanzi Writer library currently is around 30 KB, so more than doubling the size of the library just to do basic SVG animation is definitely not acceptable.
Fortunately, it turns out that working with SVG in raw JS isn’t very hard, and for most basic SVG-related tasks you don’t need to include a fancy library at all! Most browser APIs that work on HTML nodes also work fine with SVG, with a few exceptions. Let’s cover the basics below:
Creating SVG Elements
Creating the SVG tag and other SVG elements is easy using document.createElementNS. The only trick is that you need to specify the namespace as "http://www.w3.org/2000/svg"
when using SVG. Then, you can append the SVG node to the page using the standard Node.appendChild.
For example if you had a page with a div like below:
Then you could create an SVG element and append it to the div using:
Manipulating SVG
Setting attributes on SVG nodes in raw JS is a breeze using Element.setAttributeNS. In general you can leave the namespace
param as null
when working with SVG attributes. For example, we can extend the example above to draw a blue SVG circle inside our SVG node:
The result of which is below:
Animating SVG
Now that we have the ability to create and set attributes on SVG nodes, we actually have all we need to be able to animate SVG. Animating SVG in JS is just the same as doing any other type of animation in javascript, using window.requestAnimationFrame. If you’re not familiar with this requestAnimationFrame
you can read more about it here. This is how most JS animation libraries work under the hood anyway.
calling requestAnimationFrame(callback)
asks the browser to call callback
at the next paint cycle. If callback
then calls requestAnimationFrame(callback)
again, you’ll end up running callback
regularly enough to make smooth animations. Here’s an example below, which will make our circle from above move to the right over 1 second:
In the above code, the animateStep
function calls itself again via window.requestAnimationFrame(animateStep)
to keep triggering new paint cycles until the animation completes.
The example above is very basic, and in practice you may want to move animation code into a helper function or class to keep things reusable. Depending on your needs you may need to support things like canceling animations or chaining animations together, which is still doable using the building blocks described above. Below is a simple SvgTween
class that we can use to simplify animation:
then, using this class our animation example would become:
You can imagine how this simple SvgTween class could be extended to allow chaining or return promises from animations, or allow tweening multiple attributes together depending on your needs. Of course, if you need more complicated SVG behavior you can always go back to including a heavyweight SVG library like SVG.js or raphael.js, but for most basic SVG manipulation and animation its easy to get by just using standard browser APIs.