Hi guys, this is my very first post and I would like to explain the process I followed to create this little SVG animation and what I learned on the way.

The goal of all this experiment was to play around with SVG and the GSAP library and explore what possibilities this gives us for the web today. It might look like a linear process in this post but I have to say, it was not! It was more like a back and forth learning process going from design to code to design again. It was a very enriching exercise.


First thing I did was to get my sketchbook and start drawing. I spent a lot of time and paper drawing, sketching, throwing lines, I didn´t know what I was going to do. All I had in mind was to create something nice, meaningful and that could use a good amount of animation. As you can see pretty much everything is valid at this stage.

I ended up iterating a bit over it and had my final design.


Once I have decided what I want to go with I take a picture of it and import it (I could scan it, I know, but at the end the picture method is way faster) to my bottom layer in Corel Draw. Although Corel Draw is not a major choice for vector drawing for me works pretty good and the interface feels way more intuitive than the adobe one.

So once I have it in my vector editor I start with the drawing. This needs a bit of thought before just starting to create and drag points through the screen. Before starting I needed to take into account these points:

  • The main branch of the plant needs to grow so it has to be an unclosed line. Using a polygon would make creating the ¨grow¨ animation way more difficult for no reason.

  • I want it to feel natural but not spending endless hours so, to create the leaves, I did only 3 or 4 different very simple designs of a leaf and play mirroring and changing widths and lengths.

  • One of the leafs is going to fall down and create a little ripple effect when it touches the floor. It will also throw a shadow. For this I created two ellipses, one with no inner color and a thin border for the ripple and the other one filled with a circular gradient from black to transparent and no forder for the shadow.

  • Trying to create the reflection effect my approach was to create a copy of the entire design, mirror it vertically and clip it inside a square. Over it, inside the clip, a square filled with a gradient from white (same as the background color) to transparent.

  • Looking at my design I also realised that the pot in the mirror is not being shown from the same angle so the reflected pot looks slightly different than the upper one. In the one below you are able to see its bottom as if the reflection came from a thick glass. You can also notice that I have removed the leaves that are not going to be visible in the shadow to remove unnecessary code.

This is the final result:


I exported the file as an SVG directly from Corel Draw. This generates an SVG file with tons of unwanted code. There are quite a few online tools to help you optimise it, I usually use Jake Archivald´s SVGOMG it is very powerful and allows you to configure multiple parameters for the optimisation.

If you want to know more about SVG optimisation tools here is a really good article from Sara Soueidan: Useful SVGO[ptimization] Tools


Now that we have the SVG in code all clean and tidy let´s get the fun started! CODEPEN!

First of all, I copied the SVG in the HTML window and styled it a bit. I extracted the styles from the section and included it into the CSS file, everything has its place! This styles come directly from the SVG editor, some of them offer the option to export them as a separate file. To make it responsive as I only have the SVG element I will give it a 'width:100%', this will expand it to the width of its container, in this case our HTML body.

For the animations I have created several timelines so that I can play with the different starting points. First one is the simplest one, the main branch animation.

  var origMain = document.querySelector('#mainBranch');
var objMain = {length:0, pathLength:origMain.getTotalLength()};

