A Simple Animation Framework in JavaScript

The code included below implements a simple frame-based animation framework in JavaScript.  To see the code in action, paste it into an .html file, save the image “Chevrons.png” (also included below) to the same folder, and open the .html file in a web browser that runs JavaScript.

Note that certain browsers have security features that may need to be worked around to get the code to work.  Specifically, Google’s Chrome browser should be run with the “–allow-file-access-from-files” command-line option.

UPDATE: Note also that WordPress has started doing something weird to certain inline pictures, so you may need to download the image below directly from my media library at “https://thiscouldbebetter.files.wordpress.com/2013/01/chevrons.png”

Chevrons

<html>
<body>
<script type='text/javascript'>

function AnimationTest()
{}
{
	var prototype = AnimationTest.prototype;

	prototype.main = function()
	{
		var htmlImageToSlice = document.createElement("img");
		htmlImageToSlice.tag = this;
		htmlImageToSlice.onload = this.main2;
		htmlImageToSlice.src = "Chevrons.png";
	}

	prototype.main2 = function(event)
	{	
		var imagesForFrames = new ImageHelper().sliceImageIntoTiles
		(
			new Image().buildFromSystemImage(event.target),
			new Coords(4, 4) // imageSizeInTiles
		);

		var animationDefnRight = new AnimationDefn
		(
			"Right",
			"Down", // animationDefnNameNext
			new Array
			(
				new AnimationFrame(imagesForFrames[0][0], 1),
				new AnimationFrame(imagesForFrames[0][1], 1),
				new AnimationFrame(imagesForFrames[0][2], 1),
				new AnimationFrame(imagesForFrames[0][3], 1)
			)
		);

		var animationDefnDown = new AnimationDefn
		(
			"Down",
			"Left", // animationDefnNameNext
			new Array
			(
				new AnimationFrame(imagesForFrames[1][0], 1),
				new AnimationFrame(imagesForFrames[1][1], 1),
				new AnimationFrame(imagesForFrames[1][2], 1),
				new AnimationFrame(imagesForFrames[1][3], 1)
			)
		);

		var animationDefnLeft = new AnimationDefn
		(
			"Left",
			"Up", // animationDefnNameNext
			new Array
			(
				new AnimationFrame(imagesForFrames[2][0], 1),
				new AnimationFrame(imagesForFrames[2][1], 1),
				new AnimationFrame(imagesForFrames[2][2], 1),
				new AnimationFrame(imagesForFrames[2][3], 1)
			)
		);

		var animationDefnUp = new AnimationDefn
		(
			"Up",
			"Right", // animationDefnNameNext
			new Array
			(
				new AnimationFrame(imagesForFrames[3][0], 1),
				new AnimationFrame(imagesForFrames[3][1], 1),
				new AnimationFrame(imagesForFrames[3][2], 1),
				new AnimationFrame(imagesForFrames[3][3], 1)
			)
		);

		var animationDefnSetChevrons = new AnimationDefnSet
		(
			"Chevrons",
			new Array
			(
				animationDefnRight,
				animationDefnDown,
				animationDefnLeft,
				animationDefnUp
			)
		);

		var animationRun = new AnimationRun(animationDefnSetChevrons);

		var scene0 = new Scene("Scene0", animationRun);

		Globals.Instance.initialize
		(
			scene0,
			250 // millisecondsPerTick
		);
	}
}

// classes

function AnimationDefn(name, animationDefnNameNext, frames)
{
	this.name = name;
	this.animationDefnNameNext = animationDefnNameNext;
	this.frames = frames;
}

function AnimationDefnSet(name, animationDefns)
{
	this.name = name;
	this.animationDefns = animationDefns;

	this.animationDefnLookup = new Lookup(this.animationDefns);
}

function AnimationFrame(image, ticksToHold)
{
	this.image = image;
	this.ticksToHold = ticksToHold;
}

function AnimationRun(animationDefnSet)
{
	this.animationDefnSet = animationDefnSet;

	this.animationDefnNameCurrent = this.animationDefnSet.animationDefns[0].name;
	this.frameIndexCurrent = 0;
}
{
	var prototype = AnimationRun.prototype;

	prototype.advance = function()
	{
		this.frameIndexCurrent++;

		var animationDefnCurrent = this.animationDefnCurrent();
		if (this.frameIndexCurrent >= animationDefnCurrent.frames.length)
		{
			this.animationDefnNameCurrent_Set(animationDefnCurrent.animationDefnNameNext);
			this.frameIndexCurrent = 0;
		}
	}

	prototype.animationDefnCurrent = function()
	{
		return this.animationDefnSet.animationDefnLookup.get(this.animationDefnNameCurrent);
	}

	prototype.animationDefnNameCurrent_Set = function(value)
	{
		this.animationDefnNameCurrent = value;
		this.frameIndexCurrent = 0;
	}

	prototype.frameCurrent = function()
	{
		return this.animationDefnCurrent().frames[this.frameIndexCurrent];
	}

	prototype.htmlElementBuild = function()
	{
		var returnValue = document.createElement("div");
		returnValue.id = "AnimationRun_" + this.name;

		this.htmlElementForImage = this.animationDefnSet.animationDefns[0].frames[0].image.htmlElement;
		returnValue.appendChild(this.htmlElementForImage);

		this.htmlElement = returnValue;
	}

	prototype.htmlElementUpdate = function()
	{
		var htmlElementForImageNext = this.frameCurrent().image.htmlElement;

		if (this.htmlElementForImage != htmlElementForImageNext)
		{
			this.htmlElement.replaceChild
			(
				htmlElementForImageNext, // new
				this.htmlElementForImage // old
			);

			this.htmlElementForImage = htmlElementForImageNext;
		}
	}
}

