Comment puis-je obtenir les coordonnées d'un clic de souris sur un élément canvas?

Quel est le moyen le plus simple d'ajouter un gestionnaire d'événement click à un élément canvas qui retournera les coordonnées x et y du clic (par rapport à l'élément canvas)?

Aucune compatibilité de navigateur héritée requise, Safari, Opera et Firefox feront l'affaire.

245
demandé sur Tom 2008-09-11 06:07:45

19 réponses

Tel Que décrit ici:

var x;
var y;
if (e.pageX || e.pageY) { 
  x = e.pageX;
  y = e.pageY;
}
else { 
  x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 
  y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 
} 
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;

A parfaitement fonctionné pour moi.

69
répondu N4ppeL 2010-12-26 23:02:11

Mise à jour (5/5/16): la réponse de patriques devrait être utilisée à la place, car elle est à la fois plus simple et plus fiable.


Comme le canevas n'est pas toujours stylé par rapport à la page entière, le canvas.offsetLeft/Top ne renvoie pas toujours ce dont vous avez besoin. Il retournera le nombre de pixels qu'il est décalé par rapport à son élément offsetParent, qui peut être quelque chose comme un élément div contenant le canevas avec un style position: relative appliqué. Pour tenir compte de cela, vous devez faire une boucle à travers la chaîne de offsetParents, en commençant par l'élément canvas. Ce code fonctionne parfaitement pour moi, testé dans Firefox et Safari, mais devrait fonctionner pour tous.

function relMouseCoords(event){
    var totalOffsetX = 0;
    var totalOffsetY = 0;
    var canvasX = 0;
    var canvasY = 0;
    var currentElement = this;

    do{
        totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
        totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
    }
    while(currentElement = currentElement.offsetParent)

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    return {x:canvasX, y:canvasY}
}
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;

La dernière ligne rend les choses pratiques pour obtenir les coordonnées de la souris par rapport à un élément canvas. Tout ce qui est nécessaire pour obtenir les coordonnées utiles est

coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;
171
répondu Ryan Artecona 2017-05-23 12:18:18

Si vous aimez la simplicité mais que vous voulez toujours des fonctionnalités multi-navigateurs, j'ai trouvé que cette solution fonctionnait le mieux pour moi. C'est une simplification de la solution @Aldekeins mais sans jQuery.

function getCursorPosition(canvas, event) {
    var rect = canvas.getBoundingClientRect();
    var x = event.clientX - rect.left;
    var y = event.clientY - rect.top;
    console.log("x: " + x + " y: " + y);
}
134
répondu patriques 2013-08-05 15:07:35

Le navigateur moderne gère maintenant cela pour vous. Chrome, IE9 et Firefox prennent en charge le offsetX/y comme ceci, en passant l'événement à partir du gestionnaire de clic.

function getRelativeCoords(event) {
    return { x: event.offsetX, y: event.offsetY };
}

La plupart des navigateurs modernes prennent également en charge layerX/Y, mais Chrome et IE utilisent layerX/Y pour le décalage absolu du clic sur la page, y compris la marge, le remplissage, etc. Dans Firefox, layerX / y et offsetX/Y sont équivalents, mais offset n'existait pas auparavant. Donc, pour la compatibilité avec les navigateurs légèrement plus anciens, vous pouvez utiliser:

function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}
23
répondu mafafu 2016-07-06 18:45:18

Selon fraîche Quirksmode le clientX et clientY méthodes sont pris en charge dans tous les principaux navigateurs. Donc, ça y est-le bon code qui fonctionne dans un div défilant sur une page avec des barres de défilement:

function getCursorPosition(canvas, event) {
var x, y;

canoffset = $(canvas).offset();
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - Math.floor(canoffset.left);
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - Math.floor(canoffset.top) + 1;

return [x,y];
}

Cela nécessite aussi d' jQuery pour $(canvas).offset().

17
répondu Aldekein 2018-04-08 20:45:56

J'ai fait une démostration complète qui fonctionne dans chaque navigateur avec le code source complet de la solution de ce problème: coordonnées d'un clic de souris sur Canvas en Javascript. Pour essayer la démo, Copiez le code et collez-le dans un éditeur de texte. Puis enregistrez-le comme exemple.html et, enfin, ouvrez le fichier avec un navigateur.

13
répondu Manuel Ignacio López Quintero 2011-05-08 15:57:28

