Small codes

HTML5, Javascript and AS3

09/05/2012
by Frederic CHAPLIN
9 Comments

Tutorial: toggle fullscreen HTML5 canvas and other DOM elements

Here are some recipes I use to add a fullscreen feature to the HTML5 canvas tag in browsers.
Firstly, I think we have to differentiate two concepts : browser fullscreen and in-browser fullscreen or full window.

Browser fullscreen is activated when the user press the F11 key. The browser windows extends itself across the whole screen, and hide it’s toolbars. you can find on the web some snippets examples allowing to replicate this behavior, but they are neither compatible with all browsers, nor a good practice: the user must be able to choose it’s navigation mode. Otherwise he could encounter problems with regard to accessibility.

In-browser fullscreen is not really a full screen behavior: it’s often called full-window. It forces a DOM element’s size to conform to the browser window’s bounds. This practice is better considered. The user may complete his full screen experience by using the F11 key, if he wants to.

This is the behavior we are going to add to the canvas.

Build the HTML page and its CSS sheet


We are going to build a simple page containing a HTML5 Canvas. This canvas will show an animation created with EaselJS.
I added a wrapper around the H1 title and the canvas: in a web page, the canvas will rarely be put directly under the body of your page.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" >
		<title>Tutorial: Full-window canvas mode with CreateJS</title>

		<script src="http://code.createjs.com/easeljs-0.4.2.min.js" >  </script>
		<script src="http://code.createjs.com/tweenjs-0.2.0.min.js" >  </script>
		<link rel="stylesheet" href="css/style_start.css" >

		<script>
		var canvas,
			wrapper,
			stage,
			shape;
		
		function init(){
			var g;
			canvas = document.getElementById("canvas");
			wrapper = document.getElementById("wrapper");
			
			stage = new Stage(canvas);
			
			g = new Graphics();
			g.setStrokeStyle(1);
			g.beginStroke(Graphics.getRGB(0,0,0));
			g.beginFill(Graphics.getRGB(255,0,0));
			g.drawCircle(0,0,100);
			
			shape = new Shape(g);
			shape.x = 400;
			shape.y = 300;
			stage.addChild(shape);
			
			Ticker.setFPS(32);
			Ticker.addListener(stage);
			
			animate();
		}
		
		function animate(){
			var tween;
			tween = Tween.get(shape)
					.to({x:Math.random()*canvas.width,y:Math.random()*canvas.height},1000,Ease.cubicOut)
					.call(animate);
		}
		
		window.onload = init;
		</script>
	</head>
	 <body>
		 <div id="wrapper" class="wrapperNormal">
			<H1>Tutorial: Full-window canvas mode with CreateJS</H1>
			 <canvas id="canvas" class="canvasNormal" width="800" height="600">
				<p>Your browser is sooooo old! Update or download a modern one now!</p>
			 </canvas>
		 </div>
	</body>
</html>

And here’s the CSS sheet :

body{
	font-family: 'Arial', sans-serif;
	font-size: 0.8em;
}

.canvasNormal{
	position: relative;
	background-color: #000000;
}
.wrapperNormal{
	width: 900px;
	margin: auto;
}

You can see here what the starting page of this tutorial looks like.

Create a fullscreen function

First, we are going to write a full-window function and add a “onclick” manager on the canvas itself. So we’ll just have to click anywhere on the canvas to switch from a ste to another.
To store the actual state of the canvas, we create a boolean variable : fullWindowState.

var canvas,
	wrapper,
	stage,
	shape,
	fullWindowState;

Then we create the fullWindow function, that change the canvas size, and it’s CSS rules. Note that I change the CSS class of the wrapper too, so I avoid some over-size effects, which can make the browser scrollbars to show. We don’t want that.

function fullWindow(e) {
	if (!fullWindowState) {
		fullWindowState = true;
		// Canvas goes full Window
		canvas.className = "canvasFullWindow";
		wrapper.className = "wrapperFullWindow";
		canvas.width = window.innerWidth;
		canvas.height = window.innerHeight;
	} else {
		fullWindowState = false;
		//canvas goes normal
		canvas.width = 800;
		canvas.height = 600;
		canvas.className = "canvasNormal";
		wrapper.className = "wrapperNormal";
	}
}

We add the listener on the canvas element.

<canvas id="canvas" class="canvasNormal" width="800" height="600" onclick="fullWindow(this)" >
	<p>Your browser is sooooo old! Download a modern one now! </p>
</canvas>

Finally, all we have to do is to add these rules in the CSS sheet:

.canvasFullWindow{
	position: absolute;
	background-color: #000000;
	top: 0px;
	left: 0px;
}

.wrapperFullWindow{
}

The empty rule is necessary.

Please note that if we would have added a rule like “width: 100%; height= 100%;” on our canvas, we increase it’s dimensions, but also it’s render size. So we will obtain a pixelate effect. This effect could be Nous obtiendrons donc un effet de pixelisation. This effet could be desired: pixel art is still fashion these days.

You can see here the result
With a click on the canvas, it extends itself properly in the whole window. But if we resize this one, or if we switch to a real fullscreen by pressing the F11 key, we lose the effect. Scrollbars or blank zones appears
To maintain the full-window effect when resizing, we have to listen this event and manage it.

Adding a window.onresize event management

All we have to do is to add a onResizeHandler() function to resize properly the canvas if the browser’s window’s size change…

function onResizeHandler(e){
	if (fullWindowState){
		canvas.width = window.innerWidth;
		canvas.height = window.innerHeight;
	}
}

…then we add the listener for the onresize event on the window object, targeting our callback function.

window.onresize = onResizeHandler;

Here’s the result. The effect works as expected on all newests browsers, including in the case of window resizing, or if the user press the F11 key.

A word about the Fullscreen API


Specifications about the Full-screen API are being discussed by the W3C. Firefox, Chrome and Safari browsers already included them as an experiment. But as the specs are not finished, it’s too soon to use it.
Here’s a good tutorial (in french…).

Conclusion


We added a flexible in-browser fullscreen feature, with ease, that will work on any recent browser.
We can resize the browser window, toggle the browser itself in fullscreen mode with the F11 key and our element will gracefully adapt it’s size.
It works for a canvas, blso with any HTML element with a block type.

01/05/2012
by Frederic CHAPLIN
7 Comments

Playing with DOM element in CreateJS: animate your forms !

DOMElement tutorial title image

In my previous posts about CreateJS, we could see that this Javascript library has many positive attributes, but all the things we did with it were confined in the HTML5 canvas. But there’s an object in the EaselJS library that allow to break through the canvas, while remaining in the display list. This is the DomElement object.

What’s the DOMElement object ?

The DOMelement object is, as I said, part of the EaselJS library. Its purpose is to allow adding to the display list (which start with the Stage), some HTML elements layering outside the Canvas. It can be a div, a span, an image, or even a form.

These elements may be moved, made transparent, and they will inherit some properties from to their parent container in the display list. We’ll se that DOMElements objects don’t have as much methods and properties as a display object in EaselJS, even if they inherits from it.

Moreover, it’s important to keep in mind that the DOMElement object is still experimental and bugs will happen, mainly on complex manipulations. Don’t hesitate to report these bugs on the createJS website, following this link.

Building HTML page and CSS rules

We are going to create a basic HTML page, that will contain two main elements : a form, and a canvas. On page load, we will start the EaselJS engine by creating a stage from the canvas and assign a Ticker object to it. If you didn’t follow my firsts posts about EaselJS, and if you don’t know what I am talking about, you may read this post.

Here’s the HTML code :

 
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>DOMElement Tutorial</title>

	        <script src="http://code.createjs.com/easeljs-0.4.2.min.js"></script>
		<script src="http://code.createjs.com/tweenjs-0.2.0.min.js"></script>
		<link rel="stylesheet" href="css/style.css">
		
		<script>
		var canvas, stage, exportRoot;
		window.onload = init;
		
		function init() {
			canvas = document.getElementById("canvas");

			stage = new Stage(canvas);
			
			Ticker.setFPS(32);
			Ticker.addListener(stage);
		}
		</script>
	</head>

	<body>
		<div id="main">
			<H1>DOMElements example</H1>
			<form id="myform" >
				<label for="firstName">Enter your firstName:</label>
				<input type="text" id="fistName/><br>
				<label for="lastName">Enter your lastName:</label>
				<input type="text"  id="lastName"/><br>
				<br>
				<input type="checkbox" id="case" /> 
				<label for="case">Alpha 50%</label><br>
				<p id="submit" >
					<input type="submit" value="Submit"/>
				</p>
			</form>
			<canvas id="canvas" width="800" height="600" style="background-color:#000000">
				<p>Your browser is sooooo old ! Download a modern one now !</p>
			</canvas>
		</div>
	</body>
