View Full Version : What do you think is the best way to do this with FF & <canvas>?

08-02-2008, 01:11 AM
First off, this is for firefox only. IE will be coded separately since it cannot use the <canvas> tag.

Second, I would like to do with <canvas> instead of having 24 swappable images & area maps for each angle of rotation for each different base image. There are 7 base images, so that would be 168 (7x24) images and 168 area maps.

I want the user to be able to create objects from an established set of images, and then be able to rotate those objects by 15 degree increments and drag them around on the page by the nontransparent portions of the image (which is known by a positive result from a function which will henceforth be called a pixel query). I've got this working with solution #1, but I am thinking that maybe solution #3 is best.

Solution #1: New Canvas For Each Object

The following code runs the pixel query on the mouse's target, at pixel (xgetpixel, ygetpixel) of the mouse target's image.

var currentmoverctx = moveme.getContext("2d");
var imgd = currentmoverctx.getImageData(0, 0, moveme.width, moveme.height);
var pos = (ygetpix * moveme.width + xgetpix)*4;
var mypixelalpha = imgd.data[pos+3];

if (mypixelalpha!=0) {
// drag the object

The flaw of this system is that if there is one object on top of another, and the user clicks on a spot that is transparent on the top object's image but opaque on the bottom image, it will not drag the bottom image like it's supposed to.

If this is the case, then my only option is to start searching for other objects where the mouse lies within the bounding box (object.left, object.top, object.left + object.width, object.top + object.height) and then run a pixel query on those in order to start the drag operation if the user clicked on an image.

The problem with doing that is that it takes some time to run the pixel query. It's a bit more laggy than I'd like it to be. Each time the line of code in bold is run, it takes .3-.4 of a second to process. Obviously, if I have to run it 2 or 3 times, that is going to be rather unresponsive. If you know of a faster way to get the single pixel's data, please let me know!

It's laggy, but it works, and it's not that difficult.

Solution #2: Unique Canvas For Each Object + ImageData Caching
Same as solution #1, except that I will cache the ImageData from each object... which come out to a maximum of 30 objects with 300x300 or smaller images.

That way, the pixel query can work from the cache as opposed to retrieving the image data from the <canvas> tag, which I'm assuming would be faster.

Solution #3: Single Canvas Drawing All Objects
This one is self-explanatory, but I'm not sure what is the best way to detect which object was clicked on.

The best way I've come up with for doing this is having a second, hidden canvas, where all of the objects are drawn in different colors (e.g. Object1 is in RGB(1,1,1), Object2 is in RGB(2,2,2), etc.) I can then use the RGB value to tell which object the user is attempting to drag.

The problem here is that a canvas has to be redrawn by first erasing either the entire thing or a localized area around the object being moved... and that means a lot of redraws of a large canvas if it is to attempt to move in real-time. The best way I can think of for solving this problem is to redraw the canvas without the object that is being moved by the user, and draw that object instead on a floating dummy canvas, which the user will be dragging. When the user lets go of the mouse button, the main canvas will be redrawn with all of the objects, including the moved object in its new location. The dummy canvas will then be moved offscreen.

So... what do you think is the best way to do this?

08-02-2008, 02:04 AM

I tested:

for (iii=0;iii<=10;iii++) {
imgd = currentmoverctx.getImageData(0, 0, mousetarget.width, mousetarget.height);
pos = (ygetpix * mousetarget.width + xgetpix)*4;
mypixelalpha = imgd.data[pos+3];


for (iii=0;iii<=50000;iii++) {
pos = (ygetpix * mousetarget.width + xgetpix)*4;
mypixelalpha = imgd.data[pos+3];

and found that the code without the call to getImageData(...) was over 50,000 times faster, so hopefully I can salvage my code by caching the image data.

I'll have to see how much memory that will take up, but does that sound like a decent solution as long as it doesn't take up a ton of memory?