An ode to SVG animations

Decided to move this blog to a new domain. For the engine, I'm using another pet-project: my own CMS, carefully hand-carved from a pure lump of PHP + HTML + CSS amalgam. Not quite ready to open-source it, just trying to use it on a real project.

One interesting thing I've tinkered with in this project is SVG animations. Never tried it before but it turned out to be quite a fun technology. Let's take a look at how it works.

To start, open Inkscape and draw a simple shape. Save it as optimized SVG. Here we encounter the first nuance: all stages of an SVG animation must have the same number of nodes. So, plan carefully. The hexagon on the image, in fact, consists of 12 nodes, I simply doubled each corner node.

The second nuance is that SVG animations works only if the SVG is embedded directly into the page's html. Don't try to include it with an <img> tag. Instead, open the image in any text editor and copy it's contents on page:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Animate SVG</title>
</head>
<body>

<svg version="1.1" viewBox="0 0 150 56" xmlns="http://www.w3.org/2000/svg">
<path id="shape" fill="#555" d="m28 1.5691e-6 0.065406 0.037075 24.183 13.963 5.94e-4 0.07518-5.94e-4 27.925-0.06481 0.0381-24.184 13.962-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z">
</path>
</svg>

</body>
</html>

Next, move some nodes and save the image as a second stage of animation.

If you open the new image in text editor, it will look very similar to the first one. Now, let's create first animation. In the SVG embedded into HTML code, create <animate /> tag between the opening and closing <path>...</path> tags. Fill it's to attribute with the list of node coordinates from the second image:

<svg version="1.1" width="200" height="200" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
<path id="shape" fill="#555" d="m28 1.5691e-6 0.065406 0.037075 24.183 13.963 5.94e-4 0.07518-5.94e-4 27.925-0.06481 0.0381-24.184 13.962-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z">
<animate
begin=".5s"
dur="1s"
attributename="d"
calcMode="spline"
keySplines="0.3, 0, 0.7, 1"
repeatCount="indefinite"
to="m28 0 0.0654 0.037077 24.183 13.963 5.93e-4 0.07518-5.93e-4 27.925-24.249-14-4.6e-5 28-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z"
/>
</path>
</svg>

To stop animation on the second stage, replace repeatCount="indefinite" with fill="freeze". You can add as many <animate /> tags as you wish, assuming that all stages have the same amount of nodes:

<svg version="1.1" width="200" height="200" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
<path id="shape" fill="#555" d="m28 7.5e-8 0.014 27.976 24.235-13.976-24.221 14 24.221 14-24.235-13.976-0.014001 27.976-0.013999-27.976-24.235 13.976 24.221-14-24.221-14 24.235 13.976z">
<animate
begin="200ms"
dur="300ms"
attributename="d"
fill="freeze"
calcMode="spline"
keySplines="0.3, 0, 0.7, 1"
to="m28 1.5691e-6 0.065406 0.037075 24.183 13.963 5.94e-4 0.07518-5.94e-4 27.925-0.06481 0.0381-24.184 13.962-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z"
/>
<animate
begin="700ms"
dur="200ms"
attributename="d"
fill="freeze"
calcMode="spline"
keySplines="0.3, 0, 0.7, 1"
to="m28 0 0.0654 0.037077 24.183 13.963 5.93e-4 0.07518-5.93e-4 27.925-24.249-14-4.6e-5 28-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z"
/>
</path>
</svg>

Refresh the page to restart animation

But the really powerful feature of SVG embedding into the DOM is that it can be manipulated with javascript, which brings us endless possibilities, For example, animation can be invoked by mouse events:

<svg id="svg" version="1.1" width="200" height="200" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
<path id="shape" fill="#555" d="m28 7.5e-8 0.014 27.976 24.235-13.976-24.221 14 24.221 14-24.235-13.976-0.014001 27.976-0.013999-27.976-24.235 13.976 24.221-14-24.221-14 24.235 13.976z">
<animate id="animate1"
begin="200ms"
dur="300ms"
attributename="d"
fill="freeze"
calcMode="spline"
keySplines="0.3, 0, 0.7, 1"
to="m28 1.5691e-6 0.065406 0.037075 24.183 13.963 5.94e-4 0.07518-5.94e-4 27.925-0.06481 0.0381-24.184 13.962-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z"
/>
<animate id="animate2"
begin="700ms"
dur="200ms"
attributename="d"
fill="freeze"
calcMode="spline"
keySplines="0.3, 0, 0.7, 1"
to="m28 0 0.0654 0.037077 24.183 13.963 5.93e-4 0.07518-5.93e-4 27.925-24.249-14-4.6e-5 28-0.0654-0.03708-24.183-13.963-5.927e-4 -0.07518 5.938e-4 -27.925 0.064808-0.0381z"
/>
</path>
</svg>

<script>

const svg = document.getElementById("svg");
const animate1 = svg.getElementById("animate1");
const animate2 = svg.getElementById("animate2");
const from = animate2.getAttribute("to");
const to = animate1.getAttribute("to");
let mousein = false;

svg.addEventListener('mouseenter', () => {
mousein = true;
animate2.setAttribute("from", from);
animate2.setAttribute("to", to);
animate2.beginElement();
}, false);

svg.addEventListener('mouseleave', () => {
if (!mousein) return false;
mousein = false;
animate2.setAttribute("from", to);
animate2.setAttribute("to", from);
animate2.beginElement();
}, false);

</script>

Hover mouse on logo below:

More information about SVG animation with SMIL can be found on MDN.