Pregunta Detecta mouseover de ciertos puntos dentro de un lienzo HTML?


Creé un motor de visualización de datos analíticos para Canvas y se me solicitó que agregara información emergente similar a la información sobre herramientas sobre los elementos de datos para mostrar métricas detalladas para el punto de datos debajo del cursor.

Para gráficos simples de barra y Gaant, gráficos de árbol y mapas de nodos con áreas cuadradas simples o puntos de interés específicos, pude implementar esto al superponer DIVs absolutamente posicionados con: atributos de desplazamiento, pero hay algunas visualizaciones más complicadas como gráficos circulares y una representación de flujo de tráfico que tiene cientos de áreas separadas definidas por curvas bezeir.

¿Es posible adjuntar de alguna manera una superposición o desencadenar un evento cuando el usuario pasa sobre una ruta cerrada específica?

Cada área para la que se debe especificar el elemento emergente se define de la siguiente manera:

context.beginPath();
context.moveTo(segmentRight, prevTop);
context.bezierCurveTo(segmentRight, prevTop, segmentLeft, thisTop, segmentLeft, thisTop);
context.lineTo(segmentLeft, thisBottom);
context.bezierCurveTo(segmentLeft, thisBottom, segmentRight, prevBottom, segmentRight, prevBottom);
/*
 * ...define additional segments...
 */
// <dream> Ideally I would like to attach to events on each path:
context.setMouseover(function(){/*Show hover content*/});
// </dream>
context.closePath();

La vinculación a un objeto como este es casi trivial para implementar en Flash o Silverlight, ya que la implementación de Canvas actual tiene la ventaja de utilizar directamente nuestra API de Javascript existente y de integrarse con otros elementos de Ajax. Esperamos evitar incluir Flash en la mezcla.

¿Algunas ideas?


32
2017-08-03 09:11


origen


Respuestas:


Podría manejar el evento mousemove y obtener las coordenadas x, y del evento. Entonces es probable que deba repetir todas sus rutas para comprobar si el punto está sobre la ruta. Tuve un problema similar eso podría tener algún código que podrías usar.

Hacer bucles sobre cosas de esta manera puede ser lento, especialmente en IE. Una forma en que podrías acelerarlo potencialmente -y esto es un truco, pero sería bastante efectivo- sería cambiar el color con el que se dibuja cada camino para que no sea perceptible por los humanos, pero para que cada camino se dibuje en un color diferente Tenga una tabla para buscar colores en las rutas y simplemente busque el color del píxel debajo del mouse.


21
2017-08-03 09:59



Shadow Canvas

El mejor método que he visto en otros lugares para la detección de mouseover es repetir la parte de tu dibujo que deseas detectar en un lienzo oculto y despejado. A continuación, almacene el objeto ImageData. Luego puede verificar la matriz ImageData para el píxel de interés y devolver true si el valor alfa es mayor que 0.

// slow part
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(100,100,canvas.width-100,canvas.height-100);
var pixels = ctx.getImageData(0,0,canvas.width,canvas.height).data;

// fast part
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (pixels[idx]) { // alpha > 0
  ...
}

Ventajas

  • Puedes detectar todo lo que quieras ya que solo estás repitiendo los métodos de contexto. Esto funciona con PNG alpha, formas compuestas locas, texto, etc.
  • Si su imagen es bastante estática, solo necesita hacer esto una vez por área de interés.
  • La "máscara" es lenta, pero buscar el píxel es muy barato. Entonces, la "parte rápida" es excelente para la detección de mouseover.

Desventajas

  • Este es un cerdo de la memoria. Cada máscara es W * H * 4 valores. Si tiene un área de lienzo pequeña o pocas áreas para enmascarar, no es tan malo. Usa el administrador de tareas de Chrome para monitorear el uso de la memoria.
  • Actualmente hay un problema conocido con getImageData en Chrome y Firefox. Los resultados no son basura recogida de inmediato si anula la variable, por lo que si hace esto con demasiada frecuencia, verá que la memoria aumenta rápidamente. Eventualmente obtiene basura recolectada y no debe colgar el navegador, pero puede estar gravando en máquinas con pequeñas cantidades de RAM.

Un truco para salvar la memoria

En lugar de almacenar toda la matriz ImageData, podemos recordar qué píxeles tienen valores alfa. Ahorra una gran cantidad de memoria, pero agrega un bucle al proceso de máscara.

var mask = {};
var len = pixels.length;
for (var i=3;i<len;i+=4) if ( pixels[i] ) mask[i] = 1;

// this works the same way as the other method
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (mask[idx]) {
  ...
}

13
2017-07-07 01:17



Esto podría hacerse utilizando el método ctx.isPointInPath, pero no está implementado en ExCanvas para IE. Pero otra solución sería usar mapas HTML, como hice para esta pequeña biblioteca: http://phenxdesign.net/projects/phenx-web/graphics/example.htm puedes inspirarte de ello, pero todavía es un poco problemático.


7
2017-08-03 16:47



Sugiero superponer un mapa de imagen con las coordenadas adecuadas establecidas en las áreas para que coincidan con los elementos dibujados con lienzo. De esta forma, obtienes información sobre herramientas Y una gran cantidad de otras funcionalidades DOM / Browser gratis.


1
2017-09-08 14:53



Hay un libro de Eric Rowell llamado "HTML5 CANVAS COOKBOOK". En ese libro hay un capítulo llamado "Interactuando con el lienzo: Adjuntando oyentes de eventos a formas y regiones". se pueden implementar los eventos mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend y touchmove. Te sugiero que leas eso.


1
2018-03-13 08:52



Esto no puede hacerse (bueno, al menos no tan fácilmente), porque los objetos que dibuja en el lienzo (caminos) no se representan como los mismos objetos en el lienzo. Lo que quiero decir es que es simplemente un contexto 2D simple y una vez que dibujas algo sobre él, olvida por completo cómo se dibujó. Es solo un conjunto de píxeles para él.

Para ver mouseover y lo que le gusta, necesita algún tipo de lienzo de gráficos vectoriales, es decir, SVG o implementar uno propio sobre existente (que es lo que sugirió Sam Hasler)


0
2017-08-03 10:03



Necesitaba detectar los clics del mouse para una cuadrícula de cuadrados (como las celdas de una hoja de cálculo de Excel). Para acelerarlo, dividí la cuadrícula en regiones recursivamente reducidas a la mitad hasta que quedó un pequeño número de celdas, por ejemplo, para una cuadrícula de 100x100, las primeras 4 regiones podrían ser las cuadrículas de 50x50 que comprenden los cuatro cuadrantes. Entonces estos podrían dividirse en otros 4 cada uno (dando por lo tanto 16 regiones de 25x25 cada una). Esto requiere un pequeño número de comparaciones y, finalmente, la cuadrícula de 25x25 podría probarse para cada celda (625 comparaciones en este ejemplo).


0
2017-12-20 18:32