drawkcaB | Backward Compatible logo

rants and tips about software

How to apply grayscale filter to image using EaselJS (CreateJS)

Searching the Google for grayscale easeljs leads to some obscure StackOverflow Q&A from 2011. which has a working example. However, it uses a generic filter matrix so you would need to know how grayscale effect actually works on pixel level to understand what it does.

It's much easier to use Easel's built-in functions. However, those are not easy to discover using a search engine. You have to dig into docs. Here's an easy way to do it, adapted from Easel docs. Assuming you have a Bitmap, Container, Shape in variable myDisplayObject:

var matrix = new createjs.ColorMatrix().adjustSaturation(-100);
myDisplayObject.filters = [new createjs.ColorMatrixFilter(matrix)];
myDisplayObject.cache();
        

Make sure you call cache() at the end, because filters are only applied during caching. If you wish to use different filters for different objects in a container, you need to cache() each one separately before adding to container.

Now, you might run this example, and get the error message createjs.ColorMatrix() is not a constructor because createjs.ColorMatrix is undefined. The reason for this is that current version of minified files does not include filters, so you need to include ColorMatrixFilter.js script in your page separately. Lanny says it will be included in one of future versions. I'm not sure that's a good idea though. I doubt many users use filters. I almost built the entire game without it, and only want to include it for Medusa's petrifying effects.

Feedback & Share Milan Babuškov, 2013-06-18

Callback when all images in HTML page are loaded, with custom timeout

I'm developing a HTML5 game and although there are many ways to track image loading, they mostly use XHR which does not work reliably in different browsers. I don't care about progress bars, but I do compose images after loading (using EaselJS cache) and need to make sure images are loaded before caching.

The usage is really simple. In case some of the images fail to load, or takes too long, you could have a problem that program would not go on, and user won't see anything. To avoid this, I added a custom timeout, after which the callback would be called regardless. The timeout resets after each successfull download, so don't set it too high. The example below uses 12 seconds:

// 1. create image loader
var imageLoader = new ImageLoader(12);

// 2. feed it some URLs
imageLoader.add('shadow', 'http://mycnd.com/shadow.jpg');
imageLoader.add('ball',   'http://mycnd.com/ball.png');
imageLoader.add('player', 'http://mycnd.com/player.png');

// 3. wait for load to complete and then do something with the images
imageLoader.loadAll(function() {

    // do something, like for example:
    var ballSprite = new createjs.Bitmap(
        imageLoader.get('ball'));

});

The code uses alert() in two places. Please replace that with whatever error handling you use. Also, there could be a better/faster way to detect image files that are not available (HTTP code 404 and similar), so that we don't have to wait for timeout.

View and download the source code at https://github.com/mbabuskov/ImageLoader.

Feedback & Share Milan Babuškov, 2013-06-10

Creating multiple sprites from the same image using EaselJS

In the HTML5 game I'm making, I needed to have many identical sprites. At first, I used the generic new Bitmap('path.png') code, but it uses a lot of memory that way. I searched the web and finally asked at CreateJS forums. The answer is simple and easy:

var image = new Image();   // create HTML5 image object
image.src = 'url.png';     // load image from file

// now, just repeat the following for each sprite
var sprite = new Bitmap(image);

BTW, I did search for image class in EaselJS docs, but apparently it is not listed as it is a regular HTML5 type of object. I guess you should still learn HTML5 basics even if you use a wrapper library.

Feedback & Share Milan Babuškov, 2013-05-29

How to load data from QHI.DAT file

If you have an old Quicken Home Inventory or Quicken Home Inventory Manager installation and want to save your inventory database, you probably have problems using this data on a new computer system. This is because of various incompatibilities between multiple versions of Intuit inventory programs.

However, there's a simple way out. Download a program called Attic Manager. It's a home inventory program, just like QHIM, with one specific feature: it is able to import databases of all Quicken Home Inventory programs. At the time I write this, it supports the oldest IDB files, then the newer .QHI files and also the latest MDF files which come with most recent versions of Quicken.