Voici une petite modification à la réponse de Ryan Artecona pour les toiles avec une largeur variable ( % ):

 HTMLCanvasElement.prototype.relMouseCoords = function (event) {
    var totalOffsetX = 0;
    var totalOffsetY = 0;
    var canvasX = 0;
    var canvasY = 0;
    var currentElement = this;

    do {
        totalOffsetX += currentElement.offsetLeft;
        totalOffsetY += currentElement.offsetTop;
    }
    while (currentElement = currentElement.offsetParent)

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    // Fix for variable canvas width
    canvasX = Math.round( canvasX * (this.width / this.offsetWidth) );
    canvasY = Math.round( canvasY * (this.height / this.offsetHeight) );

    return {x:canvasX, y:canvasY}
}
10
répondu Cryptovirus 2017-05-23 12:34:42

Méfiez-vous lorsque vous effectuez la conversion de coordonnées; plusieurs valeurs non-cross-browser sont renvoyées dans un événement click. L'utilisation de clientX et clientY seuls ne suffit pas si la fenêtre du navigateur défile (vérifiée dans Firefox 3.5 et Chrome 3.0).

Cet article quirks Mode fournit une fonction plus correcte qui peut utiliser pageX ou pageY ou une combinaison de clientX avec document.corps.scrollLeft et clientY avec document.corps.scrollTop pour calculer le clic coordonner par rapport à l'origine du document.

UPDATE: de plus, offsetLeft et offsetTop sont relatifs à la taille rembourrée de l'élément, pas à la taille intérieure. Un canevas avec le padding: style appliqué ne signale pas la partie supérieure gauche de sa région de contenu comme offsetLeft. Il existe différentes solutions à ce problème; le plus simple peut être de vider toute la bordure, le remplissage, etc. styles sur le canevas lui - même et les appliquer à la place à une boîte contenant le canevas.

7
répondu fixermark 2010-01-20 12:56:46

Voici un très joli tutoriel -

Http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/

 <canvas id="myCanvas" width="578" height="200"></canvas>
<script>
  function writeMessage(canvas, message) {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.font = '18pt Calibri';
    context.fillStyle = 'black';
    context.fillText(message, 10, 25);
  }
  function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
  }
  var canvas = document.getElementById('myCanvas');
  var context = canvas.getContext('2d');

  canvas.addEventListener('mousemove', function(evt) {
    var mousePos = getMousePos(canvas, evt);
    var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
    writeMessage(canvas, message);
  }, false);

Espérons que cela aide!

6
répondu FraZer 2013-07-01 10:10:30

Je ne suis pas sûr de l'intérêt de toutes ces réponses qui parcourent les éléments parents et font toutes sortes de trucs bizarres.

La méthode HTMLElement.getBoundingClientRect est conçue pour gérer la position réelle de l'écran de n'importe quel élément. Cela inclut le défilement, donc des choses comme scrollTop ne sont pas nécessaires:

(de MDN) Le montant de défilement qui a été fait de la zone de fenêtre d'affichage (ou tout autre élément défilant ) est pris en compte lorsque le calcul de la rectangle délimitant

Image Normale

L'approche très simple était déjà affichée ici. Ceci est correct tant que aucune règle CSS sauvage n'est impliquée.

Manipulation de la toile tendue / image

Lorsque la largeur du pixel de l'image ne correspond pas à sa largeur CSS, vous devez appliquer un certain ratio sur les valeurs de pixel:

/* Returns pixel coordinates according to the pixel that's under the mouse cursor**/
HTMLCanvasElement.prototype.relativeCoords = function(event) {
  var x,y;
  //This is the current screen rectangle of canvas
  var rect = this.getBoundingClientRect();
  var top = rect.top;
  var bottom = rect.bottom;
  var left = rect.left;
  var right = rect.right;
  //Recalculate mouse offsets to relative offsets
  x = event.clientX - left;
  y = event.clientY - top;
  //Also recalculate offsets of canvas is stretched
  var width = right - left;
  //I use this to reduce number of calculations for images that have normal size 
  if(this.width!=width) {
    var height = bottom - top;
    //changes coordinates by ratio
    x = x*(this.width/width);
    y = y*(this.height/height);
  } 
  //Return as an array
  return [x,y];
}

Tant que la toile n'a pas de bordure, cela fonctionne pour les images étirées (jsFiddle).

Gestion des bordures CSS

Si la toile a une bordure épaisse, les choses deviennent peu compliquées . Vous aurez littéralement besoin de soustraire la bordure du rectangle délimitant. Cela peut être fait en utilisant .getComputedStyle . Cette réponse décrit le processus .

