How to Create the VueConf Load Animation (While Learning About Vue.js and SVGs in the Process)

Vue.js recently released a new landing page for VueConf 2017 that included a nice animation. How about we make that animation and learn more about Vue.js and SVGs?

What you’re getting into: A how-to on making the animation on the landing page for VueConf 2017. Specifically, I will go through the process of creating SVGs, breaking down the animation, using CSS animations to replicate the animation, and controlling the application/timing of animations using Vue.js. It is very lengthy and detailed. I recommend doing this in chunks if needed. You can use Pocket to easily save this article for later so you don’t forget to return to it.

(Update 3/27/17): I have begun the process of creating a video course for providing a very clear and detailed break down of SVGs. I will cover explaining SVGs “under the hood”, using Affinity Designer to create detailed vector graphics, animation with CSS, GreenSock, Mo.js, and Anime.js, as well as some practical applications of UX animations. You can book access here.



The Backstory

This tutorial is particularly delightful for me because Vue.js is what saved me from impostor syndrome.

After graduating with a degree in Computer Science, I had no idea what I wanted to do in the world of development and still did not feel confident in my abilities my any stretch. Quickly, I sunk into impostor syndrome. Right before I graduated, however, I was introduced to online businesses, entrepreneurship, and startups through a podcast called Smart Passive Income Podcast by Pat Flynn.

Because of this experience, discovering modern web development after my graduation was a breath of fresh air due to its wonderful aroma of energy, enthusiasm, entrepreneurial leanings, and self-starter mentality. This animated me to decide to focus on finding a career in web development.

So, I jumped right into learning all of the modern web development, most notably, React. Like many people, I had set the bar of becoming a full-stack web developer.

I plowed through video courses, Medium posts, and all other sorts of goodies and felt like I was making decent progress. Eventually, I was able to make this interactive Wrigley Field scoreboard.

As I was learning React, I was also getting really into creating vector graphics with Affinity Designer. Making vector graphics started as a fun mental break from a stressful 9–5 job working support while knowing I wanted to be a web developer and an entrepreneur, and I was doing neither.

To my surprise, I started to realize that vector graphics, and design skills in general, was not all that complicated and only for people with an art background. I was making cool, bold images from simple shapes and a bold selection of colors.

I started realizing that my design skills were actually getting legitimate. I was gaining back confidence. However, I was still trying to learn how to take my design skills to the web.

After some experimentation and self-teaching with pure CSS images, SVGs, and making websites with Bootstrap, I eventually came across Vue.js. Vue.js was another breath of fresh air. I found it was so easy to wrap my head around. It was approachable, readable, and way less intimidating than anything I had come across while learning web development.

I started pairing Vue.js with a frontend emphasis on design. I made mini-apps like a Pokemon battle app, a Mad Libs app, a Pomodoro clock, and more. I also used Vue.js to sprinkle animations and other very particular skills. The awesome part about Vue.js is that it can be used from something as particular as animations to as broad as dynamic web applications.

While I was learning Vue.js and achieving all of this, I was completely cured, finally, of impostor syndrome. I knew that I had found a marketable, specific skill in frontend development with an emphasis on design and creativity.

Coding Artist was born out a desire to provide resource after resource to help people experience the journey that I have just described of learning how to find a marketable skill-set in design-heavy frontend development.

With this backstory in mind, I couldn’t help to smile when I saw this cool animation on the VueConf landing page made by Damian Dulisz.

Vue.js is one of the main reasons that I conquered impostor syndrome, gained the confidence to tackle new challenges like animations, and formed Coding Artist. Now, I have the opportunity to write an extensive how-to on creating the animation used on their new landing page for the VueConf. As previously mentioned, I am quite delighted to do this tutorial.

Moreover, I hope this tutorial is another practical guide of how to use SVGs, animations, and Vue.js in real-world applications. It truly is a marketable skill that is 100% ok to focus most of your attention on.

With all this said, let’s get to it.

Breaking Down the Problem

When I first started to see the use of SVGs and animations like the one we are going over in this post, I had the reaction of “Ooh, that’s so cool! I just wish I had a clue on how to do that.”

After putting in the time to learn, I have realized that making an animation like this isn’t very hard if you can correctly break down the problem. So, let’s do that.

Here is what we need to break down in the broadest/most obvious sense:

  1. How to create the graphics
  2. How to create the animations

Now, let’s break down what we have to do in a more specific sense for each point.

What we need to do to create the graphics:

  1. Break down what graphics we need to create
  2. Break down the shapes that make up each graphic
  3. Create the shapes to form each graphic as a vector graphic using a tool like Illustrator, Sketch, or Affinity Designer (I will use the latter)
  4. Optimize the images for exporting
  5. Export images as SVGs
  6. Import the SVGs and add some styling to arrange the graphics in the position they will be in at the completion of the animation

What we need to do to create the animations:

  1. Slow down the complete animation
  2. Determine the animations for each specific graphic in a non-technical sense
  3. Write the CSS animations to do the animations we determined in step 2
  4. Figure out how to handle to organize the animations by using Vue.js

Note: The graphics will be made using Affinity Designer and we will code this via Codepen.


Final Demo/Code: http://codepen.io/mikemang/pen/JWNOrr/

Breaking Down the Graphics We Need to Create

As a good exercise, try to see if you can determine what graphics we need to make and what shapes will form each graphic. Jot this down and then we will go over it.


VueConf Logo

The VueConf logo is the Vue.js logo upside down. It turns out that looking at it outside down might make it easier to see what shapes we need.

We need three triangles on top of each other: one small white triangle, one medium blue triangle, and one large green triangle.

Sun

The sun graphic is very easy. It is two circles on top of each other: one smaller white circle and one larger green circle.