</html>

Then we will add a basic CSS sheet, Nous allons rajouter une feuille CSS basique, in order to have a nice looking “block” form:

 
body{
	font-family: 'Arial', sans-serif;
	font-size: 0.8em;
	width: 100%;
	min-width :800px;
}
#canvas{
	margin: auto;
	border-style:solid;
	border-color:#ffffff;
}
#main{
	width : 800px;
	padding-bottom: 20px;
	margin: auto;
}

form{
	font-size: 0.8em;
	width: 300px;
	background-color: #0066DD; 
	margin: auto;
	padding: 10px;
	border-style: solid;
	border-width: 2px;
	border-color: #0099FF;
	text-align: left;
	color: #ffffff;
	opacity:0;
}
#submit{
	text-align: right;
}

So we have our working base, that should looks like this :

Animating the form

We start by creating our DOMElement() object, with the HTML element as a parameter :

 
function init() {
	canvas = document.getElementById("canvas");
	//DOMElement creation
	form = document.getElementById("myform");
	formDOMElement = new DOMElement(form);

As the rotation center is at the coordiantes (0,0) by default, we have to move it at the center of the form. To do this, we use the regX and regY properties of the DOMElement() object.

Then, we move the form above the screen, by giving it a negative Y coordinate. We also center it on the X coordinate.

 
	//move it's rotation center at the center of the form
	formDOMElement.regX = form.offsetWidth*0.5;
	formDOMElement.regY = form.offsetHeight*0.5;
	//move the form above the screen
	formDOMElement.x = canvas.width * 0.5;
	formDOMElement.y =  - 200;

Next, we add the DOMElement() object to the display list, with the addChild() method of the Stage() object.

 
	//add the formDOMElement to the display list
	stage.addChild(formDOMElement);

All we have to do now is to create a Tween(), thank’s to the TweenJS library.

 
	Ticker.setFPS(32);
	Ticker.addListener(stage);
	
	//Apply a tween to the form
	Tween.get(formDOMElement).to({alpha:1, y:canvas.height * 0.5, rotation:720},2000,Ease.cubicOut);

We can now see our form falling from the top of the screen. For me, best results comes with Chrome, it may vary from one browser to another. On Internet Explorer there is an issue. the form lose the focus again and again and is not usable.

To fix this, we add a callback to our tween. In this callback function, we remove the form from the display list :

	[...]
	Tween.get(formDOMElement).to({alpha:1, y:canvas.height * 0.5, rotation:720},2000,Ease.cubicOut).call(tweenComplete);
}
function tweenComplete(){
	stage.removeChild(formDOMElement);
}		

The form is no longer in the EaselJS’s stage display list, but it stays in place. If we want to apply a new Tween to it, we’ll have to add the form to the display list again.

Apply a transparency effect to the form

To conclude, we will add a “turn light on/off” effect to the form. Let’s listen to the “onchange” event of the checkbox, like this :

<input type="checkbox" name="case" id="case" onchange="onCheckClick(this)"/> 

In the event callback, we get back the checkbox’s state. In relation to this state, we assign a target value to the alpha property of our form. We have to destroy any tween that would be still running on the formDomElement. Then we add it to the display list and execute the Tween.

We assign the “oncomplete” event’s callback of our tween to our tweenComplete function, which will remove the form from the display list again, fixing the Internet Explorer issue.

//Checkbox change 
function onCheckClick(e){
	//get the checkbox element
	var myCheck = e;
	//create a property to stock the alpha target value for the tween
	var alphaTarget;
	//add the form to the display list (IE)
	stage.addChild(formDOMElement);
	//If the checkbox's state is checked, turn off the lights, else turn them on.
	myCheck.checked == false?alphaTarget=1:alphaTarget=.2;
	//remove all tweens on the DOMElement object (in case of unfinished tweens when user click on the checkbox)
	Tween.removeTweens(formDOMElement);
	//use the callback tweenComplete to remove the form 
	//from the display list (IE) when the tween is complete.
	Tween.get(formDOMElement).to({alpha:alphaTarget},1000,Ease.bounceIn).call(tweenComplete);
}

Click here to see the result.

What we can’t do in EaselJS with DOMElements

the DOMElement object can be added to the display list, but it inherit only partly from the DisplayObject() class, cause it is never physically added to the canvas in a bitmap form. All transformations are made in the CSS properties of the HTML element.

So, many DisplayObjects methods can’t apply to a DOMElement(): globalToLocal(), hitTest(), filters, and all things that CSS can’t do. On the other side, we can play with skew, for example, and bend a little our DOMElement.

To conclude…

The main flaw of Flash animations is the difficulty to make text contents readable by search engines robots. This flaw still remain in the HTML5 canvas. The DOMElement() object will fix this, by including directly our texts, titles, images, and why not our videos and forms in canvas animations, while keeping them reachable for SEO.

More, for some games, and on some browers, keeping some elements outside of the canvas, in a HTML tag, can make us gain some precious FPS, as these elements are not drawn on the canvas.

28/04/2012
by Frederic CHAPLIN
0 comments

Toolkit for CreateJS : A look inside the code

Here’s the second video about the Toolkit for CreateJS, where we have a look inside the code exported in the first episode. The video is in french but you’ll have english subtiles.

You’ll watch how is the javascript version of the code for the Flash Professionnal scene and it’s library.

YouTube Preview Image

A third and a fourth video are on theyr way. One about adding interaction with an exported button, and another about code manipulation, in particular the creation of new classes inheriting from the ones that you can find in the exported javascript code.

Share at will !

25/04/2012
by Frederic CHAPLIN
7 Comments

Tutorial – Export assets from Adobe Flash CS6 with Toolkit for CreateJS

Flash CS6 will be released in may 7th in France. Amongst a few new tools, it’ll be packed with the new Toolkit for CreateJS, which allow partial exports of the Flash CS6 scene to HTML5 canvas, using createJS.

I’m recording a few videos to help using it at best. Theese will be in french but there’ll be caption in english. We’ll start with this videos, showing a simple export.

YouTube Preview Image

For now, quality is not at best, but this post will be updated later with a better quality video. Meanwhile, don’t hesitate to watch the video directly on u-tube. It’ll be better. And don’t forget to share !

In the next one, we’ll explore the exported code, html5 and javascript side, it’s structure, et the manner to use it to go farther than a simple export.

15/04/2012
by Frederic CHAPLIN
2 Comments

Random hexagon map generation using HTML5 canvas and createJS

Hexagonal tiles maps with CreateJS

This new set of posts will focus on hexagonal tiles map generation. The subject is broader than you might think. It may involve MVC architecture, random generation, pathfinding algorithms, implementation of biomes, management of user interfaces, and server-side connections by sockets. And this is certainly not an exhaustive list.

Here is what I did recently: a basic architecture for random maps generation including pathfinding:

Hexagon map generator made with createjs and html5 canvas

This generator is a part of a larger project and will evolve over time. In the next posts I will introduce some of the techniques used. And I’ll start right now with the MVC architecture for use with createJS and a note on the Promises (or futures, or deferreds).

 

Coding MVC with createJS

Over time I spend programming the javascript, I feel an increasingly need to organize my code, find and keep a clear logic. But with an untyped prototyped language, code can quickly become a horror.

I had the chance to participate recently in the Web-5 conference in Béziers. Among the speakers was Kamil Trebunia, which introduced some MVC code architecture for HTML5. My map generator was already done at this time, and worked fine. But after seeing this presentation, I deeply changed it into a Model View Controller architecture.

What’s the point ?

