Question

In a canvas I created a 2d context. In that context... with a function... I'm able to create some 'circle objects'. Now, what I want, is to get the ImageData of a single circle object instead of the image data of the whole context.

In the code below, you can see my wish commented out.

var c = document.getElementById('canvas');
var ctx = c.getContext('2d');

var circle = function (X,Y) {
    var that = this;
    that.X = X;
    that.Y = Y;
    that.clicked = function(e) {
        //
        //
        //!!!!!!!!!!!!!!
        // Code below works fine, on context level
        imgData = ctx.getImageData(e.pageX, e.pageY, 1, 1);
        //
        // Code below is at the level of the circle, that's what I want, but isn't working
        imgData = that.getImageData(e.pageX, e.pageY, 1, 1);
        //!!!!!!!!!!!!!!
        //
        //
        alert(imgData.data[3]);
    }
    that.draw = function () {
        ctx.save();
        ctx.translate(that.X, that.Y);
        ctx.fillStyle = '#33cc33';
        ctx.beginPath();
        ctx.arc(0, 0, 50, 0, 2 * Math.PI);
        ctx.fill();
        ctx.stroke();
        ctx.restore();
    }
}
var circles = new Array();
circles.push(new circle(50,50));
document.addEventListener('click',function() {
    circles.forEach(function(circ,index){
        circ.clicked();
    });
})

So, how do I get the image data on specific objects?

edit: I understand that I need to draw the circle first, I do that later in my code, but what if I've got a background rect in the context, when I click next to the circle, it will get the imageData of the background rect, when I want to return the 0 value of the alpha rgba.

Was it helpful?

Solution

To this you need to log all your drawings as a "shadow canvas". The most common way is to create shape objects and store them in for example an array:

  • Draw the shape on canvas
  • Log its type, position, dimension, colors and orientation and store as an object and push that object to the array

When you need to get an isolated shape or object as an image:

  • Get mouse position (if you want to click on the object to select it)
  • Iterate the array of objects to see which object is "hit"
  • Create a temporary canvas of the dimension of that shape
  • Draw in the shape into the temporary canvas
  • Extract the data as an image (ctx.getImageData(x, y, w, h) or canvas.toDataURL())

When you need to resize your canvas you simply iterate all the objects and redraw them. You can even serialize your data for storage using this method.

An example of an object can be:

function Rectangle(x, y, w, h, fill, stroke) {
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;
    this.fill = fill;
    this.stroke = stroke;
}

You can extend this object to render it self to canvas as well as giving you a bitmap of itself isolated from the other shapes. Add this to the above code:

Rectangle.prototype.render = function(ctx) {
    if (this.fill) {                  /// fill only if fill is defined
        ctx.fillStyle = this.fill;
        ctx.fillRect(this.x, this.y, this.width, this.height);
    }
    if (this.stroke) {                /// stroke only if stroke is defined
        ctx.strokeStyle = this.stroke;
        ctx.strokeRect(this.x, this.y, this.width, this.height);
    }
}

Rectangle.prototype.toBitmap = function() {

    var tcanvas = document.createElement('canvas'),  /// create temp canvas
        tctx = tcanvas.getContext('2d');             /// temp context

    tcanvas.width = this.width;         /// set width = shape width
    tcanvas.height = this.height;
    tctx.translate(-this.x, -this.y);   /// make sure shape is drawn at origin

    this.render(tcxt);                  /// render itself to temp context

    return tcanvas.toDataURL();         /// return image (or use getImageData)
}

You simply draw your shapes, create the object based on the positions etc:

var rect = new Rectangle(x, y, w, h, fillColor, strokeColor);

myShapeArray.push(rect);

When you need to render the shapes:

for(var i = 0, shape; shape = myShapeArray[i++];)
    shape.render(ctx);

And when you need to get its bitmap (you retrieved its index in advance with the mouse click):

var image = myShapeArray[index].toBitmap();

