Introduction
For years, it has been standard practice to provide the user
with an animated “loading” or “working” image whenever an application
or program does something that takes more than a few milliseconds.
For example:
or:
For us in the web environment, this practice presented a slight bit of a challenge because the only way we could do it was by making an animated GIF image. This
can be very difficult if you’re a programmer and not a graphics designer. The
task was also further complicated because our favorite graphics software
(Photoshop) does not support the making of animated GIFs. In fact, finding the
right GIF animator is a challenge all by itself. In addition to that, GIF supports
a very limited number of colors so making a good looking loading image was mission impossible. Thankfully, the invention of
HTML5 washed away all these worries.
HTML5 introduced a multitude of ways to present the user
with an animated loading image without having to spend hours making tons of GIF
images. In particular, HTML5 gave us the canvas and the SVG. After spending a
few hours investigating the differences between an animated GIF, Canvas, and SVG,
I decided to make a loading image that I can use across all my web pages as
needed using SVG.
Why SVG
Between an animated GIF, Canvas, and SVG, the GIF has the
worst performance. It requires the most CPU usage. In addition, you are limited
by the colors and lack of decent software. The performance difference between
Canvas and SVG is minimal. The Canvas, however, requires significantly more
code to animate. For example, for each frame of the animation, the Canvas must
be cleared, and then the whole image must be redrawn. On the other hand, the
SVG simply requires the elements inside it to be altered as necessary for each
frame. This can be done with much less code. Lastly, as we all know, SVG is
scalable that means it is easy to take an existing animated SVG and make it
larger or smaller for different applications. And that is why I chose the SVG.
The Loading Image
To make things simple, I first of all created my SVG image:
<svg id="loadingImg1" width="100" height="100">Loading...
<circle cx="80" cy="50" r="5" fill="#000080"/>
<circle cx="74" cy="32" r="5" fill="rgba(0,0,180,0.8)"/>
<circle cx="59" cy="21" r="5" fill="rgba(0,0,180,0.6)"/>
<circle cx="40" cy="21" r="5" fill="rgba(0,0,180,0.4)"/>
<circle cx="26" cy="32" r="5" fill="rgba(0,0,180,0.2)"/>
</svg>
By placing the elements of the image inside the markup instead of adding them dynamically, the speed of the code is improved and made more reusable.
Then to maintain reusability, I coded a wrapper for my SVG that will raise an event each time the next frame must be loaded.
function AnimatedImg(svg, frames, delay) {
this.onnextframe = null;
this.elements = new Array(0);
this.currentFrame = 1;
this.isAnimated = function (){}
this.startAnimation = function () {}
this.stopAnimation = function () {}
}
var loadingImg1 = new AnimatedImg(document.getElementById("loadingImg1"));
For the full definition of AnimatedImg
function, see the source code. As demonstrated in the source code, the AnimatedImg
can be used to animate more than one SVG on a single page, and it can be used for any SVG regardless of the internal structure of the SVG. To use it in your own projects, download it via NuGet (the key is AnimatedImg
).
The next step is to define the function where all the magic happens, and assign it the onnextframe
event of the AnimatedImg
. This function will then be called every 100 milliseconds. It uses the currentFrame
property of the AnimatedImg
to determine which frame to display and consequently where each element of the SVG must be.
loadingImg1.onnextframe = animateLoadingImg;
function animateLoadingImg(){
for(var l_1 = 0;l_1<this.elements.length;l_1++)
{
this.elements[l_1].setAttribute("cx",
getX(30,(Math.PI/5*this.currentFrame)-(Math.PI/5)*l_1)+50);
this.elements[l_1].setAttribute("cy",
getY(30,Math.PI/5*this.currentFrame)-(Math.PI/5)*l_1)+50);
}
}
function getX(radius,angle){
return Math.cos(angle)*radius;
}
function getY(radius,angle){
return Math.sin(angle)*radius;
}
The final step is to call the startAnimation
method to animate the SVG and the stopAnimation
method to stop the animation.
loadingImg1.startAnimation();
loadingImg1.stopAnimation();