Creating Responsive and Interactive Animations with Raphael.js

This tutorial will teach you how to animate SVG images using the JavaScript library Raphael.js and other open source tools. When you’ve finished this tutorial, you’ll have learned how to make a spinning windmill that will scale up our down with your responsive web design. It will look like this:

Why use Raphael.js?

It is also possible to create animations using HTML5 Canvas. HTML5 Canvas uses bitmap graphics and excels at processing complicated animations like flying past stars in outer space. In contrast, Raphael.js uses SVG vector images and is designed to seamlessly integrate with on page events such as clicking, hovering, and dragging. Moreover, Raphael.js provides great legacy browser support (including IE7 and IE8). As you go through this tutorial, feel free to consult Raphael’s fabulous documentation at any time.

Understanding SVG

SVG stands for scalable vector graphic. While bitmap images (png, jpeg, gif) store images using a grid of pixels, vector images are text instructions that describe the image using a coordinate system. Bitmap images will look blurry when scaled up or down, but vector images look the same at any size. This makes them a fantastic choice for use with responsive web designs and on high resolution screens.

Finding an SVG image

You can find SVG images all over the web. On stock photo sites, just search for “vector” images. Make sure that the image is delivered in the .svg format.

In this tutorial, we’ll be working off of a free SVG windmill that I found here.

Editing your SVG image

After downloading windmill.svg, we need to edit it. To do this, you’ll want to download the open source program: Inkscape. After you open up the image in Inkscape, you’ll see something like this:

After you open up the image in Inkscape you'll see something like this

If you’re working with your own SVG image, make sure that you have ungrouped all of your objects (Shift+Ctrl+G) and that all objects have been converted to paths (Shift+Ctrl+C) before proceeding.

The image you downloaded (windmill.svg) has two parts, the sails and the base. I did this by breaking the image into parts with:

(PathBreak Apart) or (Shift+Ctrl+K).

And then grouping the parts back together using:

(PathCombine) or (Ctrl+K).

Next, we need to label each part. To do this, click on the sails and use the command:

(Ctrl-Shift-O).

To bring up the Object Properties, change the id to sail, and click Set:

To bring up the Object Properties change the id to sail

Do the same for the base, giving it the id base. Next, hover over the center of the circle holding the windmill sails. In the bottom right hand corner you should see x=40.04, y=58.51 (or something quite close to that). This is the very center of the windmill around which we want our windmill sails to rotate.

Now save the windmill.svg file in Inkscape and open the same file in a text editor like Notepad or Notepad ++. You should see two objects, one for the sail and one for the base. The id that we set earlier lets us know which one is which!

The id that we set earlier lets us know which one is which

Everything after the d= is the path for the object. The path is simply the instructions that define the SVG image. For example, the windmill base instructions can be translated into English as: “Move to 40.15625, 35.21875 then draw a cubic bezier curve to the point to 36.80697, 35.21875 …” To learn more about paths, and all the possible instructions, check out this webpage.

Copy each path and save it to be used later by Raphael.js. Also copy the width and height of the SVG image.

Also copy the width and height of the SVG image

When you are finished, close Inkscape.

Setting up the web page

Our animation will appear on a webpage. Create an index.html document that contains the following:

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Example</title>
		<script src="raphael-min.js"></script>		
		<script src="windmill.js"></script>
  	  </head>
   	 <body>
		<h1>Windmill Animation</h1>
		<div id="windmill" style="width:20%"></div>
		<p>
			<button>Start</button>
			<button>Stop</button>
		</p>
	</body>
</html>

This is a basic HTML webpage that includes a head and a body. In the head, we’ve linked to the raphael.js library that you can download here. We’ve also created a windmill.js file where we will store all of the custom animation code. Store both of these files along with the index.html file in the same folder.

The div with the id windmill will contain the animation that we will create. Since our animation will fill up the div, we can use CSS to control the width of the div. We’ve also created two buttons Start and Stop to control the animation.

Setting up the script’s structure

Now that we’ve set up the project, we can start using the Raphael.js library to create our animated windmill. Open up the windmill.js file in a text editor.

We want to avoid polluting the global namespace, so we’ll use the module pattern. Our script is defined as a single function that calls itself and returns an object of properties that can be accessed outside of the function. This structure makes it simple to cleanly integrate our script with on page events.

Our script will contain 3 functions: init, start, and stop which will create the windmill, turn on the spinning, and turn off the spinning respectively. We’ll also include an options object that will make it easy to dynamically change the color and speed of the windmill.

var windmill=(function(){
	
	var options={} //configuration options kept here
	
	var init=function(){
		//this function will create the windmill
	}
	var start=function(){
		//this function will make the windmill spin 
	}
	var stop=function(){
		//this function will stop the spinning
	}

	return { //allows functions to be accessed externally
		start: start,
		stop: stop,
		init: init,
		options: options
	}
	
})();

