Talking about Javascript and Users contributions.
This is an iteresting post made by Roger Engelbert, the author of step by step creation of a 3D helicopter game using Away3D and one game, many frameworks.
Today he’s going to show you how to achieve Flash-like image manipulations with EaselJS, the same framework I used to make a javascript port of Circle Chain game.
« I‘ve decided to take a very simple application done in Flash and transform it in JavaScript for Canvas, using an open source framework, trying to use as many features typically associated with Flash as I could cram in a tiny bit of code, and see how the framework handled them (mainly: events, bitmap data manipulation, the display list and masks).
The application is the core logic inside an old project, FlashNose3D, which I used years ago to talk about PureMVC. Only here, I’m just using the animation bit.
The framework I picked was EaselJS, which is based on ActionScript.
Here is the test application in Flash, using circles:
And here are the classes:
Main.as
package { import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; public class Main extends Sprite { private var _slices:Array; function Main () { _slices = []; var startWidth:int = 400; var numSlices:int = 40; var fraction:int = startWidth/(numSlices - 1); var s:Slice; for (var i:int = 0; i < numSlices; i++) { s = new Slice ((startWidth - i * fraction) * 0.5, i); s.x = stage.stageWidth >> 1; s.y = stage.stageHeight >> 1; this.addChild(s); _slices.push(s); } addEventListener(Event.ENTER_FRAME, on_Loop); } private function on_Loop (event:Event):void { var len:int = _slices.length; var s:Slice; var previous:Slice; for (var i:int = len-1; i >= 0; i--) { s = _slices[i]; if (i == len-1) { s.update(new Point(mouseX, mouseY)); } else { previous = _slices[i + 1]; s.update(new Point(previous.x, previous.y)); } } } } }
Slice.as
package { import flash.display.Sprite; import flash.geom.Point; public class Slice extends Sprite { private var _vx:Number = 0; private var _vy:Number = 0; private var _friction:Number = 0.2; private var _spring:Number = 2; function Slice (radius:int, index:int) { this.graphics.beginFill(index * 50); this.graphics.drawCircle(0, 0, radius); this.graphics.endFill(); } public function update (target:Point) { var diffx:Number; var diffy:Number; var ax:Number; var ay:Number; diffx = target.x - x; diffy = target.y - y; ax = diffx * _spring; ay = diffy * _spring; _vx += ax; _vy += ay _vx *= _friction; _vy *= _friction; this.x += _vx; this.y += _vy; } } }
Then the EaselJS version. Click anywhere on the image to start the animation (although the effect looks better if you click on: nose, or mouth, or eyes).
And the code:
Main.js
//declare our globals var NUM_SLICES = 50; var FRICTION = 0.2; var SPRING = 2; (function(){ this.animate; this.slices; this.stage; this.image; this.init = function() { this.animate = false; this.slices = []; this.image = new Image(); //handler to preload our image this.image.onload = handleImageLoad; this.image.src = "doctor3.jpg"; } this.handleImageLoad = function () { var canvas = document.getElementById("testCanvas"); stage = new createjs.Stage(canvas); var ratio = image.width/(NUM_SLICES + 1); var s; for (var i = 0; i < NUM_SLICES; i++) { if (i == 0) { s = new Slice ( image, -1 ); } else { s = new Slice ( image, (image.width - i * ratio) * 0.5 ); } s.x = canvas.width * 0.5; s.y = canvas.height * 0.5; stage.addChild(s); slices.push(s); } //add stage event: Mouse Click stage.onClick = function (event) { if (animate) return; var p = slices[0].globalToLocal(event.stageX, event.stageY); var len = slices.length; for (var i = 0; i < len; i++) { slices[i].setNose(p.x, p.y); } animate = true; } //render stage stage.update(); //register main loop event createjs.Ticker.setFPS(30); createjs.Ticker.addListener(window); } //the main loop this.tick = function () { if (animate) { var len = slices.length; var s; var previous; for (var i = len-1; i >= 0; i--) { s = slices[i]; if (i == len-1) { s.update(stage.mouseX, stage.mouseY); } else { previous = slices[i + 1]; s.update(previous.x, previous.y); } } } //render stage stage.update(); } })(); window.onload = init;
Slice.js
(function(window) { this.circleMask; this.vx; this.vy; function Slice(image, radius) { this.initialize(); var canvas = document.getElementById("testCanvas"); this.image = image; this.regX = image.width * 0.5; this.regY = image.height * 0.5; vx = 0; vy = 0; if (radius > 0) { this.circleMask = new createjs.Shape(); this.circleMask.graphics.beginFill("0xFFF"); this.circleMask.graphics.drawCircle(0,0,radius); this.circleMask.graphics.endFill(); } } Slice.prototype = new createjs.Bitmap(); Slice.prototype.circleMask; Slice.prototype.vx; Slice.prototype.vy; Slice.prototype.Bitmap_initialize = Slice.prototype.initialize; Slice.prototype.initialize = function() { this.Bitmap_initialize(); } Slice.prototype.setNose = function (noseX, noseY) { this.regX = noseX; this.regY = noseY; if (this.circleMask) { this.mask = this.circleMask; } } Slice.prototype.update = function (targetX, targetY) { var diffx, diffy, ax, ay; diffx = targetX - this.x; diffy = targetY - this.y; ax = diffx * SPRING; ay = diffy * SPRING; vx += ax; vy += ay vx *= FRICTION; vy *= FRICTION; this.x += vx; this.y += vy; if (this.circleMask) { this.circleMask.x = this.x; this.circleMask.y = this.y; } } Slice.prototype.normalize = function (p) { var length = Math.sqrt(p.x*p.x+p.y*p.y); return new createjs.Point((p.x/length), (p.y/length)); } window.Slice = Slice; }(window));
Of course, the JS version uses an actual image, so the classes are a bit more extensive.
But you can see, among other things:
– how to write classes with EaselJS
– how you can extend a display object in EaselJS. In the code I extended the Bitmap class.
– how to draw vector shapes. I used a circle for the mask.
– how to mask a bitmap with a vector shape.
– how to add the main loop
– how to add mouse events on stage. The same logic would work for any display object.
– how to change the registration point of a display object with its properties regX and regY.
»
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.