The main interest for me is that with a decoupled view and data control, we gain a great clarity in code. Which greatly facilitates the maintenance, modification, and the addition of new features. On a constantly evolving project, I think it’s essential to keep things in order. This architecture also makes possible to reserve some processing to the server side, biome management for example, and other to client-side processing, rendering for example.

MVC architecture with EaselJS

Entry point

The entry point of the application is the main.js file. Its role is to load data files, create a namespace and store global variables, then start up the system.

//some parts of the code were written by Kamil Trebunia.
(function () {
	'use strict';
	
	window.ylende = {};
	var screenWidth,screenHeight,wrapper,agent,preloader;
	
	ylende.SPRITE_MAP_URL = "data/sprites.json";
	ylende.SOUND_MAP_URL = "sounds.json"; //not yet used
	ylende.CONFIG_URL = "data/config.json";
	
	window.addEventListener("load", function () {
		
		if(!(!!document.createElement('canvas').getContext)){
				wrapper = document.getElementById("canvasWrapper");
				wrapper.innerHTML = "Your browser does not appear to support " + "the HTML5 Canvas element";
				return;
		}
		
		ylende.agent = window.navigator.userAgent.toLowerCase();
		
		var configD = ylende.net.getJSON(ylende.CONFIG_URL);
		var spriteMapD = ylende.net.getJSON(ylende.SPRITE_MAP_URL);
		var imagesD = new ylende.Deferred();
				
		spriteMapD.addCallback(function (spriteMap) {
			
			var objectsURIs = Object.keys(spriteMap);
			var loadedObjectsNumber = 0;
			
			objectsURIs.forEach(function (uri) {
				
				spriteMap[uri].image = new Image();
				spriteMap[uri].image.addEventListener("load", function () {
					loadedObjectsNumber += 1;
					if (loadedObjectsNumber === objectsURIs.length) {
						imagesD.callback();
					}
				}, false);
				spriteMap[uri].image.addEventListener("error", function () {
					imagesD.errback();
				}, false);
				spriteMap[uri].image.setAttribute("src", spriteMap[uri].url);
			});
		});
		
		ylende.Deferred.gatherResults([spriteMapD, configD, imagesD]).addCallback(function () {
			ylende.images = spriteMapD.result[0];
			
			ylende.mapDataStore = new ylende.Store();
			var core = new ylende.Core({
				entityTypes: spriteMapD.result[0],
				config: configD.result[0]
			});
			core.init();
			
			ylende.view = new ylende.View(core);
			ylende.view.play();
			
		});
		
		
	}, false);
	
})();

Loading data – for now, these are JSON configuration files – is provided by the use of Deferreds. This technique is useful for not blocking data loading with asynchronous callbacks. It also helps bring all loading results into a single condition, as in this code: it only runs when all Deferreds are in State.SUCCESS state.

The main controller: the Core class

Then we start the separation of the code by creating an instance of Core. Core class is the main controller of the application. I pass my data objects as parameters, which allows him to create his own data: the map. The Store object is just a dummy for now. I’ll talk about it later.

ylende.Deferred.gatherResults([spriteMapD, configD, imagesD]).addCallback(function () {
			ylende.images = spriteMapD.result[0];
			
			ylende.mapDataStore = new ylende.Store();
			var core = new ylende.Core({
				entityTypes: spriteMapD.result[0],
				config: configD.result[0]
			});
			core.init();
[...]

Here’s the Core class:

(function () {

	'use strict';

	function Core(config) {
		this.initialize(config);
	}

	Core.prototype.initialize = function(config){
		this.map = {};
		this.config = config;
	}

	Core.prototype.init = function(){
		this.entityTypes = this.config.entityTypes;
		this.hexMap = new ylende.HexMap();

		if (!this.config.map){
			this.map = this.hexMap.generateMap(this.config.config.biome);
		}else{
			this.map = this.hexMap.setMap(this.config.map);
		}
	}
	window.ylende.Core = Core;
})();

The class makes sure that map data are existing in the data object. If this is not the case, it creates a new mapcontroller (HexMap instance). HexMap class contains the scripts for generating map datas (I’ll talk about it in the next post).

The main view: the View class

We’re back in the startup script (main.js) where I instantiate the View class:

[...]
        ylende.view = new ylende.View(core);
        ylende.view.play();
       	});

… passing it the main controller as parameter. The View is the general view of the application. It will be used to collect general window events (onresize, onscroll …), but also of canvas, and therefore of course createJS. Thus, the View contains the course, while all other type objects view (such as hexagons) are elements in the course EaselJS (containers, … movieclips).

Here’s the code og the View class.

(function () {

	'use strict';

	function View(controller) {
		this.initialize(controller);
	}
	var canvas = document.getElementById("stageCanvas");
	var map;

	View.prototype.initialize = function(controller){

		this.gameBounds = {top: 0, bottom: canvas.height,right: canvas.width, left: 0};
		this.controller = controller;
		this.stage = new Stage(canvas);
		this.stage.enableMouseOver();

		Touch.enable(this.stage);
		Ticker.setFPS(32);
		Ticker.addListener(this.stage);

		map = new ylende.HexMapView(this);
		this.stage.addChild(map);

		this.inviteText = new Text("Click on it, drag it - MouseWheel to zoom in and out - Double click to center", "bold 11pt courier","#FFF");
		this.inviteText.textAlign = "center";
		this.inviteText.x = this.gameBounds.right * .5;
		this.inviteText.y = this.gameBounds.bottom - 15;
		this.stage.addChild(this.inviteText);

		window.onorientationchange = function (e) {
			//console.log(e);
		};

		window.onresize = function (e) {
			//console.log(e);
		};

		window.onscroll = function (e) {
			//console.log(e);
			return false;
		};

		window.onmousewheel = function(e){
			//console.log(e);
			map.resize(e.wheelDelta);
		}
		//firefox users...
		window.addEventListener('DOMMouseScroll', scroll, false);
		function scroll(e){
			map.resize(e.detail*-0.01);
		}
	}

	View.prototype.play = function(){
		Ticker.setFPS(32);
		Ticker.addListener(this);
	}

	View.prototype.tick=function(){
	}

	window.ylende.View = View;
})();

The View class creates an object of type HexMapView, which will receive map data from the HexMap class, and will take care of displaying it (and only this). This pattern is repeated for each object on the map (hexagons. ..), with a controller that manages object properties and a view that displays them.

Note that in a classic MVC, views classes all implement a draw () function. This is not the case with CreateJS, as the Ticker object updates the views automatically.

What about data ? The Store class

On the model side, for now, i just created a minimal Store class that I’ll develop later. My goal is to make datas evolving with time.

Conclusion: CreateJS and MVC

Finally, CreateJS is well suited for MVC. It may seem tedious to organize the code in that way, but reserving to EaselJS the only task of the display, the code is much cleaner and logical, and finally you work better and faster.

In the next post, I will show how I generate the map, and talk about Dijkstra’s algorithm and its use in a hexagonal context.

06/03/2012
by Frederic CHAPLIN
0 comments

Space kamikaze 2 – Demo

I did not had a lot of time to spend on Space Kamikaze last past weeks, and even less on this blog.
Je n’ai pas eu beaucoup de temps à passer sur Space Kamikaze ces dernières semaines, et encore moins sur ce blog. I’m totaling a 30 hours of work on the game, I think … But I lose a lot of time to do graphics for a… questionable result … .
Space Kamikaze 2
Here is an almost playable demo
, with limited graphics, and a rather thin content.
There are still many bugs to solve and points for improvement!
But I have other places to visit in the world of HTML5, and it’s time I go …

I will improve the game throughout my free time, and developments of CreateJS (the next version of SoundJS should improve sound management, and PreloadJS should allow me to unify the loading of sounds and images files.)

Here are some small pieces of code on some features.

A Command Pattern for ennemies behavior