Creating a blank canvas in Raphael.js

We’ll start by creating a blank Raphael.js canvas where we can draw our windmill. To do that we need to figure out where to draw the canvas and how large it should be.

var div = document.getElementById("windmill");		
var width=div.offsetWidth;
original_width=80;
original_height=100;
scale_factor=width/original_width;
var height=original_height*scale_factor;					
var transformation='S'+scale_factor+','+scale_factor+',0,0';	
var paper=Raphael(div, width, height);

The above code selects the windmill div from our webpage and works out it’s width in pixels. If you are using a responsive design, this value will vary based on the device that is viewing your page. Our script will then use the width to determine the scaling factor and the proper height of our canvas. With this information, the script creates a Raphael.js canvas where we will be able to draw our windmill. Our Raphael.js canvas is stored in the variable paper (you don’t have to use this name, but it is a Raphael.js convention).

Scaling in Raphael.js is done using the SVG transformation syntax. So we need to create a transformation string that we can later apply to the paths that come from our original SVG file. The syntax is read as follows: scale the x and y values in the path by a factor of scaling_factor around the coordinates 0,0.

Adding the windmill to the canvas

Now we can create our windmill! First, we scale our paths using the transformation string we defined earlier and the Raphael function transformPath. This will make our windmill bigger to fit the canvas. If our webpage is twice as large as our SVG canvas, all the numbers in our SVG path would need to be doubled. It’s then easy to add our windmill sails and base to the Raphael canvas using the method path.

var sails_path="m 39.991721,2.3848034 c -2.402325,0 -4.886834,22.8234656 -5.78125,32.3125006 1.559438,-1.25501 3.623713,-1.96875 5.78125,-1.96875 2.157508,0 4.128063,0.71374 5.6875,1.96875 -0.894416,-9.489035 -3.285189,-32.3125006 -5.6875,-32.3125006 z m -8.84375,37.6875006 c -0.01814,-0.01814 -3.156905,2.265549 -5.40625,3.90625 -12.759299,9.306741 -19.6959742,15.194375 -19.7187502,16.84375 -0.00281,0.254407 0.037591,0.41113 0.375,0.5 0.2095268,0.05526 1.1092743,0.09153 1.625,0 3.4076532,-0.604525 11.3291102,-3.663405 22.5312502,-8.625 3.031875,-1.34288 6.006038,-2.654035 6.15625,-2.75 0.108257,-0.06917 0.153269,-0.08571 -0.28125,-0.28125 -2.460656,-1.108215 -4.337423,-3.48104 -5,-6.09375 -0.257428,-1.01479 -0.367334,-2.041454 -0.28125,-2.9375 0.02728,-0.297763 0.0084,-0.554626 0,-0.5625 z m 17.71875,0 c -0.0057,0.0084 -0.02433,0.179421 0,0.375 0.05849,0.457859 0.09011,1.76253 0,2.28125 -0.528943,3.050279 -2.4347,5.511272 -5.21875,6.84375 -0.228016,0.1091 -0.375,0.25122 -0.375,0.28125 0,0.12456 9.465744,4.32391 14.3125,6.34375 4.84905,2.020822 8.710951,3.469019 11.5625,4.34375 1.346636,0.413077 2.252525,0.69604 3.40625,0.875 0.252592,0.03909 0.803994,-0.04525 1.03125,-0.09375 0.327639,-0.07029 0.510563,-0.238482 0.46875,-0.5 -0.316983,-1.98232 -8.565632,-8.782191 -23.5,-19.5 -0.934162,-0.67042 -1.680528,-1.258198 -1.6875,-1.25 z";			
var base_path="m 40.141329,35.21641 c -3.34928,0 -6.064411,2.715131 -6.064411,6.064408 0,3.349294 2.715131,6.064426 6.064411,6.064426 3.349293,0 6.064425,-2.715132 6.064425,-6.064426 0,-3.349277 -2.715132,-6.064408 -6.064425,-6.064408 z m 3.221733,14.592499 c -0.0028,-0.0084 -0.114859,0.02433 -0.284126,0.09461 -0.661381,0.276239 -1.854642,0.470145 -3.032212,0.473788 -0.986251,0.0028 -1.324041,-0.0679 -2.179399,-0.284112 -0.56499,-0.142831 -1.025646,-0.206504 -1.042321,-0.18952 -0.07185,0.07395 -2.708241,45.832308 -2.653185,46.051689 0.0336,0.133988 0.3576,0.550704 0.758053,0.947556 1.040055,1.030712 2.295417,1.642517 3.885026,1.895134 0.800386,0.12711 2.487074,0.02413 3.316479,-0.189521 1.504675,-0.387599 3.023439,-1.275671 3.695506,-2.274147 L 46.110995,95.765857 44.784408,72.834768 C 44.035634,60.196617 43.365296,49.819242 43.363062,49.808923 z";			
var sails_path_scaled=Raphael.transformPath(sails_path, transformation);
var base_path_scaled=Raphael.transformPath(base_path, transformation);			
var base=paper.path(base_path_scaled);			
base.attr({fill: options.base_color, "stroke-width": 0});
sails=paper.path(sails_path_scaled);
sails.attr({fill: options.sail_color, "stroke-width": 0});

