Canvas internal elements cannot add interactive event listeners as conveniently as DOM elements, because there is no concept of elements in canvas, they are just graphics drawn by canvas. This is a necessary obstacle for interactive development. The idea of monitoring the click event of a graphic is very simple. Just listen to the click event of the canvas element itself, and then determine which graphic the click coordinates are located inside, and then the graphic click event can be realized in disguise. This article will introduce three methods to determine whether the coordinate point is located inside a canvas graphic.
AgreementThe three methods introduced in this article are suitable for identifying graphic click events with irregular shapes and irregular positions in the canvas. For scenes with regular shapes or regular positions, there must be simpler implementations, which will not be discussed here.
pixel methodThe idea of the pixel detection method is to draw multiple graphics in the canvas (if there are multiple) off-screen separately, and use the getImageData() method to obtain the pixel data and save them. When the canvas element listens to a click event, the click coordinates can be used to directly calculate the pixel on the canvas where the click occurred, and then traverse the previously saved graphics data to see if the alpha value of this pixel is 0. If it is 0, it means The landing point is not within the current figure, otherwise it means that the point has reached this figure.
Method to get the clicked pixel number based on the click coordinates:
Pixel number = (ordinate-1) * canvas width + abscissa
For example, if you click coordinates (3,3) on a canvas with a width of 5, the pixel number obtained according to the above formula is (3-1) * 5 + 3 = 18, as shown in the figure:
Because the graphics data exported by canvas stores each pixel in an array of 4 numbers in rgba order, so if you want to access the alpha value of a specified pixel, you only need to read the pIndex * 4 + 3 value of this array. , if this value is not 0, it means that the pixel is visible, that is, the graphic is clicked.
This method is the one that I think has the most direct ideas and the most accurate results, and does not have any requirements on the shape of the graphics. However, this method has a fatal limitation. When graphics need to be moved on the canvas, data caches must be created frequently to ensure detection. The results are accurate. Affected by the canvas size and the number of graphics, the performance of the getImageData() method will become a serious bottleneck. So if the canvas graphic is static, this method is very suitable, otherwise it is not suitable to use this method.
Angle methodThe principle of the angle judgment method is easy to understand. If a point is inside a polygon, the angle between the point and all the vertices of the polygon should add up to exactly 360°.
The calculation process can be transformed into the following three steps:
1. Given the known polygon vertices and known coordinates, combine the coordinates and vertices in pairs to form a three-point queue.
2. To find the angle between three known points, you can use Yu Xuan’s theorem
3. Determine whether the sum of the included angles is 360°
Each step is simple and is implemented as follows:
//Calculate the distance between two points const getDistence = function (p1, p2) { return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))};//The angle method determines that the point is inside the polygon const checkPointInPolyline = (point, polylinePoints) => { let totalA = 0; const A = point; for (let i = 0; i < polylinePoints.length; i++) { let B, C; if (i === polylinePoints.length - 1) { B = { x: polylinePoints[i][0 ], y: polylinePoints[i][1] }; C = { x: polylinePoints[0][0], y: polylinePoints[0][1] }; } else { B = { x: polylinePoints[i][0], y: polylinePoints[i][1] }; C = { x: polylinePoints[i + 1][0], y: polylinePoints[i + 1][1] }; } //Calculate the angle const angleA = Math.acos((Math.pow(getDistence(A, C), 2) + Math.pow(getDistence(A, B), 2) - Math.pow(getDistence(B, C), 2)) / (2 * getDistence(A, C) * getDistence(A, B))) totalA += angleA } //Judge the sum of angles return totalA === 2 * Math.PI}One limitation of this method is that the shape must be a convex polygon. If the polygon is not a convex polygon, it needs to be cut into a convex polygon before calculation, which is more complicated.
A similar idea is the area method. If a point is inside a polygon, then the sum of the areas of the triangle formed by the point and all the vertices of the polygon should be equal to the area of the polygon. It is very troublesome to calculate the area of the polygon first, so this method You can pass it directly.
Ray methodThe ray method is a method that I can't explain clearly but is very easy to use. As long as the number of intersection points between a point and one side of the polygon is an odd number, the point is inside the polygon. Note that you only need to count the number of focus points on any side, such as the left side. This method does not limit the type of polygon, convex polygons, concave polygons or even rings are acceptable.
It’s also very simple to implement:
const checkPointInPolyline = (point, polylinePoints) => { //Ray method let leftSide = 0; const A = point; for (let i = 0; i < polylinePoints.length; i++) { let B, C; if (i = == polylinePoints.length - 1) { B = { x: polylinePoints[i][0], y: polylinePoints[i][1] }; C = { x: polylinePoints[0][0], y: polylinePoints[0][1] }; } else { B = { x: polylinePoints[i][0], y: polylinePoints[i][1] }; C = { x : polylinePoints[i + 1][0], y: polylinePoints[i + 1][1] }; } //Judge left intersection let sortByY = [By, Cy].sort((a,b) => ab) if (sortByY[0] < Ay && sortByY[1] > Ay){ if(Bx<Ax || Cx < Ax){ leftSide++ } } } return leftSide % 2 === 1}There is a special case of the ray method, which requires special treatment when the point is on an edge of multiple deformations. But in engineering, I think it doesn't need to be processed, because if the user happens to click on the boundary of the graphic, then the program thinks that he has not clicked and can pass.
SummarizeThe above three methods can all realize click detection of irregular graphics in canvas. Among them, the advantage of the pixel method is that it does not pick shapes and has certain performance advantages in static scenes; the angle method should be said to have only theoretical value and poor practicality; the most practical one in engineering is the ray method, which has few limitations and is easy to implement. Simple, most of the time you just need to know the ray method.