Bush

The bush is also very simple. It is two circles: one larger green circle and one smaller green circle that is on the right side of the larger circle.

Trees

The trees continue the pattern of simplicity. The left tree is a blue line, a larger green circle, and a smaller green circle on top. The right tree is just a blue line and a large circle.

Title

For sake of time, I will not be doing the title in this tutorial.

Creating the Graphics

We will create the shapes to form each graphic. We can do this be using a vector graphic GUI.

I will be using Affinity Designer for this tutorial, but you could alternatively use Sketch, Adobe Illustrator, or Inkscape. Affinity Designer is the cheapest of the paid options at $50 and my personal favorite but any will do. Inkscape is free but limited.

When you’re ready, let’s get started.

VueConf Logo

Let’s start by doing the larger outer triangle.

In Affinity Designer, I can select the triangle tool like so:

Drag out a large triangle and update the color using this hex code for the green color:

#41B883

We now have:

Next, you can either drag out another triangle to be a bit smaller, or you can duplicate using CMD + J and resize by dragging a corner while holding SHIFT. (You can see all shortcuts here)

This triangle should snap to the bottom of the larger triangle. Note, you may need to adjust the layers so this triangle is on top using the following:

Here is the blue color:

#35495E

We should end up with the following:

We can repeat to add a smaller white triangle. Here is the hex code:

#FFFFFF

We now have the complete logo.

Now, we want to optimize our logo for when will export it.

You can select the move tool and drag over each triangle to select them.

Here’s a clip of this concept in action:

Next, hit CMD + G (or Layer → Group) to group these shapes which we have selected.

Next, take a look at the layers tab on the right-hand side of the screen:

We can see that we have grouped our layers/shapes. Next, hover over (Group), double click, and name this “VueConf Logo”.

For good practice, let’s also uncollapse the group:

Now, name each triangle with the following syntax:

VueConf Triangle [Color]

We should now have:

By grouping the logo, we have essentially said: “hey, these shapes grouped together will form a graphic”. Now, we can export this graphic and it will generate one block of SVG code. The SVG block for our entire logo graphic will have tags for each shape. By naming each shape, we will be able to distinguish the tags for each shape in our SVG code.

This will make more sense when we examine the SVG code and import it into our Codepen project.

For now, let’s repeat all these steps for our other graphics.

Sun

The sun should be really easy.

Using the ellipse (circle) tool, we can drag out a larger green circle.

Then, you can duplicate and resize or drag out another smaller white circle.

Lastly, you can just drag the white circle into the middle of the green circle.

Here’s a video of this concept in action:

Again, here are the colors needed:

//green
#41B883
//white
#FFFFFF

We should now have:

Note, the positioning of the graphics doesn’t really matter since we will ultimately be doing this with CSS in our Codepen project.

The last step is to again group and name each shape:

Bush

Drag out a green circle. Then, create another green circle in your preferred method.

Use the move tool to drag the smaller circle to its correct spot.

Green color:

#41B883

Once you have finished this, group the graphic, name the graphic, and then name each individual shape in that graphic. Note: I named the circles Bush Circle Left and Bush Circle Right since they have the same color.

You should now have the hang of this process.

Trees

The first tree, Tree Left, can be made by first dragging a thin blue rectangle/line.

Blue color:

#35495E

Next, duplicate this shape (CMD + G) and hold shift while dragging it to the right. Holding shift will retain the same vertical positioning.

With the base for our Tree Left and Tree Right, let’s go ahead and add a medium circle to Tree Left and a large circle to Tree Right which we will snap to be horizontally centered on the bases of our trees. This may take some fiddling around to get the right size and position.

Green color:

#41B883

Lastly, we just need to add another smaller circle on our Tree Left.

Now, we can wrap up by group each tree, naming them Tree Left and Tree Right, and naming each shape within these graphics.

Exporting As SVGs

The next step is to export each graphic as an SVG file.

To do this, simply click a graphic, go to File Export. Then, select the SVG icon and choose “Selection without background” under Area. Hit Export.

Save the file locally naming it [graphic name].svg (i.e. Bush.svg).

Preparing to Import

Open up this template and fork it: http://codepen.io/mikemang/pen/f8f28ae6adcc716d097a07a2bfb2d00d

As you can see, I have already created a box which will be the container for our complete graphic.

Importing the SVGs

(Update 3/27/2017): I recommend using SVGOMG in place of the method I describe below to optimize the SVG files and get the SVG code.


With the SVG files saved, the next step is to import them into our Codepen project.

First, we need to get the SVG code.

To do that, we can drag the SVG files into a Chrome tab. Here’s a clip of this in action:

Let’s start with the VueConfLogo.svg tab.

Right-click the image and select View Page Source.

We now see the SVG code we need:

We just need this:

VueConf Logo

<svg width="100%" height="100%" viewBox="0 0 191 169" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="VueConf-Logo"><path id="VueConf-Triangle-Green" d="M95.04,0l95.04,168l-190.08,0l95.04,-168Z" style="fill:#41b883;"/><path id="VueConf-Triangle-Blue" d="M95.04,67.95l61.624,100.05l-123.248,0l61.624,-100.05Z" style="fill:#35495e;"/><path id="VueConf-Triangle-White" d="M95.04,122.992l27.51,45.968l-55.02,0l27.51,-45.968Z" style="fill:#fff;"/></g></svg>

I will simply provide the SVG codes for the rest of our graphics.

Sun

<svg width="100%" height="100%" viewBox="0 0 89 87" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Sun"><ellipse id="Sun-Circle-White" cx="44.28" cy="43.2" rx="44.28" ry="43.2" style="fill:#41b883;"/><ellipse id="Sun-Circle-Green" cx="44.28" cy="43.2" rx="26.88" ry="26.224" style="fill:#fff;"/></g></svg>

