SVG animations with CSS
SVGs are lovely: they are small, scalable, and easily editable by a developer. They can also be animated using CSS, which means that while keeping their tiny size, they can do much better than a GIF. Need those champagne bubbles to bubble a little faster? No problem. Whatever the designer suggests can be done with a few lines of CSS, without significantly modifying the original asset.
The main ingredients: @keyframes and CSS animation •
@keyframes
are a way to define animation functions. They need a name, a few timing points (at least a from
and a to
, or percentages), and instructions on where to be at each of these time points.
CSS property animation
takes name, duration, timing function (default: ease
), delay, iteration count (default: 1
; number or infinite
) etc.
Take this beating heart as an example (it’s 11.15KB
):
Here’s the code itself — you can check the source, I’m not cheating ;)
HTML
<img src="heart.svg" class="heart" />
CSS
.heart {
width: 120px;
animation: pulse .6s ease-in infinite alternate;
}
@keyframes pulse {
from {
transform: scale(.94);
}
to {
transform: scale(1);
}
}
That’s boring. Is that all SVG can do? •
Of course not.
SVGs are written in XML, and can contain paths, texts, shapes (lines, circles etc.) — and <g>
for groups of these. We just have to name these building blocks (with CSS classes and IDs) to animate them separately.
Consider this image:
Its source code has the following structure:
<svg>
<!-- cloud in the back -->
<path style="..." d="...">
<!-- cloud in the front -->
<path style="..." d="...">
<path style="..." d="...">
<g>
<!-- cloud fluffs -->
<path style="..." d="...">
<path style="..." d="...">
<path style="..." d="...">
<!-- rain drops -->
<path style="..." d="...">
<path style="..." d="...">
<path style="..." d="...">
<!-- ...and many more of them -->
</g>
</svg>
I would like the little rain drops to fall, and the two clouds to float.
For this, we will have to slightly modify the original SVG. Namely:
- The front cloud is made up of several shapes, let’s put them in a group so that they can move together
- The back cloud has to float, let’s give it an ID as well
- Rain drops will have to do the same thing, but timed randomly, so each of them will need an ID.
- I will also make the back cloud a bit darker, because why not.
An illustration of these changes:
<svg>
<!-- cloud in the back: id added -->
<path id="cloud-back" style="fill:#8BbbD8;" d="...">
<!-- cloud in the front: grouped -->
<g id="cloud-front">
<path style="..." d="...">
<path style="..." d="...">
<!-- cloud fluffs added to front cloud's group -->
<path style="..." d="...">
<path style="..." d="...">
<path style="..." d="...">
</g>
<!-- rain drops are all individuals, but if we are lazy, we can "class"
a few together. some will move at the same time, but it's fine -->
<path class="drop1" style="..." d="...">
<path class="drop2" style="..." d="...">
<path class="drop3" style="..." d="...">
<path class="drop2" style="..." d="...">
<!-- and so forth, quasi-randomly assigning classes -->
</svg>
Now we just have to add some css animations, and we’re done.
#cloud-front {
animation: float 3s ease-in-out infinite alternate;
}
#cloud-back {
transform: translateX(12px);
animation: float2 2.2s .2s ease-in-out infinite alternate;
}
.drop1 {
animation: rainfall .5s linear infinite;
}
.drop2 {
animation: rainfall .6s .1s linear infinite;
}
.drop3 {
animation: rainfall .7s .3s linear infinite;
}
.drop4 {
animation: rainfall .6s .4s linear infinite;
}
@keyframes float {
from { transform: translateX(0); }
to { transform: translateX(22px);}
}
@keyframes float2 {
from { transform: translateX(14px);}
to { transform: translateX(0); }
}
@keyframes rainfall {
from {
transform: translate(0, 0);
opacity: 1;
}
to {
transform: translate(-15px, 30px);
opacity: .1;
}
}
That’s it — stay dry!