Pour le comportement des ennemis, je voulais quelque chose de souple, que je pourrais changer à la volée, et avoir la possibilité d’en ajouter de nouveaux facilement, et sans retoucher au code.
Pour cela, j’ai créé une classe “Behavior.js” qui devient une propriété supplémentaire de ma classe Foe. Lors de la lecture du XML pour la construction de l’ennemi, je lis quel comportement appliquer à mon ennemi. Ce nom est passé en paramètre de la classe Behavior, qui remplit automatiquement une liste d’ordres.
Dès que l’ennemi est activé, il commence à exécuter la liste d’ordres, une tache après l’autre. En fait à chaque évènement tick du moteur d’EaselJS, je vérifie que le vaisseau n’est pas en état “iddle”, si oui, je lance l’exécution de l’ordre suivant.
For ennemies behavior , I wanted a flexible solution, allowing me to change things on the fly, and making me able to easily add new ones, with a few code to edit.
So, I created a class “Behavior.js” which becomes an additional property in my class Foe. When reading the XML for the construction of the enemy, I read which behavior must be applied to my foe. This variable is passed as a parameter of the Behavior class, which automatically populates a list of orders.
Once the enemy is activated, it starts to execute the orders list, one after another.
In fact, at each tick event of the EaselJS engine, I check that the ship is not in a “iddle” state, if so, I start running the next order in list.

 
(function (window) {
	function Behavior(behavior,caller) {
        this.initialize(behavior,caller);
    }
	var actualTaskNumber = 0;
	Behavior.prototype.initialize = function (behavior,caller) {
		
		this.behavior = behavior;
		this.caller = caller;
		this.orders = Array();
		this.init(behavior);
	}
	
	Behavior.prototype.init = function (behavior) {
		switch(behavior){
			case("straightDown"):
				this.basePos = new Point(Math.round(Math.random()*((700+1-100)+100)),0);
				this.baseRotation = 90;
				this.orders.push({order:"advance",amount:200});
				this.orders.push({order:"shoot"});
				this.orders.push({order:"advance",amount:800});
				break;
			case("diagLeftDown"):
				this.basePos = new Point(Math.round(Math.random()*((700+1-500)+500)),0);
				this.baseRotation = 125;
				this.orders.push({order:"advance",amount:100});
				this.orders.push({order:"shoot"});
				this.orders.push({order:"advance",amount:1000});
				break;
			case("diagRightDown"):
				this.basePos = new Point(Math.round(Math.random()*((300+1-100)+100)),0);
				this.baseRotation = 45;
				this.orders.push({order:"advance",amount:100});
				this.orders.push({order:"shoot"});
				this.orders.push({order:"advance",amount:1000});
				break;
		}
    }
	Behavior.prototype.getAction = function(){
		if (this.orders.length==0){
			
		}else{
			this.caller.executeOrder(this.orders.shift());
		}
	}
    window.Behavior = Behavior;
} (window));

Saving games with LocalStorage

This part was pretty simple and works well. A tip for IOS devices: the key name in LocalStorage must be emptied before filling it, otherwise you could run into a “Not enought space …” error.

At the end of each level, I update the information stored. This information is nothing more than the “Player.js” instance, serialized in JSON in order to have a plain text: LocalStorage saves only String typed data.

When loading, I deserializes the data and fill the Player object’s properties.

The sound

I did some tests with SoundJS, but it is clear that this library still has some progress to make: the events are somewhat confusing, and some errors occurs unexpectedly. But with short, light sounds to load, it works pretty well.

Files to load were included in the XML, without extension. Indeed, differents extensions are needed depending on the browser. So you need to look at the user agent, and select the right extension for the sound files that will be loaded before starting.

Tablets and smartphones

Space Kamikaze tourne sur les tablettes et les téléphones. Encore une fois, je détecte l’user agent pour savoir quel est le matériel utilisé. Quand un matériel tactile est identifié, je fais passer le mode de tir en automatique, ce qui rend le jeu complètement jouable…
… Si le matériel est au minimum un Ipad1.
Pour être complètement propre, il me faudrait créer une version de dimensions plus petite du jeu, et optimiser beaucoup mieux. Quelques pistes d’optimisation : limiter les boucles switch, essayer de limiter la hiérarchie sur le stage en utilisant moins de containers, alléger les sprites… Si j’ai du temps, un de ces jours, je le ferai…

Kamikaze Space runs on tablets and phones. Again, I detect the user agent to determine which device is used. If a tactile device is identified, I enable the automatic fire mode, which makes the game completely playable …
… If the device is at least one Ipad1.
To be completely clean, I should create a smaller size version of the game, and optimize much better. Some keys in the search for optimization: limiting the use switch loops, try to limit the use of inheritance by using less containers, reducing the sprites sizes … If I have time, someday, I will do these improvements …

Conclusion

Working with EaselJS is really nice.
Ok, not as enjoyable as doing the same in AS3, but we can do very clean stuff. The rendering is very unequal on browsers, the best results being obtained with IE9 for me.
The CreateJS Suite is going to be a must soon. It has great potential for improvement (an event-integrated system would be great), and is bound to attract many flashers.

And for Space Kamikaze, there is still so much to do, but I’m in not in a hurry …
Meanwhile, this work could be usefull in part for other games, making development much shorter.

For now, I intend to deal with ExtJS, Sencha Touch, perhaps with a bit of NodeJS …

01/03/2012
by Frederic CHAPLIN
0 comments

EaselJS become CreateJS and will be used in Flash IDE to export in HTML5 canvas

This week some good news were heard from Amsterdam and the Creative Layer Blog from Adobe, all about EaselJS and it’s future.

Reminding the story

Since january 2011, Grant Skinner is working on an open source project called EaselJS. This is a library powering the HTML5 canvas tag with actionscript-like codings methods and objects. With the help of Mike Chambers from adobe and others contributors, the project growed fast.
Soon, two other libraries are created with the same purpose: SoundJS and TweenJS, and so is ZOE, a convert tool extracting SpriteSheets from SWF.
Some cool demo are showed, like this one from Mike Chambers himSelf and the well known “Pirates loves daisies”, a tower defense game using partly EaselJS.

Pirates loves daisies

In november 2011, Grant posted a video on Vimeo about the future of EaselJS, showing an export panel in Flash exporting an entire animation in the HTML5 canvas as EaselJS code, and some cool rendering option like SVG or WebGL.

The FITC Amsterdam 2012

FITC Amsterdam

Ok, I wasn’t there… But I saw the direct stream from the Woodoo Lounge, with Grant Skinner, the one with the wiskey thing (It was great !), and when I heard some guys asking about “createJS”, I was very curious to find out what it could be.
In fact, Grant introduced the EaselJS suite, which is now called the CreateJS suite. A new site opened at the same time : createJS.com

CreateJS website

I was glad to see that a new addition was made to the suite : PreloadJS (coming soon…). I’m still working on my little EaselJS based game and had to use A third part library to preload my content (PxLoader – worth the test). It’s a good idea to integrate a loading solution in CreateJS.

The Adobe Flash Professional Toolkit for CreateJS

Finaly, a new blog post in the Adobe’s Creative Layer Blog revealed “The Adobe Flash Professional Toolkit for CreateJS”, a Flash IDE extension showing as a panel, which export an entire Flash animation in the HTML5 canvas, using Eas… sorry… CreateJS.
The video shows the resulting code, a html5 page importing Easel.js, and amonst others, a “MovieClip.js” class.

The tool could be a part of Flash CS6, but nothing is clear about it’s availability when the next version of Flash will be released.
CreateJS is going to grow very fast from this point. This is good news.

03/02/2012
by Frederic CHAPLIN
1 Comment

Creating a HTML5 game with EaselJS : Space Kamikaze 2

Spalsh screen Space Kamikaze

I wanted to move to Sencha and ExtJS V2 (which looks promising) in February, and leave EaselJS aside, but I finally changed my mind.
I will try, over this year, not only to study different libraries HTML5, but also, for each of them, create an app or a game. After all, one can’t realize the possibilities of a tool only by using it on “real life” problems.

With this in mind, I got into creating my first HTML5 game : Space Kamikaze 2. Sencha can wait until March!

Space Kamikaze 2

This game will be a not-so-classic Shoot-them-up (I hope so!), of which here is roughly the specifications :

Content
  • The game will be amde of 50 levels with growing difficulty
  • for each level, player will have to survive each wave of enemy ships
  • Between levels, the player can buy upgrades for his ship with money gathered on exploded enemies
  • It’ll be possible to save the game between levels
  • Audio will be basic