Bush

<svg width="100%" height="100%" viewBox="-5 -20 70 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Bush"><ellipse id="Bush-Circle-Left" cx="29.4" cy="28.32" rx="29.4" ry="28.32" style="fill:#41b883;"/><ellipse id="Bush-Circle-Right" cx="58.8" cy="38.76" rx="18.562" ry="17.88" style="fill:#41b883;"/></g></svg>

Tree Left

<svg width="100%" height="100%" viewBox="0 -10 77 147" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Tree-Left"><rect id="Tree-Left-Base" x="33.171" y="42.234" width="10.32" height="104.766" style="fill:#35495e;"/><ellipse id="Tree-Left-Circle-Bottom" cx="38.331" cy="59.367" rx="38.331" ry="36.122" style="fill:#41b883;"/><ellipse id="Tree-Left-Circle-Top" cx="38.331" cy="23.245" rx="24.666" ry="23.245" style="fill:#41b883;"/></g></svg>

Tree Right

<svg width="100%" height="100%" viewBox="0 -16 110 171" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Tree-Right"><rect id="Tree-Right-Base" x="49.615" y="54.96" width="10.32" height="115.2" style="fill:#35495e;"/><ellipse id="Tree-Right-Circle" cx="54.775" cy="56.76" rx="54.775" ry="56.76" style="fill:#41b883;"/></g></svg>

Let’s unpack our Tree Right SVG code so we understand what this code means.

In short, the entire SVG tag is one graphic/image. Within the image, there were various shapes that were created to form that image. In this SVG tag, we have other tags which are code for the shapes created.

<ellipse id="Tree-Right-Circle" cx="54.775" cy="56.76" rx="54.775" ry="56.76" style="fill:#41b883;"/>
<rect id="Tree-Right-Base" x="49.615" y="54.96" width="10.32" height="115.2" style="fill:#35495e;"/>

In Tree Right, we had 1 circle and 1 rectangle which we named before exporting.

Take a look again and notice how the ellipse and rectangle above hasvean id with the name we specified:

<ellipse id="Tree-Right-Circle" cx="54.775" cy="56.76" rx="54.775" ry="56.76" style="fill:#41b883;"/>
<rect id="Tree-Right-Base" x="49.615" y="54.96" width="10.32" height="115.2" style="fill:#35495e;"/>

By naming this before exporting, we know what shapes these tags refer to.

If we wanted, we could add a class to either of the shape tags and manipulate specific CSS properties for just that one particular shape in the Tree Right image. Meaning, we could also add animations for just this one shape.

Pretty cool, huh?!

Moving on, let’s add these SVG tags into Codepen.

Start with the VueConf Logo code and paste it right under <div class=”box”>. Remove the height and width values at the beginning of the SVG tag and add the class vueconf-logo.

<div class="box">

<svg class="vueconf-logo" viewBox="0 0 191 169" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="VueConf-Logo"><path id="VueConf-Triangle-Green" d="M95.04,0l95.04,168l-190.08,0l95.04,-168Z" style="fill:#41b883;"/><path id="VueConf-Triangle-Blue" d="M95.04,67.95l61.624,100.05l-123.248,0l61.624,-100.05Z" style="fill:#35495e;"/><path id="VueConf-Triangle-White" d="M95.04,122.992l27.51,45.968l-55.02,0l27.51,-45.968Z" style="fill:#fff;"/></g></svg>

</div>

Now, the logo is right in the center of the container. Normally, you would have to add the size and position for the class you gave the SVG code. However, I have already written the classes for you in the template.

Repeat what we just did, however, make sure to use the appropriate class name:

sun
bush
tree-left
tree-right

When complete, we now have the graphics positioned in the correct places:

Now, it’s time to add animation.

Breaking Down the Animation

To make things easy, I have slowed down the animation 8x so we can get a good idea of how it works.

Watch the video and write down what the animations do in a non-technical manner. You want to look for the animation as well as the timing of the different animations for each shape in each graphic.

Here are my notes:

VueConf Logo

Slides down from the top of the screen and is horizontally centered, drops down just past the vertical and horizontal center point and bounces up a tad higher.

Flips 180 degrees.

Sun

Did not move from off the screen.

Grows and shrinks.

Bush

Did not move from off the screen.

Bush Circle Left grows and shrinks.

Bush Circle Right starts growing when Bush Circle Left fully grows and then shrinks.

Tree Left

Tree Left Base grows vertically then the Tree Left Circle Bottom spawns.

Tree Left Circle Bottom grows and shrinks. When complete, the Tree Left Circle Top spawns, grows, and shrinks.

Tree Right

Tree Right Base grows vertically then the Tree Right Circle spawns.

Tree Right Circle grows and shrinks.


Now, we need to make notes about the timing of when the each graphic spawns in comparison to the other graphics. (i.e. Tree Left Base spawns right after VueConf Logo rotates)

Here are my notes:

VueConf Logo completes.

Tree Left Base spawns and starts growing.

Right after Tree Left Base fully grows, Tree Right Base spawns and starts to grow.

When Tree Right Base fully grows, Bush Circle Left spawns and grows.

Sun spawns and starts growing when Tree Right is complete.


With these notes, we should have enough to start writing the CSS animations.

Writing the CSS Animations

(Update 3/27/17): Why am I using CSS animations and not an animation library such as GreenSock? In short, CSS animations are easier for getting started with animations. I do not want to introduce an external library in a blog post that is already very detailed. However, I would recommend GreenSock for anyone who is comfortable with how animation works in CSS. You can book access here for a video course I will be releasing covering CSS, GreenSock, Mo.js, and Anime.js animations with SVGs.

VueConf Logo

To start off, we need to complete the animation for the VueConf logo to do what we had in our notes:

//1
Slides down from the top of the screen, drops down just past the vertical and horizontal center point and bounces up a tad higher.
//2
Flips 180 degrees.

We will write two different animations.

The first part we can call slideInBounce. Here’s the shell:

@keyframes slideInBounce {

}

Now, we need to add changes in CSS properties at specific points in the animation.

@keyframes slideInBounce {

0%{
top: -100%;
}
70%{
top: 33%;
}
100%{
top: 25%;
}
}

Here, we define that at 0% of the animation the VueConf logo will be above the container. At 70% of the animation, we want it to be slightly lower than it’s final position. This creates the bounce effect since at 100% of the animation it will return to a top 0f 25% which the resting position in the center of our container.

We have to add one more line at each of these points.

In the original animation, the VueConf logo enters upside down from its resting position. Therefore, the logo enters in already flipped and our next animation will flip the logo back to normal.

We flip the logo upside down by using transform: rotate(-180deg).

@keyframes slideInBounce {
0%{
transform: rotate(-180deg);
top: -100%;
}
70%{
transform: rotate(-180deg);
top: 33%;
}
100%{
transform: rotate(-180deg);
top: 25%;
}
}

The use of the negative will make sense once we can see this in action.

To get this animation running, we need to add an animation statement in the vueconf-logo class.

.vueconf-logo {
position: absolute;
height: 50%;
width: 50%;
top: 25%;
left: 25%;
animation: slideInBounce 1s 1;
}

Here, we have three parts of the animation statement.

  1. The animation name (slideInBounce)
  2. The duration of the animation (1s)
  3. The number of iterations (1)

Now, we should see the animation working.

Next, let’s add another animation called flip so that the logo will rotate clockwise into it’s resting position.

@keyframes flip{
0%{
transform: rotate(-180deg);
}
100%{
transform: rotate(0deg);
}
}

We define the animation just like the previous. Here, we only want to do one change from 0% to 100%. We want the logo to rotate 180 degrees from -180degrees to 0 degrees.

We can then add another animation statement to vueconf-logo:

.vueconf-logo {
position: absolute;
height: 50%;
width: 50%;
top: 25%;
left: 25%;
animation: slideInBounce 1s 1,
flip 0.5s 1s 1;

}

By adding a comma to the previous statement, we can chain another statement.

We call our animation flip for a duration of 0.5 seconds. 1s refers to the animation-delay.

Let’s think about this.

We have two animation statements now. However, we want to run flip after slideInBounce completes. If slideInBounce lasts for 1 second, then flip needs to be delayed 1s.

Lastly, we again specified an iteration of 1.

We are now done with the VueConf Logo.

Tree Left

Tree Left Base

First, let’s review our notes:

Tree Left Base grows vertically then the Tree Left Circle Bottom spawns.

Since every other graphic wait for the VueConf logo to complete before appearing, they will need to be approached differently.

There are ways in which we could stick to just CSS animations, but we can also use Vue.js which seems appropriate.

Before we add start doing our Vue.js code, let’s recap how we will need to handle Tree Left’s animations since it has to wait for VueConf logo to finish.

  1. We will use Vue’s conditional rendering so Tree Left Base, Tree Left Circle Bottom, and Tree Left Circle Top will only render if a flag is set to true
  2. The flag controlling the rendering of Tree Left Base will be set to true after 2 seconds to wait for the VueConf logo to finish.
  3. Using class binding, Tree Left Base will appear and grow vertically until it’s animation is complete.
  4. Using the same techniques, Tree Left Circle Bottom will appear when Tree Left Base completes and will have a grow and shrink animation.
  5. Then, we will do the same with Tree Left Circle Top except it will start after Tree Left Circle Bottom completes.

Let’s start by adding the shell of a new Vue instance called app. I have already included the Vue.js script in the template.

var app = new Vue({
el: "#app",
data: {

},
methods: {

}
})

Now, let’s add a div with an id of app so we can establish a communication between our HTML and this Vue instance.

<div id="app">
<!-- Everything else stays here -->
</div>

Cool. Now, let’s add the flags in our data that will be for conditional rendering for each shape in Tree Left.

var app = new Vue({
el: "#app",
data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false

},
methods: {

}
})

We set all these flags to false since we want each shape to not render by default.

Next, we just need to add v-if = “[flag name]” for each shape in our HTML so we make the connection with the Vue instance to render the shape conditionally.

<svg class="tree-left" viewBox="0 0 77 147" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Tree-Left">
<rect v-if="showTreeLeftBase" id="Tree-Left-Base" x="33.171" y="42.234" width="10.32" height="104.766" style="fill:#35495e;"/>
<ellipse v-if="showTreeLeftCircleBottom" id="Tree-Left-Circle-Bottom" cx="38.331" cy="59.367" rx="38.331" ry="36.122" style="fill:#41b883;"/>
<ellipse v-if="showTreeLeftCircleTop" id="Tree-Left-Circle-Top" cx="38.331" cy="23.245" rx="24.666" ry="23.245" style="fill:#41b883;"/></g></svg>

We can no longer see Tree Left as planned.

Awesome!

The next step is to add the logic to set showTreeLeftBase to true after 2 seconds (when VueConf logo completes). To do this, we will make use of Vue’s lifecycle hooks.

With a simple example, conditional rendering is usually dependent on a user’s input. For instance, I function might be called on the click of a button to set a flag to true and cause something to render.

In our example, we want to call a function that will set showTreeLeftBase and cause Tree Left Base to render after two seconds (no user input).

To do this, we add the following lifecycle:

var app = new Vue({
el: "#app",
data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false
},
methods: {

},
created () {
//call a function that will set setTreeLeftBase to true
}
})

The way this lifecycle works is that we can call a function when our app is created. By doing this, a function is being triggered by a lifecycle, when the app is created, as opposed to from a user input.

Let’s make a function called toggleTreeLeftBase and add the call in the created lifecycle:

var app = new Vue({
el: "#app",
data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false
},
methods: {
toggleTreeLeftBase: function () {

}
},
created () {
this.toggleTreeLeftBase()
}
})

Next, let’s add the line that sets setTreeLeftBase to true in this new function.

var app = new Vue({
el: "#app",
data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false
},
methods: {
toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
}
},
created () {
this.toggleTreeLeftBase()
}
})

Our Tree Left Base is now appearing.

Next, we need to add the logic so that the function isn’t called until 2 seconds pass after the created lifecycle since VueConf logo has to finish.

We can use setTimeout for this like so:

created () {
setTimeout( () => {
this.toggleTreeLeftBase()
}, 2000)
}

Note: this may look like weird syntax since it is ES6 (modern JavaScrip syntax). “() =>” is just shorthand for “function ()”.

setTimeout works to delay when code within it will run. In our case, this.toggleTreeLeftBase() will be called 2000 milliseconds (2 seconds) after creation, giving us the 2 second delay to let the VueConf logo to finish.

We can now see Tree Left Base spawn after VueConf logo finishes.

Sweet!

We want Tree Left Base to have an animation, however. Vue provides a few ways in which we can do this. One option is to use transition effects which allow us to specify CSS animation when something that is conditionally rendered appears. I talk about this in lengthy detail in my video course. It is the better option, however, I think it is easier to go with our other option since there’s already been a lot to digest in this post.

The second option which we will utilize is class binding with Vue.js. By using class binding, we can dynamically control what classes, or classes, is currently active on a div in our HTML.

Here’s what we need to do to get this working:

  1. Write a class that an animation that will have Tree Left base grow vertically in one iteration.
  2. In the same function where we spawn Tree Left Base, we will add the class with an animation to be active on the Tree Left Base div.

By doing this, we will get the animation that we need.

First, let’s write the shell of the class that will call an animation. We can call it growActive.

.growActive{

}

Second, let’s define the shell of an animation which we will call grow.

@keyframes grow{

}

We want the Tree Left Base to grow vertically so from 0% into the animation to 100% into the animation, we need to change the height from 0% to 100%.

@keyframes grow{
0%{
height: 0%;
}
100%{
height: 100%;
}
}

Next, we can add the animation statement in growActive to call this animation once. grow should run once for 0.5 seconds, so we do:

.growActive{
animation: grow 0.5s 1;
}

Now, let’s tell our HTML that we want our Vue instance to control the class that is active. We will use v-bind:class.

<rect v-bind:class="treeLeftBaseClass" v-if="showTreeLeftBase" id="Tree-Left-Base" x="33.171" y="42.234" width="10.32" height="104.766" style="fill:#35495e;"/>

In the code above, we added the v-bind:class=”TreeLeftBaseClass” to the shape tag for our Tree Left Base found in the SVG code for Tree Left. Now, the class that is active on this shape will be determined by data in our Vue instance called treeLeftBaseClass, which we need to define.

var app = new Vue({
el: "#app",
data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: ""
},
methods: {
toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
}
},
created () {
setTimeout( () => {
this.toggleTreeLeftBase()
}, 2000)
}
})

Now, our Tree Left Base has a class that is controlled by treeLeftBaseClass in our Vue instance. We assigned it no class by default since we only want the class to be added when Tree Left Base loads.

The last step for Tree Left Base is to change treeLeftBaseClass inside of the toggleTreeLeftBase function which is where Tree Left Base is toggled on.

var app = new Vue({
el: "#app",
data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: ""
},
methods: {
toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
this.treeLeftBaseClass = "growActive"
}
},
created () {
setTimeout( () => {
this.toggleTreeLeftBase()
}, 2000)
}
})

Our animation is now working by using class binding!

There’s just one slight problem. We want it to grow from the bottom so we need to quickly adjust our grow animation.

@keyframes grow{
0%{
transform: translatey(100%);

}
100%{
transform: translatey(0%);
}
}

By using this animation, it is now growing from the bottom.

I’ll admit that it’s tempting to move on since this is going to require a bit of explanation as to why this works. However, I always stress that I’ll do my absolute best to never make assumptions in my teaching. Therefore, I’m going to go ahead and explain this.

translateY is used to move something up or down (think y-coordinate on a graph). Here’s the same animation being applied to a rectangle in a different project:

This black rectangle has a height of 20% (relative to the entire window). translateY(100%) is pushing it down 100% of its height, so 20%. It returns to translateY(0%), not pushed down at all, at the end of the animation. There is no growing effect but rather a shifting effect.

So, why does this work in our example?

Let’s get a visual of Tree Left. Add a border to the tree-left class:

.tree-left{
position: absolute;
height: 25%;
width: 30%;
left: 0%;
bottom: 25%;
border: solid red 3px;
}

We see that Tree Left is within a box.

Change the width of .tree-left to 20%.

The height and width are controlling the box around Tree Left.

What is going on here?

SVGs stands for scalable vector graphics. In short, it doesn’t work like a traditional image which you can force a specific height and width. It won’t be able to automatically maintain the correct height by width ratio.

Like in the image above visualizes, you can have an image that originally is 200 x 200 pixels that can be morphed into a different size without maintaining the ratio. Instead, it is able to scale and maintain the ratio.

To get a detailed understanding of this, I will punt and recommend this great article by CSS-Tricks.

