Flash-like image manipulation with CreateJS
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.