Full-fledged mouse events on Canvas graphic elements

Problem


Those involved in the development of graphics using JavaScript + Canvas have long noticed the problem of processing mouse events on any graphic elements.

There are several solutions to the problem:
  1. Do not process them at all, that is, your graphics are not interactive and you do not need it
  2. Compute a rectangle for each shape, store it in memory, and trigger events when the cursor enters these rectangles
  3. Approach each graphic element individually, using various mathematical formulas for rectangles, circles, lines, etc.


All these methods have the right to life in certain circumstances, but when it is necessary to detect events (we remove option 1), when the figures are often not rectangular, have turns, and other transformations (option 2 is also not suitable), when the figures are not geometrically correct, like for example, lines smoothed by splines, polygons with concave faces (option 3 is also forgotten), and most importantly, when these figures become countless, and storing the coordinates of each, sorting them over each MouseMove becomes overhead, another way comes to the rescue.

Solution approach


We need to know exactly how the pixel hit the cursor on our figure or not. To do this, we will do the following:
When creating a shape, assign it a unique numeric identifier, for example, 1. Now we will convert this identifier to a color in RGB HEX notation - #000001.

We will create a new canvas element for the layer on which the figure is located, but we will not add it to the DOM, this is our background canvas.
While drawing the figure on the main canvas, draw it on the background, but in the color that came from its identifier (including the border line).
Now it becomes clear that if the drawing order is respected, the shapes on the background canvas will overlap each other in the same order as on the real canvas and when the mouse cursor moves over the main canvas, you can find out the coordinates of the pixel color of the background canvas, which is the shape identifier, which allows us to clearly say that the cursor went to the figure or left it.

Underwater rocks


When the mouse moves, we determine the color of the pixel under it, and if it is opaque (Alpha = 255), determine the shape identifier and work with events.

But not everything is so rosy. There are 3 main problems:

1. Smoothing

The fact is that all browsers by default use anti-aliasing when rendering, which means that not all pixels in the figure will be completely opaque and the color that we need.
image

Solution:
It is necessary to draw shapes on the border of 2 pixels is thicker (or 2 pixels in the absence of the border). This will create additional noise in the surrounding pixels, but since for us they are not significant and also not visible to the user, this can be neglected.

2. The fusion of colors

When 2 shapes intersect, the same smoothing can create completely opaque pixels, the color of which will be transitional from the color of one shape to the color of another. At the same time, there is little chance that this color will indicate a completely different figure, which is in a completely different place. In a really working application, this probability is quite low, but do we want to foresee everything?
image

Solution:
To verify the authenticity of the identifier, you need to find out the color of 4 pixels - from above, from below, to the right and to the left. If each of them is the same color or not completely transparent, the identifier is reliable.

3. Images

In the case when we work with images, it is obvious that you need to create some kind of image processor that iteratively processes its pixel data obtained through getImageDataand replaces each opaque pixel with the color of the shape identifier, and each translucent replaces it with a transparent one.

In this case, there is no clearly posed problem, there are only 3 nuances that are worth paying attention to.

  • First, to process the image, we need to create another Canvas element that is the same size as the image to display the processing function and use it as the image source in the background canvas. You should either delete this element every time after use, or create one common one, and change its size for each new picture.
  • The second - image processing time is proportional to its size, since each pixel has to be bypassed. There is nothing to be said for sure, it all depends on the processing function.
  • The third - in the case of images received not from the domain where the page containing the Canvas element is located, browser protection will work. You have to handle the error SECURITY_ERR, and work with the image as a rectangle.


Implementation


So, we already have a background canvas with shapes drawn in the right colors. How to get the color of the pixel under the cursor? The following is an abstract code demonstrating how knowing the coordinates of the cursor to get a pixel under it:

//x, y - координаты мыши, полученые ранее
var ctx = bgCanvas.getContext("2d");
var pixel = ctx.getImageData(x, y, 1, 1).data; //массив из 4 элементов, каналы R, G, B и A, соответственно
var isOpaque = pixel[3] === 255; //Четвертый пиксель содержит альфа канал


Next, in any convenient way, you need to get a color from the channels that can be compared with the shape identifier.
The subtleties of implementing event mechanisms and storing and processing identifiers are not specifically described here, since there are countless options.

A source


The following resource was used as a source:
http://tschaub.net/blog/2011/03/31/canvas-hit-detection.html The

article does not claim to be a translation, but most of the materials from the original resource are used in it.

Also popular now: