Animating an SVG with Greensock

Recently I created an animation inspired by a Dribbble shot by @thorstenbeeck. When I saw it I immediately felt the urge to experiment with it. I’ve been doing a lot of SVG animation in client projects and thought it would be nice to show the power of SVG’s in combination with the Greensock animation platform. Once I published it I got lots positive reactions so I thought it would be nice to explain a bit about the process I went through.

Greensock

Greensock is an amazing library which gives you unlimited possibilities when it comes to animation. Especially the support of tweening SVG’s is really useful. I recommend checking out the plugins they made. For example the Greensock DrawSVG plugin. It enables you to reveal or draw strokes of any shape you can draw in Illustrator. It makes use of the CSS properties stroke-dashoffset and stroke-dasharray. Here is the simplest way of using it:

1
2
3
4
5
const tl = new TimelineMax()
tl.to(document.querySelector('.my-svg circle'), 1, {
drawSVG: '0% 0%'
})

See the Pen DrawSVG demo by Tim Rijkse (@timrijkse) on CodePen.

Recreating Coastal Mountain View

Step by step we will create the Dribbble shot below. I personally love the colors and simple shapes. This makes it relatively easy to create in your first hand coded SVG.

Shoutout to @thorstenbeeck for creating this awesome Dribbble shot

Creating a new setup

First things first. We start with by creating a container which will be our canvas. Create an <svg> element. Next add a <rect> that covers the whole stage and then mask the <svg> with a <clipPath>. This results in the following markup:

1
2
3
4
5
6
7
8
9
10
11
12
13
<svg viewBox="0 0 500 500">
<g class="canvas">
<defs>
<clipPath id="circle">
<circle class="mask" cx="250" cy="250" r="100" />
</clipPath>
</defs>
<g clip-path="url(#circle)">
<rect class="bg" x="0" y="0" width="500" height="500" />
</g>
</g>
</svg>

Cool! Our basic markup is done. Lets add some proper styling. Adding styles can be done in a few ways. You can write them as presentational properties, inline styles or in an external stylesheet.

1
2
3
<!-- Both will do the same -->
<circle class="mask" cx="250" cy="250" r="100" fill="red" />
<circle class="mask" cx="250" cy="250" r="100" style="fill: red;" />

I prefer to write my styles in an external file. This keeps your markup clean and you can benefit of the features Sass offers you. With Sass it’s possible to define variables, this is really useful for managing colors in your SVG.

@jonaskuiler told me about this great color naming app called Sip. It’s a color picker which converts hex codes into nice descriptive color names.

Sip - The best way to 
collect, organize & share your colors.

Styles