And of course: you can make similar objects for circles, lines etc.

Hope this helps!

OTHER TIPS

Remember that Canvas is a bitmap graphics tool. Anything you draw into a single context becomes part and parcel of the same object. You can't get separate image data for each "object" you used to draw on that canvas... it's painted ... flattened ... into those pixel positions for that bitmap as soon as you hit draw().

The only way you could do something like what you are looking for would be to create separate canvas contexts that you overlay on top of each other. This would be better handled by utilizing a library such as KineticJS (http://www.html5canvastutorials.com/kineticjs/html5-canvas-events-tutorials-introduction-with-kineticjs/). The only other option would be to use an object oriented drawing tool such as SVG, (through Raphael.js, for example: http://raphaeljs.com) which does preserve separate objects in the the graphics space.

For reference about getImageData, see http://www.html5canvastutorials.com/advanced/html5-canvas-get-image-data-tutorial/

You can use trigonometry instead of trying to locate your colors with getImageData.

For example, if you have a circle defined like this:

var centerX=150;
var centerY=150;
var radius=20;
var circleColor="red";

Then you can test if any x,y is inside that circle like this:

// returns true if x,y is inside the red circle
isXYinCircle(140,140,centerX,centerY,radius);

function isXYinCircle(x,y,cx,cy,r){
    var dx=x-cx;
    var dy=y-cy;
    return(dx*dx+dy*dy<=r*r);
}

If the x,y is inside that red circle then you know the color at x,y is "red"

If you have multiple overlapping circles you can test each circle in increasing z-index order. The last circle that reports x,y inside will be the color at x,y.

It is because that is not a CanvasGraphicsContext. Try:

that.draw();
imgData = ctx.getImageData(e.pageX, e.pageY, 1, 1);

At first, I create my 2 canvas elements. 1 to display, 1 to calculate the pixeldata.

var c = document.getElementById('canvas');
var c2 = document.getElementById('canvas2');
var ctx = c.getContext('2d');
var ctx2 = c2.getContext('2d');

var width = window.innerWidth,
    height = window.innerHeight;

c.width = ctx.width = c2.width = ctx2.width = width;
c.height = ctx.height = c2.height = ctx2.height = height;

Than I make my function to create an image

function Afbeelding(src, X, Y, W, H) {
    var that = this;
    that.X = X;
    that.Y = Y;
    that.W = W;
    that.H = H;
    that.onClick = function () { };
    that.image = new Image(that.W, that.H);
    that.image.src = src;
    that.draw = function (context) { 
        context = (typeof context != 'undefined') ? context : ctx;
        context.save();
        context.translate(that.X, that.Y);
        context.drawImage(that.image, 0, 0, that.W, that.H);
        context.restore();
    }

When a document.click event is fired, the next function (inside the Afbeelding function) will be called:

that.clicked = function (e) {
    if ((e.pageX > that.X - (that.W / 2) && e.pageX < that.X + (that.W / 2)) && (e.pageY > that.Y - (that.H / 2) && e.pageY < that.Y + (that.H / 2))) {
        if (that.isNotTransparent(e)) {
            that.onClick();
        }
    }
}

This function (also inside the Afbeelding function) is used to check the pixel for transparancy.

    that.isNotTransparent = function (e) {
        var result = false;
        ctx2.clearRect(0, 0, width, height);
        that.draw(ctx2);
        var imgData = ctx2.getImageData(e.pageX, e.pageY, 1, 1);
        ctx2.clearRect(0, 0, width, height);
        if (imgData.data[3] > 0) {
            result = true;
        }
        return result;
    }
}

And all below is to lauch the things up above.

var items = new Array();

var afb = new Afbeelding();
afb.draw();
afb.onClick = function () {
    alert('clicked');
}
items.push(afb);


document.addEventListener('mousedown', function (e) {
    items.forEach(function (item, index) {
        item.clicked(e);
    });
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top