function drawLineMain() {
    origMain.style.strokeDasharray = [objMain.length,objMain.pathLength].join(' ');

TweenMax.to(objMain, animDuration, {length:objMain.pathLength, strokeWidth:30, onUpdate:drawLineMain, ease:Sine.easeOut});

It gets the element and creates a function that will be called on every iteration of the animation timeline giving back the path length of the main branch object creating our animation. In the same way but getting the mirrored main branch I have created the animation for it. Something to be aware of is that the SVG path has direction so depending on how you have created it will start growing from one end or the other.

When creating the animation of the leaves growing I realised something, each of them needed different transformation origins. For this I created an array with all the transformation origins so then iterating over it I create he tweens for each of the leaves. Using the index, I assign a slightly longer delay to each leaf. *(This needs optimisation as for the moment the tweens for the shadow leaves get created even though some of the leaves do not exist in the SVG)

  var leavesTransOrigin = ['0% 50%','100% 100%','0% 50%','80% 0%','0% 0%','100% 0%','50% 0%','100% 100%','0% 50%','100% 90%','50% 100%','100% 100%','100% 50%','30% 0%','100% 0%','100% 90%','0% 0%','100% 80%','50% 0%','0% 0%','100% 50%','100% 60%','0% 0%','80% 20%','0% 0%','100% 0%','10% 10%'];
var leavesTransOriginS = ['0% 50%','100% 0%','0% 50%','80% 100%','0% 100%','100% 100%','50% 100%','100% 0%','0% 50%','100% 10%','50% 0%','100% 0%','100% 50%','30% 100%','100% 100%','100% 10%','0% 100%','100% 20%','50% 100%','0% 100%','100% 50%','100% 40%','0% 100%','80% 80%','0% 100%','100% 100%','10% 90%'];

for(var i = 0; i < 27; i++){
    TweenMax.from('#leaf' + (i + 1), Math.random() + leafAnimDuration, {scale: 0, transformOrigin:leavesTransOrigin[i], delay:animDurationPart * i});
    TweenMax.from('#leafS' + (i + 1), Math.random() + leafAnimDuration, {scale: 0,  transformOrigin:leavesTransOriginS[i], delay:animDurationPart * i});

Next animation is for the falling leaf at the end. The code is pretty simple. I have created a new time line and added a delay to wait for the main branch and itself to finish growing. Then some rotations back and forth simulating a little bit of air blowing to end up falling and a little rotating effect when it touches the floor.

  var tlFallingleaf = new TimelineMax();
tlFallingleaf.delay(animDuration + leafAnimDuration).
          to('#leaf25', .5, {rotation:7, ease:Linear.easeInOut}).
          to('#leaf25', .4, {rotation:2, ease:Linear.easeInOut}).
          to('#leaf25', .3, {rotation:7, ease:Linear.easeInOut}).
          to('#leaf25', .2, {rotation:5, ease:Linear.easeInOut}).
          to('#leaf25', 1.5, {y:'+=960', rotation:90, transformOrigin:'50% 50%', ease:Linear.easeOut}).
          to('#leaf25', .5, {rotation:80, transformOrigin:'70% 50%', ease:Linear.easeNone}).
          to('#leaf25', .3, {rotation:85, transformOrigin:'70% 50%', ease:Linear.easeNone});

As you can see, for the shadow animation, it is not needed to animate the first 'air movement' as it will not be seen, instead I have added the correspondent delay. Also yo can notice the direction of the falling goes upwards instead of downwards as in the main animation.

  var tlFallingleafS = new TimelineMax();
tlFallingleafS.delay(animDuration + leafAnimDuration + 1.4).
          to('#leafS25', 1.5, {y:'-=960', rotation:-90, transformOrigin:'50% 50%', ease:Linear.easeOut}).
          to('#leafS25', .5, {rotation:-80, transformOrigin:'70% 50%', ease:Linear.easeNone}).
          to('#leafS25', .3, {rotation:-85, transformOrigin:'70% 50%', ease:Linear.easeNone});

To create the shadow effect, I have played with the scale. It will grow from o to 1.1 and back to 1. By growing it a little more and then back to its normal size I tried to follow a more organic movement when the leave touches the floor and rotates an instant before stopping.

  var tlShadow = new TimelineMax();
tlShadow.delay(animDuration + leafAnimDuration + 1.4).
          staggerFrom('#leafShadow', 1.5, {scale:0, transformOrigin:'50% 50%', ease:Linear.easeNone}).
          staggerTo('#leafShadow', .5, {scale:1.1, transformOrigin:'50% 50%', ease:Linear.easeNone}).
          staggerTo('#leafShadow', .3, {scale:1, transformOrigin:'50% 50%', ease:Linear.easeNone});

And the last animation was the ripple effect. I have achieved this by playing with its scale again as with the shadow. The trick with this one is the opacity. The animation is done in two steps, the first one grows from 0 to its natural scale and a second one where it grows bigger and the opacity drops to cero. Also in this one I had to be careful with the eases to be sure the animation didn´t flicker and runned smoothly.

  var tlRipple = new TimelineMax();
tlRipple.delay(animDuration + leafAnimDuration + 2.7).
          from('#leafRipple', .4, {scale:0, transformOrigin:'50% 50%', ease:Linear.easeIn}).
          to('#leafRipple', .4, {scale:2, opacity:0, ease:Linear.easeOut});

And here is the final result!

See the Pen Grow (GSAP SVG animation) V1.1 by Fabio (@FabioG) on CodePen.

Hope you guys liked it, this has been my learning process and all the feedback is more than welcome, I know this can use a lot of refactoring and improvement. Have you tried something similar? What problems have you faced? How did you tackle them? Would you do something different? Write me!

Thanks to Chris Gannon for his suggestions and Robert Borghesi and Dipun Mistry for encouraging me to write the post.

3,398 8 65