Since my Sass file contains nothing more than some color variables I’m just going to share the whole file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Color palette
$red: #E9214F;
$burnt-sienna: #F06B4B;
$goldenrod: #F6DA71;
$scooter: #349597;
$midnight-express: #20283B;
$marzipan: #FCDC9F;
$fruit-salad: #49934E;
$goblin: #3D7C42;
$chambray: #44557E;
$port-gore: #384668;
$white: #ffffff;
$silver-sand: #BBBBBB;
// Set background color
body {
background-color: $midnight-express;
}
// Center the svg horizontally and vertically
svg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 500px;
height: 500px;
backface-visibility: hidden;
}
// Color styles
.canvas {
.bg { fill: $scooter; }
.ground { fill: $marzipan; }
.sky1 { fill: $goldenrod; }
.sky2 { fill: $burnt-sienna; }
.sky3 { fill: $red; }
.tree-left { fill: $fruit-salad; }
.tree-right { fill: $goblin; }
.mountain-left { fill: $chambray; }
.mountain-right { fill: $port-gore; }
.mountain-top-left { fill: $white; }
.mountain-top-right { fill: $silver-sand;
}

Mask animation

Sweet! This will be the base for our coastal mountain view. Of course we can’t start without having a basic animation. Let’s add some Greensock magic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Define all elements used in our markup
const svg = document.querySelector('svg')
const canvas = {
wrapper: svg.querySelector('.canvas'),
mask: svg.querySelector('.canvas .mask')
}
// Create a new GSAP timeline
const tl = new TimelineMax({
repeat: -1
})
// Tween mask from a 0 radius to its original radius defined in our markup
tl.from(canvas.mask, 1, {
attr: {
r: 0
},
ease: Elastic.easeOut.config(3, 1)
})

Boom! Our scene is all set up, lets have a look at what we’ve got at this moment. We’ve created an SVG with a background shape on it. We’ve added a circle mask to it and finally we’ve added a Greensock animation:

See the Pen Landscape Circle SVG - Part 1 by Tim Rijkse (@timrijkse) on CodePen.

Demo files on Codepen

I created a codepen collection containing all demo’s used in this article.

The sky is the limit

Now we have our scene setup it’s time to add the sky and the ground objects. We will create 4 <rect> elements; three for the sky elements and one for the ground element:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<svg viewBox="0 0 500 500">
<g class="canvas">
<defs>
<clipPath id="circle">
<circle class="mask" cx="250" cy="250" r="100" />
</clipPath>
<clipPath id="ground">
<rect x="50" y="50" width="400" height="260" />
</clipPath>
</defs>
<g clip-path="url(#circle)">
<rect class="bg" x="0" y="0" width="500" height="500" />
<g clip-path="url(#ground)">
<rect class="sky3" x="50" y="130" width="350" height="70" />
<rect class="sky2" x="50" y="200" width="350" height="50" />
<rect class="sky1" x="50" y="250" width="350" height="50" />
</g>
<rect class="ground" x="100" y="300" width="350" height="10" />
</g>
</g>
</svg>

We don’t want the sky and the ground to just be there so we need to add some animation to the timeline. We start with the ground, it needs to transition in from the bottom.

1
2
3
4
5
6
7
tl.from(canvas.ground, 0.5, {
autoAlpha: 0,
attr: {
y: '+=200'
},
ease: Power4.easeOut
}, 0.1)

We do the same for the sky elements. TimelineMax provides a stagger function which lets us stagger tweens easily. The sky elements will stagger in with a delay of 0.075:

1
2
3
4
5
6
7
8
tl.staggerFrom([canvas.sky1, canvas.sky2, canvas.sky3], 0.5, {
autoAlpha: 0,
skewY: 0,
attr: {
y: '+=90'
},
ease: Elastic.easeOut.config(1, 3)
}, 0.075, 0.25)

Our work in progress

We are getting there! Our background is all set up. Play with the ease variables to give the animations your own twist. Greensock provided a great ease visualizer.

See the Pen Landscape Circle SVG - Part 2 by Tim Rijkse (@timrijkse) on CodePen.

He who plants a tree. Plants a hope.

The trees contains two triangles. Let’s start with the first tree. Because we want to position the trees next to each other every tree gets its own group:

1
2
3
4
<g class="tree tree1" transform="translate(150 255)">
<polygon class="tree-left" points="15,0 15,50 0,50" />
<polygon class="tree-right" points="15,0 15,50 30,50" />
</g>

Repeat the above group seven times and position them corretly by changing the translate values. Since trees grow from seeds we also add a grow transition which looks like the following:

1
2
3
4
5
6
tl.staggerFrom([canvas.tree1, canvas.tree2, canvas.tree3, canvas.tree4, canvas.tree5, canvas.tree6, canvas.tree7], 0.5, {
rotation: 45,
scale: 0,
transformOrigin: 'bottom center',
ease: Back.easeOut.config(2, 1.55)
}, 0.05, 0.4)

Let’s see how that looks

As you can see the animation of the circle, sky and trees end almost at the same time. This makes the animation feel natural and smooth and interesting to look at.

See the Pen Landscape Circle SVG - Part 3 by Tim Rijkse (@timrijkse) on CodePen.

Demo files on Codepen

I created a codepen collection containing all demo’s used in this article.

It’s not the mountain we conquer, but ourselves.

Last but not least we need to add the mountain. The mountain exists of 4 triangles. The markup looks like this:

1
2
3
4
5
6
<g class="mountain" transform="translate(150 185)">
<polygon class="mountain-left" points="100,0 100,120 0,120" />
<polygon class="mountain-right" points="100,0 200,120 100,120" />
<polygon class="mountain-top-left" points="100,0 100,30 75,30" />
<polygon class="mountain-top-right" points="99,0 125,30 99,30" />
</g>

And when we add our last transition, your very first hand coded SVG is done!

1
2
3
4
5
6
7
tl.staggerFrom([canvas.mountain], 0.75, {
y: '+=50',
skewX: -200,
scale: 0,
transformOrigin: 'bottom center',
ease: Back.easeOut.config(1, .2)
}, 0.0125, 0.5)

Result

See the Pen Landscape Circle SVG by Tim Rijkse (@timrijkse) on CodePen.

As you can see drawing and tweening shapes is made really easy thanks to the Greensock platform. It’s not the code you write which is difficult, it’s the eye for detail. Getting the right timings and eases is the most important thing when it comes down to creating high quality animations. The only way up is by practising a lot. Try to recreate some Dribbble shots and add your own animations to it. I’d love to see what you make of them! Drop me a tweet!

The final code can be found on Codepen

Share Comments