La fonction grandit alors un peu:

/* Returns pixel coordinates according to the pixel that's under the mouse cursor**/
HTMLCanvasElement.prototype.relativeCoords = function(event) {
  var x,y;
  //This is the current screen rectangle of canvas
  var rect = this.getBoundingClientRect();
  var top = rect.top;
  var bottom = rect.bottom;
  var left = rect.left;
  var right = rect.right;
  //Subtract border size
  // Get computed style
  var styling=getComputedStyle(this,null);
  // Turn the border widths in integers
  var topBorder=parseInt(styling.getPropertyValue('border-top-width'),10);
  var rightBorder=parseInt(styling.getPropertyValue('border-right-width'),10);
  var bottomBorder=parseInt(styling.getPropertyValue('border-bottom-width'),10);
  var leftBorder=parseInt(styling.getPropertyValue('border-left-width'),10);
  //Subtract border from rectangle
  left+=leftBorder;
  right-=rightBorder;
  top+=topBorder;
  bottom-=bottomBorder;
  //Proceed as usual
  ...
}

Je ne peux pas penser à quelque chose qui pourrait confondre cette fonction finale. Reportez-vous à la JsFiddle.

Notes

Si vous n'aimez pas modifier les prototypenatifs, changez simplement la fonction et appelez-la avec (canvas, event) (et remplacez tout this par canvas).

6
répondu Tomáš Zato 2017-05-23 11:47:19

En utilisant jQuery en 2016, pour obtenir les coordonnées de clic relatives à la toile, je fais:

$(canvas).click(function(jqEvent) {
    var coords = {
        x: jqEvent.pageX - $(canvas).offset().left,
        y: jqEvent.pageY - $(canvas).offset().top
    };
});

Cela fonctionne depuis canvas offset () et jqEvent.pageX/y sont relatifs au document quelle que soit la position de défilement.

Notez que si votre canevas est mis à l'échelle, ces coordonnées ne sont pas les mêmes que canevas coordonnées logiques . Pour les obtenir, vous devriez aussi Faire:

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}
3
répondu Hai Phan 2016-04-21 16:26:33

Je recommande ce lien- http://miloq.blogspot.in/2011/05/coordinates-mouse-click-canvas.html

<style type="text/css">

  #canvas{background-color: #000;}

</style>

<script type="text/javascript">

  document.addEventListener("DOMContentLoaded", init, false);

  function init()
  {
    var canvas = document.getElementById("canvas");
    canvas.addEventListener("mousedown", getPosition, false);
  }

  function getPosition(event)
  {
    var x = new Number();
    var y = new Number();
    var canvas = document.getElementById("canvas");

    if (event.x != undefined && event.y != undefined)
    {
      x = event.x;
      y = event.y;
    }
    else // Firefox method to get the position
    {
      x = event.clientX + document.body.scrollLeft +
          document.documentElement.scrollLeft;
      y = event.clientY + document.body.scrollTop +
          document.documentElement.scrollTop;
    }

    x -= canvas.offsetLeft;
    y -= canvas.offsetTop;

    alert("x: " + x + "  y: " + y);
  }

</script>
2
répondu 2013-07-02 05:40:30

Dans Prototype, utilisez cumulativeOffset () pour faire la sommation récursive comme mentionné par Ryan Artecona ci-dessus.

Http://www.prototypejs.org/api/element/cumulativeoffset

1
répondu Jesse Smith 2011-07-26 23:10:13

Vous pouvez simplement faire:

var canvas = yourCanvasElement;
var mouseX = (event.clientX - (canvas.offsetLeft - canvas.scrollLeft)) - 2;
var mouseY = (event.clientY - (canvas.offsetTop - canvas.scrollTop)) - 2;

Cela vous donnera la position exacte du pointeur de la souris.

1
répondu user2310569 2013-11-03 08:19:34

Voir la démo à http://jsbin.com/ApuJOSA/1/edit?html,sortie .

  function mousePositionOnCanvas(e) {
      var el=e.target, c=el;
      var scaleX = c.width/c.offsetWidth || 1;
      var scaleY = c.height/c.offsetHeight || 1;

      if (!isNaN(e.offsetX)) 
          return { x:e.offsetX*scaleX, y:e.offsetY*scaleY };

      var x=e.pageX, y=e.pageY;
      do {
        x -= el.offsetLeft;
        y -= el.offsetTop;
        el = el.offsetParent;
      } while (el);
      return { x: x*scaleX, y: y*scaleY };
  }
1
répondu dpatru 2013-12-01 08:30:52

Hey, c'est dans dojo, juste parce que c'est ce que j'avais déjà le code pour un projet.

Il devrait être assez évident comment le convertir en JavaScript non Dojo vanilla.

  function onMouseClick(e) {
      var x = e.clientX;
      var y = e.clientY;
  }
  var canvas = dojo.byId(canvasId);
  dojo.connect(canvas,"click",onMouseClick);

J'espère que ça aide.

0
répondu Brian Gianforcaro 2008-09-11 02:49:27

Voici quelques modifications de la solution de Ryan Artecona ci-dessus.

function myGetPxStyle(e,p)
{
    var r=window.getComputedStyle?window.getComputedStyle(e,null)[p]:"";
    return parseFloat(r);
}

function myGetClick=function(ev)
{
    // {x:ev.layerX,y:ev.layerY} doesn't work when zooming with mac chrome 27
    // {x:ev.clientX,y:ev.clientY} not supported by mac firefox 21
    // document.body.scrollLeft and document.body.scrollTop seem required when scrolling on iPad
    // html is not an offsetParent of body but can have non null offsetX or offsetY (case of wordpress 3.5.1 admin pages for instance)
    // html.offsetX and html.offsetY don't work with mac firefox 21

    var offsetX=0,offsetY=0,e=this,x,y;
    var htmls=document.getElementsByTagName("html"),html=(htmls?htmls[0]:0);

    do
    {
        offsetX+=e.offsetLeft-e.scrollLeft;
        offsetY+=e.offsetTop-e.scrollTop;
    } while (e=e.offsetParent);

    if (html)
    {
        offsetX+=myGetPxStyle(html,"marginLeft");
        offsetY+=myGetPxStyle(html,"marginTop");
    }

    x=ev.pageX-offsetX-document.body.scrollLeft;
    y=ev.pageY-offsetY-document.body.scrollTop;
    return {x:x,y:y};
}
0
répondu Simon Hi 2013-06-18 20:35:49

Tout d'Abord, comme d'autres l'ont dit, vous avez besoin d'une fonction pour obtenir les position de l'élément canvas. Voici une méthode qui est un peu plus élégante que certaines des autres sur cette page (à mon humble avis). Vous pouvez lui passer n'importe quel élément et obtenir sa position dans le document:

function findPos(obj) {
    var curleft = 0, curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
    return undefined;
}

Calculez maintenant la position actuelle du curseur par rapport à cela:

$('#canvas').mousemove(function(e) {
    var pos = findPos(this);
    var x = e.pageX - pos.x;
    var y = e.pageY - pos.y;
    var coordinateDisplay = "x=" + x + ", y=" + y;
    writeCoordinateDisplay(coordinateDisplay);
});

Notez que j'ai séparé la fonction Générique findPos du code de gestion des événements. ( comme il se doit soit. Nous devrions essayer de garder nos fonctions à une tâche chacune.)

Les valeurs de offsetLeft et offsetTop sont relatives à offsetParent, qui pourrait être un nœud wrapper div (ou toute autre chose, d'ailleurs). Quand il n'y a pas d'élément enveloppant le canvas, ils sont relatifs au body, donc il n'y a pas de décalage à soustraire. C'est pourquoi nous devons déterminer la position de la toile avant de pouvoir faire autre chose.

Similaires, e.pageX et e.pageY donnent la position du curseur par rapport à la document. C'est pourquoi nous soustrayons le décalage du canevas de ces valeurs pour arriver à la position réelle.

Une alternative pour les positionné éléments est d'utiliser directement les valeurs de e.layerX et e.layerY. C'est moins fiable que la méthode ci-dessus pour deux raisons:

  1. Ces valeurs sont aussi par rapport à l'ensemble du document lorsque l'événement n'a pas lieu à l'intérieur d'un élément positionné
  2. ils ne font partie d'aucune norme
0
répondu Wayne Burkett 2014-01-05 06:26:35

ThreeJS r77

var x = event.offsetX == undefined ? event.layerX : event.offsetX;
var y = event.offsetY == undefined ? event.layerY : event.offsetY;

mouse2D.x = ( x / renderer.domElement.width ) * 2 - 1;
mouse2D.y = - ( y / renderer.domElement.height ) * 2 + 1;

Après avoir essayé de nombreuses solutions. Cela a fonctionné pour moi. Peut aider quelqu'un d'autre, d'où l'affichage. Je l'ai obtenu de ici

0
répondu Aniket Betkikar 2016-06-13 12:49:39