Now, how does this help you, when you have a DAT file? Well, QHI.DAT is not really a database with your items. If you have QHI.DAT file, this means you have the oldest version of QHI, and there should be a file called QHI.IDB around as well. Attic Manager is able to load all items, locations, categories and other data from QHI.IDB file, so use that one.

As far as I know, Attic Manager is the only product on the market that is able to do this. Once you load the database into Attic Manager, you can export it into CSV format and then load into any Inventory program that supports loading from CSV or Excel (most of them do). Or, you can simply use Attic Manager itself. It is simple, clean, and fast. It works on newer versions of Windows, like Windows 7 and Windows 8 and also on 64bit systems as well. And knowing your data can be exported at any time sets you free from vendor lock-in.

Feedback & Share Milan Babuškov, 2013-05-15

Easel.js docs need improvement

A few days ago, Sebastian DeRossi asked me on Twitter how to improve Easel.js docs. As this is too large for Twitter's 140 characters, here's a short blog post of some issues I found:

1. I was looking for a way to Flip an image and docs don't mention that you can use negative values to scaleX and scaleY. I was really planning to work around this by creating all the required mirror images using ImageMagic and load 2 sets of sprites, when I accidentally found the example using negative values on some blog while searching for something completely different.

2. Say you are a complete beginner like me, and you wish to add a mouse click event handler to Bitmap. You would go into docs, click Bitmap, go to list of events, where it says Click and there are links to DisplayObject and MouseEvent there, but none of those lead to example how to actually use it. Failing this, I first found onClick only to find out that it is deprecated and I should use addEventListener(), without any example how to use it. BTW, I did manage to get onClick to work, but I did not want to use a deprecated function. In the end, I asked on StackOverflow and got a real example how to use addEventListener for mouse events.

3. The thing I'm still confused about, is what is the standard application structure. I.e. how to do the main game loop? In docs, the Getting Started section ends with this:

//Update stage will render next frame
createjs.Ticker.addEventListener("tick", handleTick);

function handleTick() {
    //Circle will move 10 units to the right.
    circle.x += 10;

    //Will cause the circle to wrap back
    if (circle.x > stage.canvas.width) { circle.x = 0; }
    stage.update();
}

Am I supposed to update all my logic in handleTick()? I would create my own functions of course, and call it from there. Should the structure of my program look like this:

createjs.Ticker.addEventListener("tick", handleTick);

function handleTick() {
    updateWorldLogic();
    stage.update();
}

Somewhere else, I found an example like this:

var canvas = document.getElementById("canvas_id");

startGame();
function startGame() {
    stage = new createjs.Stage(canvas);

    // NOTE the following comment, I have NO idea what it means???
    // We want to do some work before we update the canvas,
    // otherwise we could use Ticker.addListener(stage);

    createjs.Ticker.addListener(window);
    createjs.Ticker.useRAF = true;
    createjs.Ticker.setFPS(60);
}

function tick()
{
    // update the stage:
    stage.update();
}

This code works, but I don't understand the difference between:

  • createjs.Ticker.addListener(window);
  • createjs.Ticker.addListener(stage);
  • createjs.Ticker.addEventListener("tick", handleTick);

...and I'm having a hard time getting this clear from the docs.

Feedback & Share Milan Babuškov, 2013-05-13

How to flip an image horizontaly or vertically using easel.js

Looking at Easel.js docs, you might think that Flip() function is missing. However, flipping is done using scale with negative values. To flip image horizontally, use:

image.scaleX = -1;

To flip vertically, use:

image.scaleY = -1;

Before flipping, make sure you set the regX and/or regY to the center of image. Full example with image sized 120x50:

var myimg = new createjs.Bitmap("sword.png");
myimg.regX = 60;
myimg.regY = 25;
myimg.scaleX = -1;  // flip horizontally
myimg.scaleY = -1;  // flip vertically
Feedback & Share Milan Babuškov, 2013-05-08

Creating a mouse hover effect for button/image with HTML5 Canvas and easel.js

After ditching many other HTML5 Canvas libs, I was left with Easel.js. Documentation is sparse, without many examples. I had to google a lot to find this information, so I'm getting it up here hoping it might help someone else as well.