All you need to grasp in order to understand our translateY animation is that there is a box around Tree Left for scaling purposes. If we push Tree Left down below the box, we wouldn’t be able to see it.

With that in mind, let’s remove the border and re-examine the grow animation:

@keyframes grow{
0%{
transform: translatey(100%);

}
100%{
transform: translatey(0%);
}
}

Here, we push Tree Left Base down 100%, due to translateY(100%), so it is entirely below of the box and invisible.

When it slides back up, due to translateY(0%), it is going from below the box entirely to being with the box. Until we fully hit the 100% translation of Y, there is going to be part of Tree Left Base being cut off. For that reason, we have a “grow” effect from bottom to top by “revealing” Tree Left Base over the time of the duration.


Alright. Let’s take a deep breath and recap. We now have a working animation for Tree Left Base when it appears.

We were able to do this by sprinkling Vue’s class binding and conditional rendering on top of our CSS animations.


Tree Left Circle Bottom

The good news is that with Tree Left Base out of the way, everything else should be easier to grasp and complete.

Let’s push through and add the Tree Left Circle Bottom.

First, let’s review our notes on the animation.

Tree Left Circle Bottom grows and shrinks.

We also need to keep in mind the timing. This should appear after the Tree Left Base animation completes. Let’s start by just getting it to appear on time. Then, we can add the animation.

We can use a similar approach for delaying the animation by using a setTimeout. This time, however, the setTimeout will be inside toggleTreeLeftBase

methods: {
toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
this.treeLeftBaseClass = "growActive"
setTimeout( () => {

}, 500)

}
},

If you recall, this function is called in the Created lifecycle which is delayed by 2 seconds using a setTimeout:

created () {
setTimeout( () => {
this.toggleTreeLeftBase()
}, 2000)
}

When this function is called, Tree Left Base spawns and is give a class with an animation which lasts 0.5 seconds.

Therefore, by adding a setTimeout within toggleTreeLeftBase, we can call a function named toggleTreeLeftCircleBottom after 0.5 seconds (500 milliseconds) in order to let the Tree Left Base animation finish.

Let’s add the call to toggleTreeleftCircleBottom which we will write shortly.

methods: {
toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
this.treeLeftBaseClass = "growActive"
setTimeout( () => {
this.toggleTreeLeftCircleBottom()
}, 500)
}
},

Next, let’s add toggleTreeLeftCircleBottom and the logic so we can have Tree Left Circle Bottom appear.

toggleTreeLeftCircleBottom: function () {
  this.showTreeLeftCircleBottom = true
}

We should now have Tree Left Circle Bottom appearing after Tree Left Base finishes as we hoped.

Now, we need to handle the animation.

We will again use the class binding.

First, let’s add the v-bind:class in the Tree Left Circle Bottom shape tag in our HTML.

<ellipse v-bind:class="treeLeftCircleBottomClass" v-if="showTreeLeftCircleBottom" id="Tree-Left-Circle-Bottom" cx="38.331" cy="59.367" rx="38.331" ry="36.122" style="fill:#41b883;"/>

Second, we can add some data to our Vue instance which will assign the class.

data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: ""
},

Now, we make the connection so Tree Left Circle Bottom‘s class will be controlled by treeLeftCircleBottomClass. We want to give it no class to start, and we will add a class containing an animation in the same function when we toggle it on. Let’s do this now and assign a class “growShrinkActive”.

toggleTreeLeftCircleBottom: function () {
  this.showTreeLeftCircleBottom = true
this.treeLeftCircleBottomClass = "growShrinkActive"
}

Next, let’s create a class called growShrink in our CSS.

.growShrinkActive{

}

The next step is to define an animation called growShrink.

@keyframes growShrink{
0% {
transform-origin: center;
transform: scale(0);
}
50% {
transform-origin: center;
transform: scale(1.3);
}
100% {
transform-origin: center;
transform: scale(1);
}
}

Here, we use transform: scale( [scale amount] ) to handle the grow and shrink effect.

We use transform-origin: center to specify that we want it to grow and shrink from the center of the circle, like animation we are trying to replicate.

To get a visual of this, let’s add the animation statement to growShrinkActive.

.growShrinkActive{
animation: growShrink 0.5s 1;
}

In this animation statement, we call growShrink to last 0.5 seconds and to only run once.

We can now see it working as expected.

Woohoo! Tree Left Circle Bottom is complete!

Tree Left Circle Top

By learning how to handle Tree Left Base and Tree Left Circle Bottom, the rest of this process by easier and faster to complete.

Tree Left Circle Top will be handled exactly like Tree Left Circle Bottom.

The only change will be that it is waiting on Tree Left Circle Bottom to finish.

First, we add a setTimeout that will call a function named toggleTreeLeftCircleTop.

toggleTreeLeftCircleBottom: function () {
   this.showTreeLeftCircleBottom = true
this.treeLeftCircleBottomClass = "growShrinkActive"
setTimeout( () => {
this.toggleTreeLeftCircleTop()
}, 500)

}

Second, we define the function we just called and add the logic to toggle on Tree Left Circle Top.

toggleTreeLeftCircleTop: function () {
this.showTreeLeftCircleTop = true
}

Next, we add the v-bind:class to the shape tag in our HTML.

<ellipse v-bind:class="treeLeftCircleTopClass" v-if="showTreeLeftCircleTop" id="Tree-Left-Circle-Top" cx="38.331" cy="23.245" rx="24.666" ry="23.245" style="fill:#41b883;"/>

Now, we will have to define treeLeftCircleTop in our Vue instance and assign it a class for Tree Left Circle Top.

data: {
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: "",
treeLeftCircleTopClass: ""
},

As we did before, let’s write the logic in toggleTreeLeftCircleTop to assign growShrinkActive.

