web-dev-qa-db-fra.com

Détecter le survol de 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 de type info-bulle sur des éléments de données pour afficher des mesures détaillées pour le point de données sous le curseur.

Pour les diagrammes à barres et Gaant simples, les diagrammes d'arbre et les cartes de nœuds avec des zones carrées simples ou des points d'intérêt spécifiques, j'ai pu implémenter cela en superposant des DIVs absolument positionnés avec: des attributs de survol, mais il existe des visualisations plus compliquées telles que les diagrammes circulaires et un rendu de flux de trafic qui a des centaines de zones distinctes définies par des courbes de Bezeir.

Est-il possible de joindre d'une manière ou d'une autre une superposition ou de déclencher un événement lorsque l'utilisateur survole 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 comme celui-ci est presque triviale à implémenter dans Flash ou Silverlight, car mais 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 mettre Flash dans le mélange.

Des idées?

32
ryandenki

Vous pouvez gérer l'événement mousemove et obtenir les coordonnées x, y de l'événement. Ensuite, vous devrez probablement parcourir tous vos chemins pour tester si le point se trouve sur le chemin. J'ai eu un problème similaire qui pourrait avoir du code que vous pourriez utiliser.

Boucler les choses de cette manière peut être lent, en particulier sur IE. Une façon dont vous pourriez potentiellement l'accélérer - et c'est un hack, mais ce serait assez efficace - serait de changer la couleur avec laquelle chaque chemin est dessiné afin qu'il ne soit pas perceptible par les humains mais pour que chaque chemin soit dessiné dans une couleur différente. Ayez un tableau pour rechercher les couleurs des chemins et recherchez simplement la couleur du pixel sous la souris.

21
Sam Hasler

Toile d'ombre

La meilleure méthode que j'ai vue ailleurs pour la détection de survol est de répéter la partie de votre dessin que vous souhaitez détecter sur un canevas masqué et effacé. Enregistrez ensuite l'objet ImageData. Vous pouvez ensuite vérifier le tableau ImageData pour le pixel d'intérêt et renvoyer 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 car vous ne faites que répéter les méthodes de contexte. Cela fonctionne avec PNG alpha, formes composées folles, texte, etc.
  • Si votre image est assez statique, vous ne devez 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 de survol.

Inconvénients

  • Ceci est un porc de mémoire. Chaque masque a des valeurs W * H * 4. Si vous avez une petite zone de toile ou quelques zones à masquer, ce n'est pas si mal. Utilisez le gestionnaire de tâches de Chrome pour surveiller l'utilisation de la mémoire.
  • Il existe actuellement un problème connu avec getImageData dans Chrome et Firefox. Les résultats ne sont pas récupérés immédiatement si vous annulez la variable, donc si vous le faites trop souvent, vous verrez la mémoire augmenter rapidement Il finit par récupérer les ordures et il ne devrait pas planter le navigateur, mais il peut être taxable sur les machines avec de petites quantités de RAM.

n hack pour économiser de la mémoire

Plutôt que de stocker l'intégralité du tableau ImageData, nous pouvons simplement nous rappeler quels pixels ont des valeurs alpha. Il économise beaucoup de mémoire, mais ajoute une boucle au processus de masque.

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
jcampbelly

Cela pourrait être fait en utilisant la méthode ctx.isPointInPath, mais cela n'est pas implémenté 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 c'est quand même un petit buggy.

7
Fabien Ménager

Je suggérerais de superposer une carte d'image avec des coordonnées appropriées définies sur les zones pour correspondre à vos éléments dessinés sur toile. De cette façon, vous obtenez gratuitement des info-bulles ET de nombreuses autres fonctionnalités DOM/Navigateur.

1
Quickredfox

J'avais besoin de détecter les clics de souris pour une grille de carrés (comme les cellules d'une feuille de calcul Excel). Pour l'accélérer, j'ai divisé la grille en régions de manière récursive en deux jusqu'à ce qu'il reste un petit nombre de cellules, par exemple pour une grille 100x100, les 4 premières régions pourraient être les grilles 50x50 comprenant les quatre quadrants. Ensuite, ceux-ci pourraient être divisés en 4 autres chacun (donnant ainsi 16 régions de 25x25 chacune). Cela nécessite un petit nombre de comparaisons et enfin la grille 25x25 pourrait être testée pour chaque cellule (625 comparaisons dans cet exemple).

1
Sid

Il y a un livre d'Eric Rowell intitulé "HTML5 CANVAS COOKBOOK". Dans ce livre, il y a un chapitre intitulé "Interagir avec le canevas: attacher des écouteurs d'événements aux formes et aux régions". Les événements mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend et touchmove peuvent être implémentés. Je vous suggère fortement de lire cela.

1
Shekhar

Cela ne peut pas être fait (enfin, du moins pas si facilement), car les objets que vous dessinez sur le canevas (chemins) ne sont pas représentés comme les mêmes objets dans le canevas. Ce que je veux dire, c'est que c'est juste un simple contexte 2D et une fois que vous avez dessiné quelque chose dessus, il oublie complètement comment il a été dessiné. C'est juste un ensemble de pixels pour cela.

Pour regarder le survol de la souris et ses goûts, vous avez besoin d'une sorte de canevas de graphiques vectoriels, c'est-à-dire SVG ou implémentez le vôtre au-dessus de l'existant (ce que Sam Hasler a suggéré)

0
Maxim Sloyko