Pregunta

He creado un motor de visualización de datos analíticos para Canvas y se me ha pedido que agregue un cursor flotante sobre los elementos de datos para mostrar métricas detalladas para el punto de datos debajo del cursor.

Para barra simple & amp; Gráficos de Gaant, gráficos de árbol y mapas de nodos con áreas cuadradas simples o puntos de interés específicos, pude implementar esto superponiendo DIV absolutamente posicionados con: atributos de desplazamiento, pero hay algunas visualizaciones más complicadas como gráficos circulares y un flujo de tráfico renderizado 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 se mueve sobre una ruta cerrada específica?

Cada área para la que se debe especificar el desplazamiento 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();

El enlace a un objeto como este es casi trivial de implementar en Flash o Silverlight, ya que, pero la implementación actual de Canvas tiene la ventaja de usar directamente nuestra API Javascript existente e integrarse con otros elementos de Ajax, esperamos evitar poner Flash en la mezcla.

¿Alguna idea?

¿Fue útil?

Solución

Puede manejar el evento mousemove y obtener las coordenadas x, y del evento. Entonces, probablemente tendrá que recorrer todos sus caminos para probar si el punto está sobre el camino. Tuve un problema similar que podría tener algún código que puedas usar.

Recorrer las cosas de esta manera puede ser lento, especialmente en IE. Una forma en que podría acelerarlo, y esto es un truco, pero sería bastante efectivo, sería cambiar el color con el que se dibuja cada ruta para que los humanos no la noten, pero para que cada ruta se dibuje en Un color diferente. Tenga una tabla para buscar colores en las rutas y solo busque el color del píxel debajo del mouse.

Otros consejos

Lienzo de sombras

El mejor método que he visto en otros lugares para la detección del mouseover es repetir la parte de su dibujo que desea detectar en un lienzo oculto y despejado. Luego almacene el objeto ImageData. Luego puede verificar la matriz ImageData para el píxel de interés y devolver verdadero 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 lo que quieras ya que solo estás repitiendo los métodos de contexto. Esto funciona con PNG alfa, formas compuestas locas, texto, etc.
  • Si su imagen es bastante estática, entonces solo necesita hacer esto una vez por área de interés.
  • La " máscara " es lento, pero buscar el píxel es muy barato. Entonces la "parte rápida" es ideal para la detección del mouseover.

Desventajas

  • Esto es un cerdo de memoria. Cada máscara tiene valores W * H * 4. Si tiene un área de lienzo pequeña o pocas áreas para enmascarar, no está tan mal. Use el administrador de tareas de Chrome para monitorear el uso de memoria.
  • Actualmente hay un problema conocido con getImageData en Chrome y Firefox. Los resultados no son basura recolectada de inmediato si anula la variable, por lo que si hace esto con demasiada frecuencia, verá que la memoria aumenta rápidamente. Eventualmente se recolecta basura y no debería bloquear el navegador, pero puede ser gravoso en máquinas con pequeñas cantidades de RAM.

Un truco para ahorrar 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]) {
  ...
}

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

Sugeriría superponer un mapa de imagen con las coordenadas adecuadas establecidas en las áreas para que coincidan con sus elementos dibujados en lienzo. De esta manera, obtienes información sobre herramientas Y muchas otras funciones DOM / Navegador gratis.

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 dividiéndolas recursivamente a la mitad hasta que quedara 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. Luego, estos podrían dividirse en otros 4 cada uno (por lo tanto, dar 16 regiones de 25x25 cada uno). 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).

Hay un libro de Eric Rowell llamado " HTML5 CANVAS COOKBOOK " ;. En ese libro hay un capítulo llamado "Interactuar con el lienzo: Adjuntar oyentes de eventos a formas y regiones". Se pueden implementar eventos de mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend y touchmove. Le recomiendo que lea eso.

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

Para ver el mouseover y los me gusta, necesita algún tipo de lienzo de gráficos vectoriales, que es SVG o implementar el suyo sobre el existente (que es lo que sugirió Sam Hasler)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top