How to Detect Canvas Shape Clicks Using Pixel Color and Transparency
By leveraging pixel color and alpha values, this guide explains how to determine whether a user click falls inside a Canvas shape, compares it with traditional methods like crossing number and winding number, and provides a practical implementation using Create.js with sample code.
In the previous article we mentioned the crossing number and winding number methods for point‑in‑polygon tests. This article introduces another pixel‑color based detection scheme and provides source code to answer a question about Create.js.
Basic principle
When a user clicks on a Canvas, the color of the pixel at the click position can be used to decide whether the click hit a shape. For example, in the figure below the rectangle area is orange while the outside is transparent; if the color at point P is orange, we infer that P lies inside the rectangle.
In ideal cases this works, but real situations are more complex. If all rendered shapes share the same color, the method cannot distinguish them.
Most Canvas libraries (e.g., Fabric.js, Create.js) store shape attributes and state in memory, allowing them to be redrawn later. By redrawing the shape onto a fresh transparent Canvas, we can reuse the same pixel‑color detection: if the sampled pixel’s alpha is 0 the point missed the shape; otherwise it hit.
Implementation
Create.js uses the alpha‑comparison method to test whether a point lies on a shape. Below is a snippet of the relevant code.
/**
* Tests whether the display object intersects the specified point in local coordinates
* (i.e., draws a pixel with alpha > 0 at the specified position). This ignores the alpha,
* shadow, hitArea, mask, and compositeOperation of the display object.
*
* @method hitTest
* @param {Number} x The x position to check in the display object's local coordinates.
* @param {Number} y The y position to check in the display object's local coordinates.
* @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject
* intersect the specified local Point.
*/
p.hitTest = function(x, y) {
// DisplayObject._hitTestContext is a new Context
var ctx = DisplayObject._hitTestContext;
// Transform the point to (0,0)
ctx.setTransform(1, 0, 0, 1, -x, -y);
// Draw the object onto the new context
this.draw(ctx, !(this.bitmapCache && !(this.bitmapCache._cacheCanvas instanceof WebGLTexture)));
// Get the alpha value at (0,0); if greater than 1 the point is on the shape
var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, 2, 2);
return hit;
};Note that if a shape’s fill is fully transparent, this method fails; Create.js works around the issue by binding an opaque shape of the same size to the transparent one and using that for hit testing. The full source can be found at the linked GitHub file.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
