📜 ⬆️ ⬇️

Full mouse events on Canvas graphics

Problem


Those who are engaged in the development of graphics using JavaScript + Canvas have long noticed the problem of handling mouse events on any elements of the graphics.

There are several solutions to the problem:
  1. Do not process them at all, that is, your graphics are non-interactive and you need nothing
  2. Calculate a rectangle for each shape, store it in memory, and trigger events when the cursor hits these rectangles
  3. Approach each element of the graphics individually, applying different mathematical formulas for rectangles, circles, lines, etc.


All these methods have the right to life in certain circumstances, but when events need to be discovered (we dismiss option 1), when the figures are often not rectangular, they have turns, and other transformations (option 2 also does not fit), when the figures are not geometrically correct, as for example, lines smoothed by splines, polygons with concave edges (option 3 was also forgotten), and most importantly, when these figures become innumerable, and to store the coordinates of each, sorting them out for each MouseMove becomes overhead, another method comes to the rescue.

Solution approach


We need to know exactly up to the pixel whether the cursor hit our shape or not. To do this, take the following:
When creating a shape, assign it a unique numeric identifier, for example, 1. Now we convert this identifier to the color in RGB HEX notation - #000001 .
')
Let's create a new canvas element for the layer on which the shape is located, but we will not add it to the DOM, this is our background canvas.
When drawing a shape on the main canvas, we will draw it on the background, but with the color resulting from its identifier (including the border lines).
Now it becomes clear that if the order of drawing is observed, the figures on the background canvas will overlap each other in the same order as on the real one, and when you move the mouse cursor 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 unequivocally say that the cursor entered a figure or left it.

Underwater rocks


When the mouse moves, we determine the color of the pixel below it, and if it is opaque (Alpha = 255) , we define the identifier of the shape 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 of the shape will be completely opaque and of the color that we need.
image

Decision:
It is necessary to draw the borders of the shape 2 pixels thicker (or just 2 pixels in the absence of this 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. Merge colors

When intersecting 2 shapes, the same smoothing can create completely opaque pixels, the color of which will be a transition from the color of one shape to the color of another. In this case, there is a small probability that this color will indicate a completely different shape, which is located in a completely different place. In a really working application, this probability is quite low, but we want to foresee everything?
image

Decision:
To verify the authenticity of the identifier you need to know the color of 4 pixels - top, bottom, right and left. If each of them is the same color or not completely transparent, the identifier is valid.

3. Images

In the case when we work with images, it is obvious that you need to create a certain image handler that iteratively processes its pixel data obtained via getImageData and replaces each opaque pixel with the color of the shape identifier, and each semi-transparent replaces it with a transparent one.

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



Implementation


So, we already have a background canvas with the shapes drawn by the necessary colors. How to get the color of the pixel under the cursor? Below is an abstract code that demonstrates how to get the pixel below it, knowing the coordinates of the cursor:

 //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; //     


Then, in any convenient way, you need to get a color from the channels, which can be compared with the figure ID.
It does not specifically describe the intricacies of the implementation of the mechanisms of events and the storage and processing of identifiers, since the options are countless.

A source


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

The article does not claim the right to be a translation, but it uses most of the materials of the original resource.

Source: https://habr.com/ru/post/195966/


All Articles