Compatibility
  • The game will be compatible with all latest versions of major browsers on desktop: FireFox, IE9, Chrome,Safari and Opéra
  • The game will be playable on Android and iPad tablets, with some adjustments
Delay
  • The game must completed by the end February. ( I think there are 6 or 7 full days of work, 1 ° but I also have a real job full-time during the day – 2 ° I am not alone in life, which leaves me with approximately one hour per day to work on it.. ;) )
Technics
  • The game will be coded with HTML5+Javascript, using EaselJS and JQuery frameworks
  • The game will be coded with OOP

In the beginning was the schematization

Before starting, I scribbled two or three schematic, one for the navigation by the user point of view, one for different interface elements, and last roughly the skeleton of my classes. I knew these were not final scheme and that I should them adapt gradually. But it’s a huge time saver to take a moment to think about how to do things before the start.

Mechanics and navigation menu

I then laid the basis of the navigation engine in the game. I defined some states (“main-menu”, “level”, “level-ending”, “shop” …) that I associate with a gameState variable. EaselJs it’s Ticker object, allow you to read this property’s status during a “tick”. The game then displays the interface part associated with that state, after the scene clean-up.
This system allows me to deal with a pretty cruel lack in EaselJS at this level: almost no event system. But I am hopeful that the many contributors to the framework will fill sooner or later this shortcoming. If I have some time to spend, I’ll try to implement a system of my own with the help of jQuery. But for now, this states system works pretty well.

Space Kamikaze Menu

External datas

For now, external data is loaded by XML with Ajax. But it is clear that this system has serious limitations in terms of security. I think I can do better with a database.
The XML contains the levels contents with the successive waves of enemies. Each enemy is described by an entry in the XML: type (bomber, chaser …), speed, hit points and behavior.
So I also created a class “Foe.js”, which is built based on data from the XML. This class contains another class “Behavior.js”, which governs the behavior of the enemy ship (moves, shoots), still according to the XML.

 	
<level title="1">
		<wave>
			<ship type="bomber" behavior="harmless" shields="1" maxFire="0" delay="3000" side="top"/>
			<ship type="bomber" behavior="harmless" shields="1" maxFire="0" delay="2000" side="left"/>
			<ship type="bomber" behavior="harmless" shields="1" maxFire="0" delay="1000" side="right"/>
			<ship type="bomber" behavior="harmless" shields="1" maxFire="0" delay="500" side="right"/>
			<ship type="bomber" behavior="harmless" shields="1" maxFire="0" delay="500" side="right"/>
		</wave>
		<wave>
			<ship type="chaser" behavior="harmless" shields="1" maxFire="0" delay="3000" side="top"/>
			<ship type="bomber" behavior="harmless" shields="3" maxFire="1" delay="500" side="top"/>
			<ship type="bomber" behavior="harmless" shields="3" maxFire="1" delay="500" side="top"/>
			<ship type="bomber" behavior="harmless" shields="3" maxFire="1" delay="500" side="top"/>
		</wave>
	</level>

With this system, I can easily create new enemies and new levels. I even thought of making a custom levels generation system. Anyway, later, if I have time.

Gameplay

The gameplay – that is, for me, everything is managed in the “gameLoop” – is still quite poor.
For the collisions, I simplified to the max with a management system “by circles”, which is fine for a Shoot-em-up. All the rest is operated by classes.
The score is counted, the money gains too.
I still work on the handling of the ship, that i’d like to be less flexible.

InGame ScreenShoot

Graphismes et animations

Again, it is still very limited.
I created the ships on Photoshop, and some animations with Flash. I then used Zoe to transform swf to SpriteSheets.
But at this state, I have a problem to solve:I have to slow down my animations, but I need to keep my framerate for general movements. I saw on this tutorial from David Rousset a method that seems the right one to me, but I try to find an easy way to apply it in my own system.

It’s still a bit early for a demo, but I hope to post one next week.
In the meantime, I hope I progress through menus, and the shop.

Feel free to send me advices, or ask me details or snippets, if you’re interested.

22/01/2012
by Frederic CHAPLIN
0 comments

Discover EaselJS : HTML5 XML dynamics carrousel and dock menus

Today, I’ll just apply what I have shown in my previous posts to create advanced dynamics menus, with externalized in XML content.
So I’ll create a Menu() class, which will take a Jquery loaded XML as parameter . This class will create the menu’s icons and manage various mouse events to react according to the position of the pointer.

XML Loading

The XML File will look like this:

<?xml version="1.0" encoding="utf-8" ?>
<menu>
	<item>
		<title>icon1</title>
		<color>#733</color>
		<source>img/1.png</source>
		<link>index.html</link>
	</item>
	<item>
		<title>icon2</title>
		<color>#737</color>
		<source>img/2.png</source>
		<link>index.html</link>
	</item>
	<item>
		<title>icon3</title>
		<color>#773</color>
		<source>img/3.png</source>
		<link>index.html</link>
	</item>
	<item>
		<title>icon4</title>
		<color>#337</color>
		<source>img/4.png</source>
		<link>index.html</link>
	</item>
	<item>
		<title>icon5</title>
		<color>#373</color>
		<source>img/5.png</source>
		<link>index.html</link>
	</item>
</menu>

Save it in a “/data” folder at the site ‘s root and name the file “menuData.xml”.

In order for the XML to be loaded on all covered browsers (Chrome, Safari and Firefox …), using jQuery is the best way.
The loading code will be written in the “main.js” file, like this:

(function(){
var stage;
var myCanvas = $("#stageCanvas").get(0);

this.init = function() {
	stage = new createjs.Stage(myCanvas);
	
	createjs.Ticker.setFPS(32);
        createjs.Ticker.addListener(this);
       $.ajax({
		type: "GET",
		crossDomain: false,  //edit du 25/01 : cette propriété doit être passée à false. 
		url: "data/menuData.xml",
		dataType: "xml",
		success: initMenu
	});
}

this.initMenu = function(data){
	console.log(data);               
}

this.tick = function(){
}

window.onload = init();
})();

Your browser’s console should show the xml content.

Prior to start to serious things, I will change a little the Icon class to make it fully suitable for use in our menu class:

(function (window) {
	function Icon(iconWidth,imgSrc,color,titleText,link)  {
        //Passe les paramètres au constructeur
		this.initialize(iconWidth,imgSrc,color,titleText,link);
    }
	
	//Héritage de container
	Icon.prototype = new createjs.Container();
	Icon.prototype.Container_initialize = Icon.prototype.initialize;
	Icon.prototype.Container_tick = Icon.prototype._tick; 
	
	//Constructeur
    Icon.prototype.initialize = function (iconWidth,imgSrc,color,titleText,link) {
		this.Container_initialize();//Execute le contructeur de la class Container
		this._iconWidth = iconWidth;//Taille de l'icone
		this._link = link;//Lien URL
		this._imgSrc = imgSrc;//Source de l'image
		this._color = color;//Couleur du fond
		this._titleText = titleText;//Texte
        //Placement du point d'origine au centre de l'icone
		this.regX = this._iconWidth*0.5;
		this.regY = this._iconWidth*0.5;
		//Création du fond
		this.graphic = new createjs.Graphics();
		this.graphic.beginFill("#777");
		this.graphic.drawRoundRect(0,0,this._iconWidth,this._iconWidth,this._iconWidth*.1);
		this.roundRectangle= new createjs.Shape(this.graphic);
		this.addChild(this.roundRectangle);
		//Création du texte	
		this.text = new createjs.Text(this._titleText, "bold 16px Courier",this._color);
		this.text.textAlign ="center";
		this.text.y = this._iconWidth+5;
		this.text.x = (this._iconWidth*0.5);
		this.text.alpha = 0; //Le texte est masqué 
		this.addChild(this.text);
		//Ajout de l'image	
		this.bitmap = new createjs.Bitmap(this._imgSrc);
		this.bitmap.regX = this._iconWidth*0.5;
		this.bitmap.regY = this._iconWidth*0.5;
		this.bitmap.x =  this._iconWidth*0.5;
		this.bitmap.y =  this._iconWidth*0.5;
		this.addChild(this.bitmap);
		
		//En cas de click : ouverture d'une nouvelle fenetre
		this.roundRectangle.onClick = function(){
			//console.log("click "+this.graphics);
			window.open(this._link);
			
		}
		//En cas de survol
		this.roundRectangle.onMouseOver = function(){
			//console.log("mouseOver ");
			//On rafraichit le graphics du fond 
			this.parent.graphic.clear();
			this.parent.graphic.beginFill(this.parent._color);
			this.parent.graphic.beginStroke(createjs.Graphics.getRGB(255,255,255));
			this.parent.graphic.drawRoundRect(0,0,this.parent._iconWidth,this.parent._iconWidth,this.parent._iconWidth*.1);
			this.parent.roundRectangle.graphics = this.parent.graphic;
			//On aggrandis l'icone
			this.parent.scaleX = 1.2;
			this.parent.scaleY = 1.2;
			//On montre le texte
			this.parent.text.alpha = 1;
			
		}
		//Si le pointeur quitte l'icone
		this.roundRectangle.onMouseOut = function(){
			console.log("mouseOut ");
			//On rafraichit le graphics du fond 
			this.parent.graphic.clear();
			this.parent.graphic.beginFill("#777");
			this.parent.graphic.drawRoundRect(0,0,this.parent._iconWidth,this.parent._iconWidth,this.parent._iconWidth*.1);
			this.parent.roundRectangle.graphics = this.parent.graphic;
			//L'icone reprends sa taille d'origine
			this.parent.scaleX = 1;
			this.parent.scaleY = 1;
			//Le texte est masqué
			this.parent.text.alpha = 0;
		}
	}
	
    Icon.prototype._tick = function () {
		this.Container_tick();
    }
    window.Icon= Icon;
} (window));