toggleTreeLeftCircleTop: function () {
this.showTreeLeftCircleTop = true
this.treeLeftCircleTopClass= "growShrinkActive"
}

Notice, we already defined growShrinkActive for Tree Left Circle Bottom. We do this because it is the same animation. It will just be applied at a different time.

Woah! We should complete with Tree Left Circle Top.

Tree Right

Since we have completed Tree Left, Tree Right will be nearly identical. It will just be a tad easier since we only have one circle. Because of this, I am going to pick up the pace and be a little less exhaustive on the details.

Tree Right Base

Let’s start by checking the notes for our timing of Tree Right Base.

Right after Tree Left Base fully grows, Tree Right Base spawns and starts to grow.

First, we need to set up conditional rendering since we want this to render when Tree Left Base fully grows.

We will need to define the flag to handle this in our Vue instance data I also added the flag for Tree Right Circle.

data: {
//treeLeft stuff
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: "",
treeLeftCircleTopClass: "",
//treeRight stuff
showTreeRightBase: false,
showTreeRightCircle: false

},

Then, we need to let our HTML know Vue.js is going to handle the rendering.

We do this by adding v-if = “[flag name]” in both the Tree Right Base and Tree Right Circle shape tags.

<rect v-if="showTreeRightBase" id="Tree-Right-Base" x="49.615" y="54.96" width="10.32" height="115.2" style="fill:#35495e;"/>
<ellipse v-if="showTreeRightCircle" id="Tree-Right-Circle" cx="54.775" cy="56.76" rx="54.775" ry="56.76" style="fill:#41b883;"/>

Our Tree Right is now longer being rendered since the flags were set to false.

Perfect. Next, we need to handle having Tree Right Base appear and applying the animation when it appears.

In our notes, we mentioned that Tree Right Base spawns after Tree Left Base grows. This is also the same time Tree Left Circle Bottom appears. Therefore, we can call a function to toggle on Tree Right Base in the same setTimeout that calls the function to toggle on Tree Left Circle Bottom.

    toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
this.treeLeftBaseClass = "growActive"
setTimeout( () => {
this.toggleTreeLeftCircleBottom()
this.toggleTreeRightBase()
}, 500)
},

Next, we write toggleTreeRightBase and the logic to toggle on Tree Right Base.

toggleTreeRightBase: function() {
this.showTreeRightBase = true
}

The class growShrinkActive is applied to Tree Left Base when it spawns. We will then do the same for Tree Right Base.

First, let’s tell our HTML that Vue.js will handle the class assigned to Tree Right Base.

<rect v-bind:class="treeRightBaseClass" v-if="showTreeRightBase" id="Tree-Right-Base" x="49.615" y="54.96" width="10.32" height="115.2" style="fill:#35495e;"/>

Second, we will define treeRightBaseClass in our Vue instance’s data.

data: {
//treeLeft stuff
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: "",
treeLeftCircleTopClass: "",
//treeRight stuff
showTreeRightBase: false,
showTreeRightCircle: false,
treeRightBaseClass: ""
},

Then, we need to assign the growShrinkActive class when Tree Right spawns.

  toggleTreeRightBase: function() {
this.showTreeRightBase = true
this.treeRightBaseClass= "growShrinkActive"
}

We now have our Tree Right Base fully functioning.

Tree Right Circle

Like our Tree Left Circle Bottom, we need this to spawn right after the base grows. Therefore, we can add a setTimeout in toggleTreeRightBase that will run a call to a function named toggleTreeRightCircle when Tree Right Base finishes animation.

    toggleTreeRightBase: function() {
this.showTreeRightBase = true
this.treeRightBaseClass= "growShrinkActive"
setTimeout(() => {
this.toggleTreeRightCircle()
}, 500)

}

Next, we can define toggleTreeRightCircle and include the logic to toggle Tree Right Circle on.

toggleTreeRightCircle: function () {
this.showTreeRightCircle = true
}

Our Tree Right Circle is now appearing on time.

To finish up, we will have turn on class binding with Vue.js in our HTML, define the data that will assign the active class, and then assign growShrinkActive in the same function that toggles on Tree Right Circle.

To turn on class binding with Vue.js in our HTML, we can do:

<ellipse v-if="showTreeRightCircle" v-bind:class="treeRightCircleClass" id="Tree-Right-Circle" cx="54.775" cy="56.76" rx="54.775" ry="56.76" style="fill:#41b883;"/>

Next, we define this data to assign the active class.

data: {
//treeLeft stuff
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: "",
treeLeftCircleTopClass: "",
//treeRight stuff
showTreeRightBase: false,
showTreeRightCircle: false,
treeRightBaseClass: "",
treeRightCircleClass: ""
},

To end, we can set treeRightCircleClass to growShrinkActive in toggleTreeRightCircle.

    toggleTreeRightCircle: function () {
this.showTreeRightCircle = true
this.treeRightCircleClass = "growShrinkActive"
}

Just like that, Tree Right is done!

Bush

The hardest parts, the trees, are done. By now, we should have an understanding of the process. For that reason, I am just going to provide the code for each step with few lines of description for the remaining bush and sun.


Let’s review the notes for Bush.

Bush Circle Left grows and shrinks.
Bush Circle Right starts growing when Bush Circle Left fully grows and then shrinks.
//timing
When Tree Right Base fully grows, Bush Circle Left spawns and grows.

Bush Circle Left

Step 1: Add conditional rendering (will also include Bush Circle Right)

HTML

<ellipse v-if="showBushCircleLeft" id="Bush-Circle-Left" cx="29.4" cy="28.32" rx="29.4" ry="28.32" style="fill:#41b883;"/><
<ellipse v-if="showBushCircleRight" id="Bush-Circle-Right" cx="58.8" cy="38.76" rx="18.562" ry="17.88" style="fill:#41b883;"/>

JS

data: {
//treeLeft stuff
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: "",
treeLeftCircleTopClass: "",
//treeRight stuff
showTreeRightBase: false,
showTreeRightCircle: false,
treeRightBaseClass: "",
treeRightCircleClass: "",
//bush stuff
showBushCircleLeft: "",
showBushCircleRight: ""

},

Step 2: Call toggleBushCircleLeft from the setTimeout in toggleTreeBaseLeft. Add function with logic to toggle on.

toggleTreeLeftBase: function () {
this.showTreeLeftBase = true
this.treeLeftBaseClass = "growActive"
setTimeout( () => {
this.toggleTreeLeftCircleBottom()
this.toggleTreeRightBase()
this.toggleBushCircleLeft()
}, 500)
},
//later
toggleBushCircleLeft: function () {
this.showBushCircleLeft = true
},

Step 3: Add class binding in HTML and JS. Assign class growShrinkActive in toggleBushCircleLeft.

HTML

<ellipse v-bind:class="bushCircleLeftClass" v-if="showBushCircleLeft" id="Bush-Circle-Left" cx="29.4" cy="28.32" rx="29.4" ry="28.32" style="fill:#41b883;"/>

JS

data: {
//treeLeft stuff
showTreeLeftBase: false,
showTreeLeftCircleBottom: false,
showTreeLeftCircleTop: false,
treeLeftBaseClass: "",
treeLeftCircleBottomClass: "",
treeLeftCircleTopClass: "",
//treeRight stuff
showTreeRightBase: false,
showTreeRightCircle: false,
treeRightBaseClass: "",
treeRightCircleClass: "",
//bush stuff
showBushCircleLeft: "",
showBushCircleRight: "",
bushCircleLeftClass: ""
},
//under methods
toggleBushCircleLeft: function () {
this.showBushCircleLeft = true
this.bushCircleLeftClass = "growShrinkActive"
},

Bush Left Circle should now be working just like we want!

Bush Circle Right

Since Bush Circle Right is the exact same steps and logic, I am simply going to provide all the code updates.

HTML

<ellipse v-if = "showBushCircleRight"  v-bind:class="bushCircleRightClass" id="Bush-Circle-Right" cx="58.8" cy="38.76" rx="18.562" ry="17.88" style="fill:#41b883;"/>

JS

    //under data
    //bush stuff
showBushCircleLeft: "",
showBushCircleRight: "",
bushCircleLeftClass: "",
bushCircleRightClass: ""
    //methods
    toggleBushCircleLeft: function () {       
this.showBushCircleLeft = true
this.bushCircleLeftClass = "growShrinkActive"
//wait 0.25 seconds since we want Bush Circle Right to spawn when Bush Circle Left grows
setTimeout(() => {
this.toggleBushCircleRight()
}, 250)

},
toggleBushCircleRight: function () {
this.showBushCircleRight = true
this.bushCircleRightClass = "growShrinkActive"
},

Just like that, Bush is done with only Sun remaining! Finally!

Sun

Sun is the easiest animation. Instead of having to conditionally render and animate each specific shape, we can just apply it to the entire graphic since it does not have distinct animations and timing for its shapes.

Sun grows and shrinks.
//timing
Sun spawns and starts growing when Tree Right is complete.

Step 1: Add the conditional rendering

HTML

<svg v-if="showSun" class="sun" viewBox="0 0 89 87" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">

JS

//under data
//sun stuff
showSun: false

Step 2: Create a function to toggle on Sun. This should be called in a new setTimeout under toggleTreeRightCircle since Sun needs to appear when Tree Right Circle grows.

   toggleTreeRightCircle: function () {
this.showTreeRightCircle = true
this.treeRightCircleClass = "growShrinkActive"
setTimeout(() =>{
this.toggleSun()
},250)
//we wait 250 seconds since that is the point when Tree Right Circle grows and not when it ends
},
   //later
   toggleSun: function () {
this.showSun = true
}

Step 3: Add class binding and assign class growShrinkActive right after Sun is toggled on. I will also note that the class binding here will be slightly different. Since we are applying this to the entire Sun, there is already a sun class in the SVG tag. This will be removed and by default, sun will be the assigned class. Since we can have multiple classes in CSS, we will have both the class sun and growShrinkActive assigned right after Sun appears.

HTML

<svg v-bind:class="sunClass" v-if="showSun" viewBox="0 0 89 87" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">

JS

//data
//sun stuff
showSun: false,
sunClass : "sun"
//methods
toggleSun: function () {
this.showSun = true
this.sunClass = "sun growShrinkActive"
}

Cool beans! We have completed the animation!

Final Demo/Code: http://codepen.io/mikemang/pen/JWNOrr/


Concluding Thoughts

After 15 hours of writing this post, I recognize that this not a perfect replica and a lot to take in. Hopefully, there were some still helpful bits and pieces that you extracted from reading this.

If you are looking for more practice with animations, I encourage you to do just what we did in this post. Break down the animation and complete it piece by piece. I would highly recommend starting with an easier example and progressing.

If you’re looking for real, practical animations to break down and replicate, I would recommend Product Hunt. Product Hunt has tons of links to landing pages that are bound to be modern and using at least some animation.

If you are looking for something more structured, you can book access to my upcoming SVG course which will cover everything from SVG fundamentals to creating detailed vector graphics with Affinity Designer, all the ways to animate SVGs using external libraries, and practical applications such as making an icon library, creating an animated landing page, and UX animations.


Any feedback is welcome and appreciated.

Cheers,
Mike Mangialardi
Founder of Coding Artist