If you need a simple graphic (or text) button with hover support, then Easel's ButtonHelper class is what you need. You can create a simple image containing 3 buttons states (normal, hover, pressed) and set up ButtonHelper to do all the work.

Here's how I did it. First create an image with all 3 states. I used this PNG:

As you can see my image is 300x45 with each state being 100x45 pixels. Now the code:

// setup
stage.enableMouseOver();
var data = {
     images: ["3buttons.png"],
     frames: {width:100, height:45},
     animations: {normal:[0], hover:[1], clicked:[2]}
};
var spriteSheet = new createjs.SpriteSheet(data);
var button = new createjs.BitmapAnimation(spriteSheet);
var helper = new createjs.ButtonHelper(button, "normal", "hover", "clicked");

// set the button coordinates and display it on the screen
button.x = 100;
button.y = 100;
button.gotoAndStop("normal");
        

Yes, that's all. If you're looking for example with Text, take a look at this jsFiddle.

Note that each of the button states can be animated, just add more frames to the image file and configure the data.animations properly.

Feedback & Share Milan Babuškov, 2013-05-08

Selecting a HTML5 Canvas library for a turn-based strategy game

In the past couple of days I had determined to select a HTML5 Canvas library to use for my next game project. Some of the features I require:

  • Scaling and rotating support with Tweening
  • Availability or ready-made resource (images, audio) loader or able to easily make your own
  • Ability to click on a random image or text element (sprite) and handle the event easily, like jQuery 'click' handler
  • Ability to easily make hover effect over images/text
  • Some other stuff like Flip is desired by not absolutely required

After investigating a lot of frameworks, I narrowed the list down to: Crafty, MelonJS, Quintus, LimeJS, CanvasEngine, Cocos2d-hmtl5, CreateJS/EaselJS. Crafty does not have rotating support, MelonJS and Cocos2d require that you manually, traverse all the child nodes, find which ones are visible and hittest the mouse coordinates to get the hover effect. I could not find this information of Lime.js, but inability to preload audio turned me off. Quintus apparently does not support hover at all. So, I was left with CanvasEngine and EaselJS. RPG.js is moving to CanvasEngine, so I thought there must be some reason for that and tried CE first. However, elements.events.mouseover is buggy - the event fires only when mouse stops moving. So, I was left with EaselJS, and managed to get it to work, even easier than I thought by using ButtonHelper class. More in my next post...

Feedback & Share Milan Babuškov, 2013-05-08

Capturing mouse movement with Cocos2d-html5 and replacing default cursor

I decided to try to use Cocos2d instead of jQuery and DOM for my next browser game. I find Cocos2d documentation confusing, and googling around you are more likely to get Cocos2d-iPhone documentation that simply does not apply for some stuff.

I spent a couple of hours trying to understand how to handle mouse or if it is even possible. I found examples using Cocos2d-javascript that worked fine, but using the same code with Cocos2d-html5 did not. At one point I was close to conclude that mouse is not supported as everything tries to emulate touch. However, this is not the case, mouse handling works fine.

Currently (Cocos2d-html5 version 2.1.3), the best documentation is to read the file CCMouseDispatcher.js in cocos2d/touch_dispatcher directory. In your code, in layer object you can use onMouseMoved and other methods found in this file. You might need to figure out the parameters yourself. For example, onMouseMoved returns an event object which has getLocation() function, which returns another object with x and y properties. So, the code to draw a custom cursor would be something like creating the sprite and then updating its position like this:

onMouseMoved:function (event) {
    cursorSprite.setPosition(cc.p(
        event.getLocation().x,
        event.getLocation().y)
    );
}

Now, this would give you two cursors. I tried setting the cursor for the canvas element to none via CSS, but it did not work. Another workaround would be to set a transparent 1x1 pixel cursor using CSS like:

canvas { cursor: url('transparent-image.png') }

I'm yet to try if this works, but somehow I feel it won't. This is all using Firefox 16 on Linux.

Feedback & Share Milan Babuškov, 2013-05-07

Past posts

Copyright © Milan Babuškov 2006-2013