A link parameter is passed to the Icon, which can now be clicked, opening a new browser window.
The Icon width grows when the pointer is over it, and shrink back to normal when the mouse get out of it.

The DockMenu class

The DockMenu() class will take the loaded XML datas as parameter. It will read the datas and create icons in a “for each” loop.
The icons are stored in an array “icons”. This array is not used here, but it can be useful, as we will see below.

(function (window) {
	function DockMenu(xml, menuIconMin, menuIconMax)  {
        //passe les paramètres au constructeur
		this.initialize(xml, menuIconMin, menuIconMax);
    }
	
	//Héritage de Container();
	DockMenu.prototype = new createjs.Container();
	DockMenu.prototype.Container_initialize = DockMenu.prototype.initialize;
	DockMenu.prototype.Container_tick = DockMenu.prototype._tick; 
	
    DockMenu.prototype.initialize = function (xml, menuIconMin, menuIconMax) {
		
		this.Container_initialize(); //obligatoire
		this.icons = new Array(); //tableau qui contiendra les icones du menu
		this._data = xml;
		this._menuIconMin = menuIconMin; //Taille minimale des icones 
		this._menuIconMax = menuIconMax; //Taille maximale des icones
		
		this._space = this._menuIconMin*0.5; //Espace entre les icones
		
		this._numIcons = $(this._data).find('item').length;//extraction du nombre d'icones
		var root = this; //Création d'une référence à la racine de la classe
		var num = 0;
		//Boucle de création des icones
		$(this._data).find('item').each(function(){
			//Extraction des paramètres de chaque icone dans le xml
			var title = $(this).find('title').text();
			var color = $(this).find('color').text();
			var source = $(this).find('source').text();
			var link = $(this).find('link').text();
			//Creation d'une icone avec ces paramètres
			var icon = new Icon(root._menuIconMin,source,color,title,link);
			//Placement et ajout à la liste d'affichage
			icon.x = num*( root._space + root._menuIconMin);
			icon.y = root._menuIconMax;
			root.addChild(icon);
			//ajout de l'icone dans le tableau icons
			root.icons.push(icon);
			//incrémentation
			num++;
		});	
		
	}
		
	DockMenu.prototype._tick = function () {
		this.Container_tick();
        //console.log("Icon Ticked");
    }
    window.DockMenu= DockMenu;
} (window));

All we have to do now is to instanciate the dock menu in the “main.js” file :

(function(){
var stage;
var myIcon;
var myMenu;
var myImage;
var myCanvas = $("#stageCanvas").get(0);

this.init = function() {
	stage = new createjs.Stage(myCanvas);
	stage.enableMouseOver();
	
	createjs.Ticker.setFPS(24);
	createjs.Ticker.addListener(this);
	
	$.ajax({
		type: "GET",
		crossDomain: false,
		url: "data/menuData.xml",
		dataType: "xml",
		success: initMenu
	});
}

this.initMenu = function(data)
{
	myMenu = new DockMenu(data, 60, 80);
	myMenu.x = 180;
	myMenu.y = 200;
	stage.addChild(myMenu);
	stage.update();
}
this.tick = function(){
	
      stage.tick();
}

window.onload = init();
})();

Here is the result
(Tested on Chrome, Safari IE9, Firefox and Opera.)

Using tween.js


At this state, AS3 coders reading this are probably thinking: “Yeah… But with Flash, At least, we had Tween transistions. It was better…”.
Great news : Grant Skinner created two other libraries working hand in hand with EaselJS: tween.js et sound.js.
Here we will use Tween.js in our Icon class to make it more smoothy.
First, download the Tween.js file, and add it in your “js/libs” project’s folder. Then import it in the html page :

<script src="js/libs/tween.js"></script>

Next, we have to modify a little our Icon() class to add some resize transitions:

(function (window) {
	function Icon(iconWidth,imgSrc,color,titleText,link)  {
        //Pass parameters to the class constructor
		this.initialize(iconWidth,imgSrc,color,titleText,link);
    }
	
	//container() inheritance
	Icon.prototype = new createjs.Container();
	Icon.prototype.Container_initialize = Icon.prototype.initialize;
	Icon.prototype.Container_tick = Icon.prototype._tick; 
	
	//Constructor
    Icon.prototype.initialize = function (iconWidth,imgSrc,color,titleText,link) {
		this.Container_initialize();//required
		this._iconWidth = iconWidth;
		this._link = link;
		this._imgSrc = imgSrc;
		this._color = color;
		this._titleText = titleText;
          //Place the registration point in the center of the icon
		this.regX = this._iconWidth*0.5;
		this.regY = this._iconWidth*0.5;
		//Background creation
		this.graphic = new createjs.Graphics();
		this.graphic.beginFill("#777");
		this.graphic.drawRoundRect(0,0,this._iconWidth,this._iconWidth,this._iconWidth*.1);
		this.roundRectangle= new createjs.Shape(this.graphic);
		this.addChild(this.roundRectangle);
		//Text	
		this.text = new createjs.Text(this._titleText, "bold 16px Courier",this._color);
		this.text.textAlign ="center";
		this.text.y = this._iconWidth+15;
		this.text.x = (this._iconWidth*0.5);
		this.text.alpha = 0; //Le texte est masqué 
		this.addChild(this.text);
		//Image
		this.bitmap = new createjs.Bitmap(this._imgSrc);
		this.bitmap.regX = this._iconWidth*0.5;
		this.bitmap.regY = this._iconWidth*0.5;
		this.bitmap.x =  this._iconWidth*0.5;
		this.bitmap.y =  this._iconWidth*0.5;
		this.addChild(this.bitmap);
		this.tweenSize
		//If clicked, open a new window
		this.roundRectangle.onClick = function(){
			console.log("click "+this.graphics);
			window.open(this._link);
			
		}
		//Mouse over
		this.roundRectangle.onMouseOver = function(){
			console.log("mouseOver ");
			//Refresh the bg graphics
			this.parent.graphic.clear();
			this.parent.graphic.beginFill(this.parent._color);
			this.parent.graphic.beginStroke(createjs.Graphics.getRGB(255,255,255));
			this.parent.graphic.drawRoundRect(0,0,this.parent._iconWidth,this.parent._iconWidth,this.parent._iconWidth*.1);
			this.parent.roundRectangle.graphics = this.parent.graphic;
			//Resize icon with a Tween transistion
			//First we have to clean any Tween running
			createjs.Tween.removeTweens(this.parent);
			//Then create a tween object
			this.parent.tweenSize = createjs.Tween.get(this.parent).to({scaleX:1.3,scaleY:1.3},500);
			this.parent.text.alpha= 1;
			
		}
		//Mouse out
		this.roundRectangle.onMouseOut = function(){
			console.log("mouseOut ");
			//Refresh the bg graphics 
			this.parent.graphic.clear();
			this.parent.graphic.beginFill("#777");
			this.parent.graphic.drawRoundRect(0,0,this.parent._iconWidth,this.parent._iconWidth,this.parent._iconWidth*.1);
			this.parent.roundRectangle.graphics = this.parent.graphic;
			//Resize icon to base width with a Tween transistion
			createjs.Tween.removeTweens(this.parent);
			this.parent.tweenSize = createjs.Tween.get(this.parent).to({scaleX:1,scaleY:1},500);
			this.parent.text.alpha= 0;
			
		}
	}
	
    Icon.prototype._tick = function () {
		this.Container_tick();
    }
    window.Icon= Icon;
} (window));

