Animating SVGs
How SVGs work and how to animate them
When I started to learn to code websites, there were a few different options for creating icons. You could use a sprite sheet, or use a special icon font but I always liked SVGs.
After all they are scalable, so they can be created with a small footprint. But they can also be blown up to fill any large screen without loss of definition. And as a subset of XML, they can be written inline in HTML and targeted with CSS.
Whilst the other techniques have fallen out of fashion, SVGs are here to stay.
You can create SVGs in your favourite Vector Graphic editing software, and export the code created and plonk it into your code. You can use the excellent SVGOMG tool, to optimise the code as your favourite Vector Graphic software may add some extra bloat.
You can also import SVGs as JSX, in React (although you may not want to do that).
I thought it would be interesting to get under the hood and explore how to create SVGs from scratch myself rather than solely using software.
This is a work-in-progress where I intend to uncover the basics of SVG animation and then do some follow up experiments where I animate elements of my personal website and explore generative SVGs.
The SVG element
In HTML, there is a SVG element which you can use to render a SVG document fragment. The SVG element has a viewBox attribute which defines the coordinates of your viewport. The window into your SVG world.
The first two numbers in the viewBox denote the top and left co-ordinates of the viewport and the last two numbers represent the width and length of the viewport.viewport.
<svg viewBox="0 0 100 100" />
Everything drawn in a SVG is written using co-ordinates that are relative to its viewBox which scales to the dimensions of its width and height.
Shape elements
You can add shape elements as children of an SVG. They are circle
, ellipse
, line
, polygon
, polyline
, rect
and path
.
I’m sure you can take a reasonable guess at what these shapes are. The most flexible and interesting one is the path
element.
The path
element is defined along with a path data property, d. The path data is a “series of commands” that tell the browser how the path should be drawn — in a similar way to how you might program a robot turtle.
Each command starts with a letter and, in most cases, followed by some co-ordinates.
Here, I use the ”move” command (M), along with the ”line” command (L) and the ”close-path” command (Z) to draw a polygon.
<svg viewBox="0 0 100 100">
<path d="M 0 40 L 50 0 L 100 60 L 30 70 Z" fill="none" stroke="#2c363f"></path>
</svg>
The command code can either be uppercase or lowercase:
- uppercase commands are absolute — their parameters are relative to the origin point,
- lowercase commands are relative to the previous command’s endpoint.
Our commands instruct a cursor (our turtle) to determine where to start and stop drawing and what sort of lines to draw.
Arcs and Bézier Curves
To draw curves, SVG gives us a few more commands:
A
arcs,C
cubic bézier curves, andQ
quadratic bézier curves.
The arc command lets us draw a section of an ellipse. If we write the command as a function, it takes the following parameters: a(radius x, radius y, x-axis rotation, large-arc-flag, sweep-flag, x, y)
.
Given the co-ordinates, there are four ways an arc can be drawn, so the flags determine which of these to use.
<svg
fill="none"
stroke-width="2"
>
<path stroke="#00487d"
d="M 40 40 A 25 25 0 1 0 60 60"
></path>
<path stroke="red"
d="M 40 40 A 25 25 0 1 1 60 60"
></path>
<path stroke="green"
d="M 40 40 A 25 25 0 0 1 60 60"
></path>
<path stroke="blue"
d="M 40 40 A 25 25 0 0 0 60 60"
></path>
</svg>
Bézier curves are a line from one point to another controlled by control points, quadratic bézier curves have one control point. A control point curves the line towards it, as if the control point was pulling it in its direction.
The Q command takes four arguments: the X and Y co-ordinates of the control point, and the X and Y co-ordinates of the end point.
<svg
fill="none"
stroke-width="2"
>
<path stroke="#00487d"
d="M 10 10 L 10 30 Q 10 90 70 90 L 90 90"
></path>
</svg>
A T
command will draw a quadratic bézier curve using the reflection of the previous curve’s control point.
<svg
fill="none"
stroke-width="2"
>
<path stroke="#00487d"
d="M 10 10 Q 10 50 50 50 T 90 90"
></path>
</svg>
Cubic bézier curves have two control points and they can written with the C
command and reflected with the S
command:
<svg
fill="none"
stroke-width="2"
>
<path stroke="#00487d"
d="M 50 10 C 10 60 10 70 50 70 S 100 0 50 10"
></path>
</svg>
Transform attribute
Once we have our shapes, we can transform them. SVGs give us the following transformations:
translate(x, y)
— moves the object by x and y,rotate(n, x, y)
— specifies a rotation by n degrees about a given point (x, y),skewX(n)
orskewY(n)
— specifies a skew transformation along the x or y axis by n degrees,scale(x, y)
— specifies a scale operation by x and y.
(There’s also something called matrix
which allows you do all of the above).
When we transform a shape, we create a copy of the co-ordinate system of the viewport and transform the whole lot.
Let’s see this in action:
<path
stroke="#00487d"
fill="#ffad33"
fill-opacity="0.5"
d="M 30 30 L 70 30 L 70 70 L 30 70 Z"></path>
<path
stroke="red"
transform="rotate(45 50 50)"
d="M 30 30 L 70 30 L 70 70 L 30 70 Z"></path>
<path
stroke="green"
transform="translate(-20 -20)"
d="M 30 30 L 70 30 L 70 70 L 30 70 Z"></path>
<path
stroke="blue"
transform="skewX(25)"
d="M 30 30 L 70 30 L 70 70 L 30 70 Z"></path>
<path
stroke="yellow"
transform="scale(1.5 1.5)"
d="M 30 30 L 70 30 L 70 70 L 30 70 Z"></path>
Animating shapes
SVG contains a few elements that control animations such as <animate />
, <animateMotion />
and <animateTransform />
.
Adding an animate element as a child to a shape, allows you to animate properties of its parent. Using the attributeName
specifies the name of the attribute to animate.
<svg>
<circle cx="50" cy="50" r="5" fill="yellow">
<animate
attributeName="r"
from="1"
to="50"
dur="12s"
repeatCount="indefinite"></animate>
</circle>
</svg>
Animating transforms
The animateTransform
element can animate transformations on a target element.
<path
stroke="#00487d"
fill="var(--color-highlight)"
stroke-width="2"
fill-opacity="0.25"
d="M30 60 L50 20 70 60 Z"
>
<animateTransform
attributeName="transform"
type="rotate"
from="0 50 50"
to="360 50 50"
dur="8s"
repeatCount="indefinite"></animateTransform>
</path>
Keyframes and Tweens
When animation used to be done by hand, master animators drew certain key frames and delegated drawing the frames in between to juniors. We use these terms in animation to describe Keyframes where we declare the position of a frame and Tweens where we describe how we get to the next Keyframe.
CSS allows us to use Keyframes for animatable properties (which are essentially anything with a numerical or colour value) by declaring the animation
property, or its sub-properties and a @keyframe
at-rule.
This example uses CSS Keyframes to animate a ball:
here’s the css I used:
@keyframes bounce {
from {
transform: translateY(0);
}
to {
transform: translateY(80px);
}
}
.ball {
animation-name: bounce;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
fill: var(--color-highlight);
}
Many animations can also be created using CSS by targeting SVG element properties. However CSS can’t target all SVG properties. The d
property is a good example of a SVG element property that can’t be targeted with CSS.
Animating with a javascript library
Here, I’m using GSAP to scale the circle’s X and Y dimensions to simulate how balls deform when colliding with the floor: