32 votes

Détecter le passage de la souris sur certains points dans un canevas HTML ?

J'ai construit un moteur de visualisation de données analytiques pour Canvas et on m'a demandé d'ajouter un survol des éléments de données sous forme d'info-bulle pour afficher des métriques détaillées pour le point de données sous le curseur.

Pour les simples diagrammes à barres et de Gaant, les graphiques arborescents et les cartes de nœuds avec de simples zones carrées ou des points d'intérêt spécifiques, j'ai pu mettre cela en œuvre en superposant des DIV absolument positionnés avec des attributs :hover, mais il y a des visualisations plus compliquées comme des camemberts et un rendu de flux de trafic qui a des centaines de zones séparées définies par des courbes de Béziers.

Est-il possible d'attacher une superposition, ou de déclencher un événement lorsque l'utilisateur passe la souris sur un chemin fermé spécifique ?

Chaque zone pour laquelle le survol doit être spécifié est définie comme suit :

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 liaison à un objet de ce type est presque triviale à mettre en œuvre dans Flash ou Silverlight, car si l'implémentation actuelle de Canvas a l'avantage d'utiliser directement notre API Javascript existante et de s'intégrer à d'autres éléments Ajax, nous espérons éviter de faire intervenir Flash.

Des idées ?

21voto

Sam Hasler Points 10253

Vous pourriez gérer l'événement de déplacement de la souris et obtenir les coordonnées x et y de l'événement. Ensuite, vous devrez probablement itérer sur tous vos chemins pour tester si le point se trouve sur le chemin. J'avais un problème similaire qui pourrait avoir un code que vous pourriez utiliser.

Ce type de boucle peut être lent, surtout sous IE. Une façon d'accélérer le processus - et c'est un hack, mais il serait assez efficace - serait de changer la couleur avec laquelle chaque chemin est dessiné de façon à ce qu'il ne soit pas perceptible par les humains mais que chaque chemin soit dessiné dans une couleur différente. Avoir une table pour rechercher les couleurs des chemins et simplement rechercher la couleur du pixel sous la souris.

13voto

jcampbelly Points 526

Toile d'ombre

La meilleure méthode que j'ai vue ailleurs pour la détection du passage de la souris consiste à répéter la partie de votre dessin que vous voulez détecter sur un canevas caché et effacé. Enregistrez ensuite l'objet ImageData. Vous pouvez alors vérifier le tableau ImageData pour le pixel d'intérêt et retourner true si la valeur alpha est supérieure à 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
  ...
}

Avantages

  • Vous pouvez détecter tout ce que vous voulez puisque vous ne faites que répéter les méthodes contextuelles. Cela fonctionne avec les alpha PNG, les formes composées folles, le texte, etc.
  • Si votre image est assez statique, vous n'avez besoin de le faire qu'une seule fois par zone d'intérêt.
  • Le "masque" est lent, mais la recherche du pixel est très bon marché. La "partie rapide" est donc idéale pour la détection du passage de la souris.

Inconvénients

  • C'est un gros consommateur de mémoire. Chaque masque a une valeur de W*H*4. Si vous avez une petite surface de toile ou peu de zones à masquer, ce n'est pas si grave. Utilisez le gestionnaire de tâches de chrome pour surveiller l'utilisation de la mémoire.
  • Il y a actuellement un problème connu avec getImageData dans Chrome et Firefox. Les résultats ne sont pas immédiatement récupérés si vous annulez la variable. Si vous le faites trop souvent, vous verrez la mémoire augmenter rapidement. Cette opération est finalement récupérée et ne devrait pas faire planter le navigateur, mais elle peut être pénible sur les machines disposant de peu de RAM.

Un hack pour sauver la mémoire

Plutôt que de stocker l'ensemble du tableau ImageData, nous pouvons simplement nous rappeler quels pixels ont des valeurs alpha. Cela permet d'économiser beaucoup de mémoire, mais ajoute une boucle au processus de masquage.

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]) {
  ...
}

7voto

Fabien Ménager Points 45472

Cela pourrait être fait en utilisant la méthode ctx.isPointInPath, mais elle n'est pas implémentée dans ExCanvas pour IE. Mais une autre solution serait d'utiliser des cartes HTML, comme je l'ai fait pour cette petite bibliothèque : http://phenxdesign.net/projects/phenx-web/graphics/example.htm vous pouvez vous en inspirer, mais il est encore un peu bogué.

1voto

Shekhar Points 529

Il existe un livre d'Eric Rowell intitulé "HTML5 CANVAS COOKBOOK". Dans ce livre, il y a un chapitre intitulé "Interacting with the Canvas : Attaching Event Listeners to Shapes and Regions". Les événements mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend et touchmove peuvent être implémentés. Je vous conseille vivement de le lire.

1voto

Quickredfox Points 807

Je vous suggère de superposer une carte-image avec des coordonnées appropriées sur les zones correspondant aux éléments dessinés sur la toile. De cette façon, vous obtenez des infobulles ET un grand nombre d'autres fonctionnalités DOM/Browser gratuitement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X