And it’s much better!

Creating a carrousel menu


carrousel image
Here is another example of menu, which Flash technology made inevitable: the Carrousel menu.
I just changed the DockMenu class, to make a CarrouselMenu class. That said, I could have worked cleaner by creating a generic class “Menu”, and by inheriting from it to create my DockMenu and CarrouselMenu classes.

Here’s the CarousselMenu class.

(function (window) {
	function CarousselMenu(xml, menuIconMin, menuRadius)  {
        this.initialize(xml, menuIconMin, menuRadius);
    }
	//Container() inheritance
	CarousselMenu.prototype = new createjs.Container();
	CarousselMenu.prototype.Container_initialize = CarousselMenu.prototype.initialize;
	CarousselMenu.prototype.Container_tick = CarousselMenu.prototype._tick; 
	
    CarousselMenu.prototype.initialize = function (xml, menuIconMin, menuRadius) {
			this.Container_initialize();//Required
			this._icons = new Array();//Array containing menu icons
			this._data = xml;
			this._radius = menuRadius;//Rayon du menu
			this._speed = 1; //Rotating speed
			this._angles = new Array();//Array containing each icon angle.
			
			this._menuIconMin = menuIconMin;
			this._numIcons = $(this._data).find('item').length; //Icon count extraction
			
			var root = this;//Create a reference for the class root
			var num = 0;
			$(this._data).find('item').each(function(){ 
				
				//Extract parameters for each icons in xml
				var title = $(this).find('title').text();
				var color = $(this).find('color').text();
				var source = $(this).find('source').text();
				var link = $(this).find('link').text();
				//Create a new icon with these parameters
				var icon = new Icon(root._menuIconMin,source,color,title,link);
				var angle = num*(360/root._numIcons);
				//Add the icon angle and the icon to the arrays
				root._angles.push(angle);
				root._icons.push(icon);
				//placing it and adding it to the display list
				root.addChild(icon);
				num++;
			});	
	}
		
	CarousselMenu.prototype._tick = function () {
		this.Container_tick();
		//At each "tick" we browse all icons
		for (var i= 0; i< this._numIcons; i++)
		{
			var cible= this._icons[i];
			//We take the radian angle for the icon
			var radian = deg2rad(this._angles[i]);
			//And the icon is placed with cos and sin for this angle
			//The *.5 for Y allows a flattened circle, giving the impression of 3D
			cible.x = this.x+this._radius*Math.cos(radian);
			cible.y = this.y+this._radius*Math.sin(radian)*.5;
			//Math.Sin(radian) is always between 0 and 1
			//so we can use it's value as scale and alpha parameters
			Math.sin(radian)+1>0.8?cible.scaleX = cible.scaleY = 0.8:cible.scaleX = cible.scaleY=Math.sin(radian)+1;
			Math.sin(radian)+1<-0.5?cible.scaleX = cible.scaleY = 0.2:cible.scaleX = cible.scaleY=Math.sin(radian)+1;
			Math.sin(radian)>-0.5?cible.alpha = Math.sin(radian):cible.alpha = 0;
			//We add the speed value to the icon's angle
			this._angles[i]+=this._speed;
			//The modulo restrain the angle to a number between 0 and 360
			this._angles[i] %=360;
		}
    }
	//Transforms degrees to radian
	this.deg2rad = function (deg){
		return deg * (Math.PI/180)
	}
    window.CarousselMenu= CarousselMenu;
} (window));

Here’s my carrousel.
(Tested on Chrome, Firefox, IE9, Safari and Opera)
I used the same icon, but given that the management of alphas and scales is here devolved to menu itself, I removed the transitions of the Icon() class.
And yes, new design for the example page!
Note that Touch is not supported in this example, but it could be possible to set up the mobile support with the Touch object of easelJS.

Canvas and SEO

This is a cool menu, but it represent a problem in terms of SEO. Spiders just won’t see our links and texts in the canvas.
To correct this, we just have to add the structure of the menu under le canvas tag, like this:

<figure id="canvasWrapper" role="main">
	<canvas id="stageCanvas" width="720" height="540">
		<nav>
			<ul>
				<li><a href="*" title="icon1" target="_blank">icon1</li>
				<li><a href="*" title="icon2" target="_blank">icon2</li>
				<li><a href="*" title="icon3" target="_blank">icon3</li>
				<li><a href="*" title="icon4" target="_blank">icon4</li>
				<li><a href="*" title="icon5" target="_blank">icon5</li>
			</ul>
		</nav>
	<canvas/>	
</figure>

So robots will just ignore the canvas tag and read directly the nav with the links and any content you would want to be saw by search engines.

14/01/2012
by Frederic CHAPLIN
12 Comments

Discover EaselJS: OOP and complex objects creation

EaselJS can therefore reproduce in the HTML5 canvas some reflexes and work habits for developers coming from actionscript with a Stage object, a display list, and objects that fit into each other .
For developers who are new to these principles, they of may have started to see the great potential and flexibility they can provide.

But we still miss something essential: Object Oriented Programming (OOP), with inheritance, custom classes, and its benefits in terms of maintenance and reusable code
Wait a bit … In my previous post, I talked about the fact that all display objects that I described all inherit from the DisplayObject class.
So, couldn’t we extend classes like Container to create complex and reusable objects?
The answer is yes! This is what I will introduce in this post.

Extending the Container class

We want to create a new object which would display a custom icon that we could reuse in various kinds of menus without retouching the code.
We will create an Icon class, which will inherit the Container class. This class will accept theese parameters: size in pixels, a path to an image, a color, and a text. We want to use it this way:

var myIcon = new Icon(50,pathToMyPicture,"#fff","Home");
stage.addChild(icon);

The class will inherit all properties and functions from the Container class, and thus from the DisplayObject class.

If we look a little more closely at the Container class,which is possible here, we can see several things:

  • Everything is annotated, including the structure of the class. We can draw inspiration from this structure to create our own class
  • Container inherits from DisplayObject
  • An initialize() function is present: it is important since we will have to run this method in our child class
  • A _tick() function is present : this protected method is called by the Ticker object
  • Of course, all other methods of the Container class can be read and are annotated;

The Icon class structure

Here, we will not invent anything. The structure of our class must be identical to the Container class structure. We’ll just change its name, and change any references to DisplayObject to references to Container;
Here’s what it looks:

(function (window) {
	function Icon() {
        this.initialize();
    }
	//Inheritance from Container
	Icon.prototype = new Container(); 
	Icon.prototype.Container_initialize = Icon.prototype.initialize;
	Icon.prototype.Container_tick = Icon.prototype._tick; 
    
    Icon.prototype.initialize = function () {
                //call to initialize() method from parent class 
		this.Container_initialize();
               console.log("Icon initialized");
	}
     Icon.prototype._tick = function () {
	       //call to _tick method from parent class 
               this.Container_tick();
               console.log("Icon Ticked");
    }
    window.Icon= Icon;
} (window));

Save your file as “Icon.js” in the /js folder in our project, right next to main.js file.
You can keep it as a template: our display classes will always be built by this architecture.
We must also include this new file in our index.html page, adding the import along with the others, just below the footer:

	<script src="js/libs/easel.js"></script>
	<script src="js/main.js"></script>
	<script src="js/Icon.js"></script> 

We can test our new class by modifying our main.js file as follows:

(function(){
var stage;
var myIcon;
var myCanvas = $("#stageCanvas").get(0);

this.init = function() {
	
	stage = new Stage(myCanvas);
	myIcon = new Icon();
	stage.addChild(myIcon);
	stage.update();
    
        Ticker.setFPS(24);
        Ticker.addListener(this);
}
this.tick = function(){
       stage.tick();
}
})();

You should see “Icon initialized” message appearing once and “Ticked Icon” messages appearing multiple times in your browser’s console.
This indicates that the Icon object has been instantiated, and that it responds to the tick event.

Adding properties

So we have an Icon class, which is a copy of the Container class. We could use one or the other to do the same things. It would be transparent.
But that’s not what interests us: we want to add specifics properties and methods to our child class.

We will therefore create the parameter I told you about: a size in pixels, a path to an image, a color, and a text.
Here’s how:

(function (window) {
	// The class now receives parameters that are passed to the initalize() method (the constructor).
        function Icon(iconWidth,imgSrc,color,titleText)  {
        this.initialize(iconWidth,imgSrc,color,titleText);
    }
	
	Icon.prototype = new Container();
	Icon.prototype.Container_initialize = Icon.prototype.initialize;
	Icon.prototype.Container_tick = Icon.prototype._tick; 
    
    //The initialize method register the class variables with the pased parameters
    Icon.prototype.initialize = function (iconWidth,imgSrc,color,titleText) {
		this.Container_initialize();
		this._iconWidth = iconWidth;
		this._imgSrc = imgSrc; 
		this._color = color; 
		this._titleText = titleText;  
        console.log("Icon initialized : " + this._iconWidth+" - "+this._imgSrc+" - "+this._color+" - "+this._titleText);
	}
	
     Icon.prototype._tick = function () {
		this.Container_tick();
               console.log("Icon Ticked");
    }
    window.Icon= Icon;
} (window));

All we have to do now is to make use of these variables: we will create exactly the same icon that we created in the previous post, but we’ll add the elements in our Icon class instead of a Container .

(function (window) {
	function Icon(iconWidth,imgSrc,color,titleText)  {
        this.initialize(iconWidth,imgSrc,color,titleText);
    }
	
	Icon.prototype = new Container();
	Icon.prototype.Container_initialize = Icon.prototype.initialize;
	Icon.prototype.Container_tick = Icon.prototype._tick; 
    
    Icon.prototype.initialize = function (iconWidth,imgSrc,color,titleText) {
		this.Container_initialize();
		this._iconWidth = iconWidth;
		this._imgSrc = imgSrc;
		this._color = color;
		this._titleText = titleText;
        console.log("Icon initialized : " + this._iconWidth+" - "+this._imgSrc+" - "+this._color+" - "+this._titleText);

		var graphic = new Graphics();
		graphic .beginFill(this._color);
		graphic.drawRoundRect(0,0,this._iconWidth,this._iconWidth,this._iconWidth*.1);
		this.roundRectangle= new Shape(graphic);
		this.addChild(this.roundRectangle);
			
		this.text = new Text(this._titleText, "bold 16px Courier",this._color);
		this.text.textAlign ="center";
		this.text.y = this._iconWidth+15;
		this.text.x = (this._iconWidth*0.5);
		this.addChild(this.text);
			
		this.bitmap = new Bitmap(this._imgSrc);
		this.bitmap.x =  this._iconWidth*0.5;
		this.bitmap.y =  this._iconWidth*0.5;
		this.addChild(this.bitmap);
	}
	
     Icon.prototype._tick = function () {
		this.Container_tick();
               console.log("Icon Ticked");
    }
    window.Icon= Icon;
} (window));

We now have a handly Icon object reusable and customizable.
Here it is !

But it could be much better, don’t you think ? 
Let’s add some behaviors to it ! 

Adding behaviors

Our Icon class inherits all its characteristics from the Container class, which itself inherits from DisplayObject, and its events: onClick, OnDoubleClick, onMouseOut, onMouseOver, and onPress tick.

So we can add support for some of these events in our Icon class:

(function (window) {
	function Icon(iconWidth,imgSrc,color,titleText)  {
        this.initialize(iconWidth,imgSrc,color,titleText);
    }
	
	Icon.prototype = new Container();
	Icon.prototype.Container_initialize = Icon.prototype.initialize;
	Icon.prototype.Container_tick = Icon.prototype._tick; 
	
    Icon.prototype.initialize = function (iconWidth,imgSrc,color,titleText) {
		this.Container_initialize();
		this._iconWidth = iconWidth;
		this._imgSrc = imgSrc;
		this._color = color;
		this._titleText = titleText;
        console.log("Icon initialized : " + this._iconWidth+" - "+this._imgSrc+" - "+this._color+" - "+this._titleText);
	
		this.graphic = new Graphics();
		this.graphic.beginFill("#777");
		this.graphic.drawRoundRect(0,0,this._iconWidth,this._iconWidth,this._iconWidth*.1);
		this.roundRectangle= new Shape(this.graphic);
		this.addChild(this.roundRectangle);
			
		this.text = new Text(this._titleText, "bold 16px Courier",this._color);
		this.text.textAlign ="center";
		this.text.y = this._iconWidth+15;
		this.text.x = (this._iconWidth*0.5);
		this.addChild(this.text);
			
		this.bitmap = new Bitmap(this._imgSrc);
		console.log(this.bitmap.image.width+"-"+this._iconWidth);
		
		this.bitmap.regX = this._iconWidth*0.5;
		this.bitmap.regY = this._iconWidth*0.5;
		this.bitmap.x =  (this._iconWidth*0.5);
		this.bitmap.y =  (this._iconWidth*0.5);
		this.addChild(this.bitmap);
		
		this.roundRectangle.onClick = function(){
			console.log("click "+this.graphics);
			
		}
		this.roundRectangle.onMouseOver = function(){
			console.log("mouseOver ");
			this.parent.graphic.clear();
			this.parent.graphic.beginFill(this.parent._color);
			this.parent.graphic.beginStroke(Graphics.getRGB(255,255,255));
			this.parent.graphic.drawRoundRect(0,0,this.parent._iconWidth,this.parent._iconWidth,this.parent._iconWidth*.1);
			this.parent.roundRectangle.graphics = this.parent.graphic;
		}
		this.roundRectangle.onMouseOut = function(){
			console.log("mouseOut ");
			this.parent.graphic.clear();
			this.parent.graphic.beginFill("#777");
			this.parent.graphic.drawRoundRect(0,0,this.parent._iconWidth,this.parent._iconWidth,this.parent._iconWidth*.1);
			this.parent.roundRectangle.graphics = this.parent.graphic;
		}
	}
	
    Icon.prototype._tick = function () {
		this.Container_tick();
        //console.log("Icon Ticked");
    }
    window.Icon= Icon;
} (window));
Important: for onMouseOver and onMouseOut events being managed, it is necessary to activate their support at Stage level.
To do this, simply add stage.enableMouseOver (); in the main.js file:
1
(function(){
var stage;
var myIcon;
var myImage;
var myCanvas = $("#stageCanvas").get(0);

this.init = function() {
	
	stage = new Stage(myCanvas);
    myIcon = new Icon(60,"img/car.png","#fff","Title");
	myIcon.x = 400;
	myIcon.y = 300;
	
	stage.addChild(myIcon);
	stage.update();
    stage.enableMouseOver();
	
	Ticker.setFPS(24);
    Ticker.addListener(this);
}
this.tick = function(){
       stage.tick();
}
})();
 

Here’s the result.

What can I do from here ?

From there, just imagine.
This object-oriented concept will allow you to create many reusable objects. You can gather these new classes in packages, or improve our icon by giving it additional parameters: a link, a “circle” option, the possibility for larger images to automatically adapt their size?

You could change the ugly spaceship I use for my examples ? (Well, you really should do that…)

Now you can also properly schematize a UML component, or even an application that uses EaselJS (Although the library is still a little young for such an advanced use, in my opinion).

You can also extend from other classes like SpriteSheet, or create other objects that inherit from Container to make an animated menu, for example.
This is what we will do in the next post, and I’ll show you two examples I developed with EaseJS.

Social Widgets powered by AB-WebLog.com.