We set the default colors using the options set at the beginning of the script.

var options={};
options.sail_color='skyblue';
options.base_color='gray';
options.time=3000;

We apply these values to each element using the method attr and the fill property. We also set the border width to 0 using the stroke-width property. attr is one of the most powerful methods in the Raphael.js library. You can use it to change an element’s opacity, change its position, give it a url and much more. You can see the full list of attr properties in this section of the documentation.

Making the windmill spin

To animate an element we simply need to provide Raphael with a new set of attr properties that describe the final state of the element. For example, we could specify that an element take on a new color or move to a new location. Next, we tell Raphael how long this transition should take and what easing formula to use (e.g. should the transition be constant over time or not).

In our example, we apply a transformation to the element that will rotate it 360 degrees. In transformation syntax that is “R360,…” followed by the coordinates for the center of the rotation. However, while Raphael and Inscape use the same coordinate axis’s for x values they use opposite axis’s for y values as illustrated in this diagram:

Raphael.js and Inscape use the same coordinate axis's for x values

Therefore, to transform the y-value into Raphael form, we must subtract the Inkscape y value from the height of the Inkscape canvas. Both the x and y values also need to be scaled up or down based on our scaling factor. We add time to our animation, use a linear easing formula, and repeat the animation infinitely. Our animation can now be applied to our sails element using the animate method.

var center_x=(40.04)*scale_factor;
var center_y=(original_height-(58.51))*scale_factor;				
var position='R'+360+', '+center_x+', '+center_y;				
var anim=Raphael.animation({transform: position}, options.time, 'linear').repeat(Infinity); 
sails.animate(anim);

Stopping the animation

It is easy to stop the animation with the stop method. However, doing so will leave the sails in a partially rotated state. If we were to restart the animation, everything would be out of whack. For example, if the sails had rotated 180 degrees, animating to a 360 degree rotation over the same period of time would make the animation appear twice as slow!

Therefore, we need to reset the transform property on our sails back to being blank without changing the position of the sails. To do this, we take advantage of the transformPath method we used earlier when we scaled our map. It allows us to get the raw path of a path that has been transformed. Then, we can update the sail with the new raw path and set the transform property back to being blank.

sails.stop();
var current_path=Raphael.transformPath(sails.attr('path'), sails.attr('transform'))
sails.attr({path: current_path, transform: ''});

Keep track of the animation state

Finally, we only want to be able to stop the animation when it is running and start the animation when it is stopped. Therefore, we keep track of the state of the animation with variable spinning. We then check this value to make sure it’s ok to start and stop the windmill. This is the code we use when starting the windmill:

if (spinning){return}else{spinning=true}

Integrate the animation into the webpage

Now for the fun part! We can add our animation to our webpage and make it interactive. We need to do 3 things:

  1. Initialize the windmill with windmill.init(). This should happen after the document has loaded. You can dynamically change any of the windmill properties like windmill.options.sail_color, windmill.options.base_color, or windmill.options.time prior to initializing the map.
  2. Call windmill.start() when the Start button is clicked.
  3. Call windmill.stop() when the Stop button is clicked.

There are a number of ways to do this, but an easy to read, jQuery-free, implementation looks like this:

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Example</title>
		<script src="raphael-min.js"></script>		
		<script src="windmill.js"></script>
		<script>
			function pageLoaded(){
				windmill.init();
			}
		</script>
    </head>
    <body onLoad="pageLoaded()">
		<h1>SVG Animation</h1>
		<div id="windmill" style="width:20%"></div>
		<p>
			<button onClick="windmill.start()">Start</button>
			<button onClick="windmill.stop()">Stop</button>
		</p>
	</body>
</html>

Note: Using onClick, onLoad, and inline styles aren’t best practices for big projects, but help to keep this example short and easy to read.

What’s next?

I hope this tutorial gets you excited about how you might be able to use Raphael.js in your projects. To learn more, I encourage you to play around with the demo, you might try:

  1. Reversing the direction that the sails spin.
  2. Tweaking the script so that you can dynamically create multiple windmills on a page.
  3. Replicating the tutorial with your own svg image.

Author: (1 Posts)

Chris Youderian is a software developer and the founder of Simplemaps, an interactive map-making tool used by Fortune 500 companies and universities around the world. His latest project, ContactMetrics, is a hosted contact form that teaches businesses about their customers. He loves learning and writing about JavaScript, Python, economics and data analysis. You can follow him on Twitter.

Comments