Combine the power of SCSS and SVG to create a loading animation.

Example

Here is the animation we are going to recreate:

See the Pen Loading SVG Cubez by Jonathan klughertz (@klugjo) on CodePen.

Steps

If you want to follow along, the easiest way is to use a tool like CodePen, JSFiddle or whichever is your favourite. Set it up to use SCSS.

1. Create the SVG Element

Let’s start by setting our body’s background color to black:

body {
background: black;
}

And create a 100px by 100px SVG element:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100">

</svg>

2. Draw the squares

SVG/HTML

To draw a rectangle, use the <rect /> element. We are going to need 16 rectangles. All with a class of .box and .pos-ij where i is the row and j is the column.

Here is the final result and the only HTML/SVG code we are going to need:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100">

<rect class="box pos-11" />
<rect class="box pos-12" />
<rect class="box pos-13" />
<rect class="box pos-14" />

<rect class="box pos-21" />
<rect class="box pos-22" />
<rect class="box pos-23" />
<rect class="box pos-24" />

<rect class="box pos-31" />
<rect class="box pos-32" />
<rect class="box pos-33" />
<rect class="box pos-34" />

<rect class="box pos-41" />
<rect class="box pos-42" />
<rect class="box pos-43" />
<rect class="box pos-44" />

</svg>

SCSS

Let’s start by creating our .box class. All squares should be 15px by 15px and of white colour:

.box {
fill: white;
width: 15px;
height: 15px;
}

Now our boxes are all of the right size and colour but they are all on top of each other. Let’s fix that with some SCSS goodness.

@for $i from 1 through 4 {
@for $j from 1 through 4 {
.pos-#{$i}#{$j} {
x: 20 * $j;
y: 20 * $i;
}
}
}

This is what we have so far


3. Colour Animation

Let’s start by defining our keyframes in the CSS. We will create a hideshow animation which prints our squares in white for the first 20% of the time and then fades to black during the remaining 80%.

@keyframes hideshow {
0% {
fill: white;
}
20% {
fill: white;
}
100% {
fill: black;
}
}

Now let’s apply this to all the .box element

.box {
width: 15px;
height: 15px;
animation: hideshow 1.6s ease infinite;
}
  • 1.6s indicates that the animation lasts 1.6 seconds. Since we have 16 squares, we will put 100ms of delay in between each square.
  • infinite loops the animation forever.

This what we have so far:


4. Cascading Effect

Now we want all our cubes to start fading to black one after the other. This can be achieved using the animation-delay CSS property.

The element with .pos-11 will have 0s delay, the element with .pos-12 will have a 0.1sec delay and so on until .pos-44 with a delay of 1.5sec.

The formula to calculate the delay is:

delay = ((row - 1) * 4 + (column - 1)) / 10

And the SCSS equivalent is:

@for $i from 1 through 4 {
@for $j from 1 through 4 {
.pos-#{$i}#{$j} {
x: 20 * $j;
y: 20 * $i;
animation-delay: #{ (($i - 1) * 4 + ($j - 1))/10 }s;
}
}
}

This is what we have so far:

This is starting to look good. However the animation goes from left to right for all rows and we want it to go from right to left for even rows.

This can be achieved with a bit of SCSS. I am going to introduce a $k variable that will have a decreasing column index for even rows:

$k: if($i % 2 == 0, $j, 5 - $j);

Where $i is the row number and $j is the column number.
$i % 2 == 0 indicates an even row.
5 - $j will transform 1,2,3,4 into 4,3,2,1

Then let’s use these new index instead of $j in our animation-delay

@for $i from 1 through 4 {
@for $j from 1 through 4 {
$k: if($i % 2 == 0, $j, 5 - $j);
.pos-#{$i}#{$j} {
x: 20 * $j;
y: 20 * $i;
animation-delay: #{ (($i - 1) * 4 + ($k - 1))/10 }s;
}
}
}

Let’s look at the result:

5. Scaling Effect

The last step needed is to scale each cube down as they fade out.

There are 2 aspects to this:

a. Scale the dimensions out

Done by adding a transform: scale(0.3) property to the animation. 0.3 means that the figure will be scaled down to 30% of its original size.

@keyframes hideshow {
0% {
fill: white;
}
20% {
fill: white;
}
100% {
fill: black;
transform: scale(0.3);
}
}

b. Set the origin of the transform to the center of each square

We will have to compute the transform-origin property for each square.

Inside the double loop, add the following property:

transform-origin: #{20 * $j + 7}px #{20 * $i + 7}px;

Here is the final result:

And the full SCSS code:

body {
background: black;
}

@keyframes hideshow {
0% {
fill: white;
}
20% {
fill: white;
}
100% {
fill: black;
transform: scale(0.3);
}
}

.box {
width: 15px;
height: 15px;
animation: hideshow 1.6s ease infinite;
}

@for $i from 1 through 4 {
@for $j from 1 through 4 {
$k: if($i % 2 == 0, $j, 5 -$j);
.pos-#{$i}#{$j} {
x: 20 * $j;
y: 20 * $i;
transform-origin: #{20 * $j + 7}px #{20 * $i + 7}px;
animation-delay: #{ (($i - 1) * 4 + ($k - 1))/10 }s;
}
}
}