function Coords(x, y)
{
	this.x = x;
	this.y = y;
}
{
	var prototype = Coords.prototype;  

	prototype.clone = function()
	{
		return new Coords(this.x, this.y);
	}

	prototype.divide = function(other)
	{
		this.x /= other.x;
		this.y /= other.y;

		return this;
	}

	prototype.multiply = function(other)
	{
		this.x *= other.x;
		this.y *= other.y;

		return this;
	}

	prototype.overwriteWith = function(other)
	{
		this.x = other.x;
		this.y = other.y;

		return this;
	}

	prototype.toString = function()
	{
		return "(" + this.x + "," + this.y + ")";
	}
}

function Globals()
{}
{
	Globals.Instance = new Globals();

	var prototype = Globals.prototype;

	prototype.initialize = function(scene, millisecondsPerTick)
	{
		this.scene = scene;
		this.millisecondsPerTick = millisecondsPerTick;

		this.scene.initialize();
		setInterval("Globals.Instance.processTick()", this.millisecondsPerTick);
	}

	prototype.processTick = function()
	{
		this.scene.update();	
	}
}

function Image()
{}
{
	var prototype = Image.prototype;

	prototype.buildFromFilePath = function(filePath)
	{
		this.filePath = filePath;

		this.htmlElement = document.createElement("img");
		this.htmlElement.src = this.filePath;

		return this;
	}

	prototype.buildFromSystemImage = function(systemImage)
	{
		this.htmlElement = systemImage;
		this.filePath = this.htmlElement.src;

		return this;
	}
}

function ImageHelper()
{}
{
	var prototype = ImageHelper.prototype;

	prototype.sliceImageIntoTiles = function(imageToSlice, sizeInTiles)
	{
		var returnImages = new Array();

		imageToSlice = imageToSlice.htmlElement;		

		var imageToSliceSize = new Coords(imageToSlice.width, imageToSlice.height);
		var tileSize = imageToSliceSize.clone().divide(sizeInTiles);

		var tilePos = new Coords(0, 0);
		var sourcePos = new Coords(0, 0);

		for (var y = 0; y < sizeInTiles.y; y++)
		{
			tilePos.y = y;

			var returnImageRow = new Array();

			for (var x = 0; x < sizeInTiles.x; x++)
			{							
				tilePos.x = x;

				var canvas		 = document.createElement("canvas");
				canvas.id		 = "tile_" + x + "_" + y;
				canvas.width		 = tileSize.x;
				canvas.height		 = tileSize.y;
				canvas.style.position	 = "absolute";

				var graphics = canvas.getContext("2d");

				sourcePos.overwriteWith(tilePos).multiply(tileSize);

				graphics.drawImage
				(
					imageToSlice,
					sourcePos.x, sourcePos.y, // source pos
					tileSize.x, tileSize.y, // source size
					0, 0, // destination pos
					tileSize.x, tileSize.y // destination size
				);

				// browser dependent?
				var imageFromCanvasURL = canvas.toDataURL("image/png");

				var htmlImageFromCanvas = document.createElement("img");
				htmlImageFromCanvas.width = canvas.width;
				htmlImageFromCanvas.height = canvas.height;
				htmlImageFromCanvas.style.position = "absolute";
				htmlImageFromCanvas.src = imageFromCanvasURL;

				imageFromCanvas = new Image().buildFromSystemImage(htmlImageFromCanvas);

				returnImageRow.push(imageFromCanvas);
			}

			returnImages.push(returnImageRow);
		}

		return returnImages;
	}
}

function Lookup(namables)
{
	this.systemLookup = new Array();

	var numberOfNamables = namables.length;

	for (var i = 0; i < numberOfNamables; i++)
	{
		var namable = namables[i];

		this.systemLookup[namable.name] = namable;
	}
}
{
	var prototype = Lookup.prototype;

	prototype.get = function(key)
	{
		return this.systemLookup[key];
	}
}

function Scene(name, animationRun)
{
	this.name = name;
	this.animationRun = animationRun;
}
{
	var prototype = Scene.prototype;

	prototype.initialize = function()
	{
		this.animationRun.htmlElementBuild();
		document.body.appendChild(this.animationRun.htmlElement);
	}

	prototype.update = function()
	{
		this.animationRun.advance();
		this.animationRun.htmlElementUpdate();
	}
}

//

new AnimationTest().main();

</script>
</body>
</html>
Advertisements
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

One Response to A Simple Animation Framework in JavaScript

  1. When someone writes an post he/she keeps the thought of a user in his/her brain that how a
    user can know it. Thus that’s why this post is amazing. Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s