Project 1 – Snap.svg

Published April 12, 2014

Recently, I further investigated the power of SVGs. If you do not know what scalable vector graphics are, Wikipedia is a good start: additionally, if you are a developer check out W3School’s tutorial. Although, if you have taken any type of vector math you understand it is just a way to show direction and magnitude and ‘scalable’ refers to the graphic’s ability to be resized without any loss in quality. Basically perfect for any situation where re-sizable shapes are needed.

Now, vectors are amazing but they can be extremely complicated to write by hand. And that’s why we have programs for it like Adobe Illustrator. But in terms of manipulating svg’s in the browser, Snap.svg is a great tool. Using a jQuery like syntax makes it fairly simple to create and manipulate svg items. Here is the demo.

It only needs very basic html index file that contains an svg element where the shapes will be created and manipulated:

<!DOCTYPE html>
<html>
	<head>
		<title>Project 1</title>
		<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
		<script src="snap.svg-min.js"></script>
		<script src="scripts.js"></script>
	</head>
	<body style="background-color:black">
		<svg>
		</svg>
	</body>
</html>

Obviously, I didn’t use any additional css files; the entirety of the animation is generated using the Snap.svg API. This code is fairly simple needing a total of only 4 functions.

First, I store all the information necessary:

var colorArray = [
		"#98FFFC",
		"#6BE87C",
		"#ECFF82",
		"#E8C86B",
		"#FFAB75",
		"#8B8DE8",
		"#ECA6FF",
		"#E88D8B"
		];
var data;
var defaults = {
	"timingIndex"	: 0,
	"circleIndex"	: 0,
	"centerIndex"	: 0,
	"removeIndex"	: colorArray.length-1,
	"circleRadius"	: 70,
	"canvas"		: null,
	"centerCircle"	: null,
	"windowWidth"	: window.outerWidth
};

One postulate of the project was extensibility, so I focused on creating a single space where variables were generated and in order to add or remove shapes you only need to edit the color array.

The first function is to generate all of the circles and send them to the center of the screen:

function generateCircles(){
	while(colorArray[data.timingIndex] != undefined){
		setTimeout(function(){
				var circle = data.canvas.circle(-70, 400, data.circleRadius);
				circle.attr({fill: colorArray[data.circleIndex]});
				circle.animate({cx:"50%", cy:140}, 500, mina.easeout, function(){
					updateCenterCircle(true);
					if(colorArray[data.centerIndex] === undefined){
						data.centerIndex-=2;
						data.circleIndex--;
						data.timingIndex--;
						removeCircles();
					}
				});
				data.circleIndex++;
			}, 300*data.timingIndex+200
		);
		data.timingIndex++;
	}
};

It utilizes a setTimeout function to space the creation and movement of the circles. Within the .animate(), the callback triggers the updateCenterCircle(true) and checks if all the circles have arrived at the center with the if statement. This if statement recognizes the arrival of the final circle and prepares the variables for the removal function.

Here is the function managing the center circle:

function updateCenterCircle(goingUp){
	if(data.centerCircle == null){
		data.centerCircle = Snap('circle');
		/*data.centerCircle.after('svg circle:last-child');*/
	} else if(colorArray[data.centerIndex] != undefined){
		data.centerCircle.animate({r:(data.circleRadius*(1+.65*data.centerIndex/colorArray.length)), fill:colorArray[data.centerIndex], "z-index":data.centerIndex}, 100);
	}
	if(goingUp){
		data.centerIndex++;
	} else {
		data.centerIndex--;
	}
	
};

You can see that the updateCenterCircle() function uses the input parameter ‘goingUp’ to determine whether it should go be counting up or down. This then determines the changes reflected in the first circle created, the other circles are just sitting on top of it. Having only the center circle change instead of every circle was a decision I made to simplify the operations. However, this would be the first change I would make in order to improve the animation.

This next function pulls all of the circles out of the center also updating the center circle but in the opposite direction by setting goingUp to false:

function removeCircles(){
	while(colorArray[data.timingIndex] != undefined){
		setTimeout(function(){
				updateCenterCircle(false);
				var circle = Snap.selectAll('circle')[data.circleIndex];
				circle.attr({fill: colorArray[data.circleIndex]});
				circle.animate({cx:data.windowWidth+70, cy:400}, 500, mina.easeout, function(){
					Snap.selectAll('circle')[data.removeIndex].remove();
					data.removeIndex--;
					if(colorArray[data.removeIndex] === undefined)
						restart();
				});
				data.circleIndex--;
			}, 300*(colorArray.length-data.timingIndex)+200
		);
		data.timingIndex--;
	}

};

The removeCircles() function animates each circle off the page and removes it. Then, when all of the circles have been removed it calls the final function, restart():

function restart(){
	data = $.extend({}, defaults);
	generateCircles();
}

This just resets the data and calls generateCircles() thereby making an indirectly recursive, infinite loop, but due to the use of callbacks the browser doesn’t crash. This was also my first time using callback functions and I do say they are very useful!

So that is the explanation of the code, feel free to use it and experiment with it yourself! But I highly recommend checking out the demos from Snap.svg as well, though they are clearly more advanced. Additionally, Codrops frequently will use Snap.svg in inspiring ways.

This entry was posted in Professional. Bookmark the permalink. Both comments and trackbacks are currently closed.