Los elementos internos del lienzo no pueden agregar detectores de eventos interactivos tan convenientemente como los elementos DOM, porque no existe el concepto de elementos en el lienzo, son solo gráficos dibujados por el lienzo. Este es un obstáculo necesario para el desarrollo interactivo. La idea de monitorear el evento de clic de un gráfico es muy simple: simplemente escuche el evento de clic del elemento del lienzo y luego determine en qué gráfico se encuentran las coordenadas de clic. entonces el evento de clic gráfico se puede realizar disfrazado. Este artículo presentará tres métodos para determinar si el punto de coordenadas está ubicado dentro de un gráfico de lienzo.
AcuerdoLos tres métodos presentados en este artículo son adecuados para identificar eventos de clics gráficos con formas irregulares y posiciones irregulares en el lienzo. Para escenas con formas regulares o posiciones regulares, debe haber implementaciones más simples, que no se discutirán aquí.
método de píxelesLa idea del método de detección de píxeles es dibujar varios gráficos en el lienzo (si hay varios) fuera de la pantalla por separado y utilizar el método getImageData () para obtener los datos de píxeles y guardarlos. Cuando el elemento del lienzo escucha un evento de clic, las coordenadas del clic se pueden usar para calcular directamente el píxel en el lienzo donde ocurrió el clic y luego recorrer los datos gráficos previamente guardados para ver si el valor alfa de este píxel es 0. Si es 0, significa que el punto de aterrizaje no está dentro de la cifra actual; de lo contrario, significa que el punto ha alcanzado esta cifra.
Método para obtener el número de píxel en el que se hizo clic según las coordenadas del clic:
Número de píxeles = (ordenada-1) * ancho del lienzo + abscisa
Por ejemplo, si hace clic en las coordenadas (3,3) en un lienzo con un ancho de 5, el número de píxeles obtenido según la fórmula anterior es (3-1) * 5 + 3 = 18, como se muestra en la figura:
Debido a que los datos gráficos exportados por lienzo almacenan cada píxel en una matriz de 4 números en orden rgba, si desea acceder al valor alfa de un píxel específico, solo necesita leer el valor pIndex * 4 + 3 de esta matriz. , si este valor no es 0, significa que el píxel es visible, es decir, se hace clic en el gráfico.
Este método es el que creo que tiene las ideas más directas y los resultados más precisos, y no tiene ningún requisito sobre la forma de los gráficos. Sin embargo, este método tiene una limitación fatal cuando es necesario mover los gráficos en el lienzo. , los cachés de datos deben crearse con frecuencia para garantizar la detección. El rendimiento del método getImageData() se verá afectado por el tamaño del lienzo y la cantidad de gráficos. Entonces, si el gráfico del lienzo es estático, este método es muy adecuado; de lo contrario, no es adecuado utilizar este método.
Método del ánguloEl principio del método de cálculo de ángulos es fácil de entender. Si un punto está dentro de un polígono, el ángulo entre el punto y todos los vértices del polígono debe sumar exactamente 360°.
El proceso de cálculo se puede transformar en los siguientes tres pasos:
1. Dados los vértices del polígono conocidos y las coordenadas conocidas, combine las coordenadas y los vértices en pares para formar una cola de tres puntos.
2. Para encontrar el ángulo entre tres puntos conocidos, puedes usar el teorema de Yu Xuan.
3. Determina si la suma de los ángulos incluidos es 360°.
Cada paso es simple y se implementa de la siguiente manera:
// Calcula la distancia entre dos puntos const getDistence = function (p1, p2) { return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2. y) * (p1.y - p2.y))};//El método del ángulo determina que el punto está dentro del polígono const checkPointInPolyline = (point, polylinePoints) => { let totalA = 0; const A = punto; for (let i = 0; i < polylinePoints.length; i++) { let B, C; if (i === polylinePoints.length - 1) { B = { x: polilinePoints[i][0 ], y: polilíneaPuntos[i][1] }; C = { x: polilíneaPuntos[0][0], y: polilíneaPuntos[0][1] } else { B = { x: polilíneaPuntos[i][0], y: polilíneaPuntos[i][1] }; C = { x: polilíneaPuntos[i + 1][0], y: polilíneaPuntos[i + 1][1] }; } //Calcular el ángulo constante ánguloA = 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 } //Juzga la suma de los ángulos devuelven totalA === 2 * Math.PI}Una limitación de este método es que la forma debe ser un polígono convexo. Si el polígono no es un polígono convexo, es necesario cortarlo en un polígono convexo antes del cálculo, lo cual es más complicado.
Una idea similar es el método del área. Si un punto está dentro de un polígono, entonces la suma de las áreas del triángulo formado por el punto y todos los vértices del polígono debe ser igual al área del polígono. Es muy problemático calcular primero el área del polígono, por lo que este método Puedes pasarlo directamente.
método de rayosEl método del rayo es un método que no puedo explicar claramente pero que es muy fácil de usar. Siempre que el número de puntos de intersección entre un punto y un lado del polígono sea impar, el punto está dentro del polígono. Tenga en cuenta que solo necesita contar el número de puntos de enfoque en cualquier lado, como el lado izquierdo. Este método no limita el tipo de polígono, son aceptables polígonos convexos, cóncavos o incluso anillos.
También es muy sencillo de implementar:
const checkPointInPolyline = (punto, polilíneaPuntos) => { //Método de rayo let leftSide = 0; const A = punto for (let i = 0; i <polylinePoints.length; i++) { let B, C; == polilíneaPuntos.longitud - 1) { B = { x: polilíneaPuntos[i][0], y: polilíneaPuntos[i][1] }; polilíneaPuntos[0][0], y: polilíneaPuntos[0][1] }; else { B = { x: polilíneaPuntos[i][0], y: polilíneaPuntos[i][1] }; : polylinePoints[i + 1][0], y: polylinePoints[i + 1][1] } } //Juzga la intersección izquierda let sortByY = [By, Cy].sort((a,b) => ab) if (sortByY[0] < Ay && sortByY[1] > Ay){ if(Bx<Ax || Cx < Ax){ lado izquierdo++ } } } return lado izquierdo % 2 === 1}Existe un caso especial del método del rayo, que requiere un tratamiento especial cuando el punto se encuentra en un borde de múltiples deformaciones. Pero en ingeniería, creo que no es necesario procesarlo, porque si el usuario hace clic en el límite del gráfico, entonces el programa piensa que no ha hecho clic y puede pasar.
ResumirLos tres métodos anteriores pueden realizar la detección de clics en gráficos irregulares en el lienzo. Entre ellos, la ventaja del método de píxeles es que no selecciona formas y tiene ciertas ventajas de rendimiento en escenas estáticas; se debe decir que el método de ángulo tiene solo valor teórico y poca practicidad, el más práctico en ingeniería es el método de rayos; , que tiene pocas limitaciones y es fácil de implementar. Simple, la mayoría de las veces solo necesitas conocer el método de rayos.