Comment obtenir les coordonnées d'un clic de souris sur un élément de canevas?

276

Quelle est la façon la plus simple d'ajouter un gestionnaire d'événements click à un élément canvas qui renverra 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 suffiront.

À M
la source
Cela ne devrait pas être différent de l'obtention d'événements de souris à partir d'éléments dom normaux. quirksmode a une bonne référence à ce sujet.
airportyh
4
Le code que vous listez ci-dessus ne fonctionne que lorsque le canevas n'est pas au fond des autres conteneurs. En général, vous devez utiliser quelque chose comme la fonction de décalage jquery [var testDiv = $ ('# testDiv'); var offset = testDiv.offset ();] pour obtenir le décalage correct de manière croisée. C'est une vraie douleur dans le ***.
Aaron Watters
Le code publié ci-dessus avec Update ne fonctionne pas si la page contenant le canevas défile.
Timothy Kim
J'ai supprimé mon ancienne «réponse» qui était incluse en tant que mise à jour de la question. Comme mentionné, il était obsolète et incomplet.
Tom
3
Puisqu'il y a environ 50 réponses ici, je recommande de faire défiler jusqu'à cette réponse des gars: patriques - un bon et simple 5 lignes.
Igor L.

Réponses:

77

Edit 2018: Cette réponse est assez ancienne et utilise des vérifications pour les anciens navigateurs qui ne sont plus nécessaires, car les propriétés clientXet clientYfonctionnent dans tous les navigateurs actuels. Vous voudrez peut-être consulter Patriques Answer pour une solution plus simple et plus récente.

Réponse originale:
comme décrit dans un article que j'ai trouvé à l'époque mais qui n'existe plus:

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.

N4ppeL
la source
30
Cette solution ne fonctionne pas toujours - la réponse de Ryan Artecona fonctionne dans le cas plus général lorsque la toile n'est pas nécessairement positionnée par rapport à la page entière.
Chris Johnson
3
Cette solution n'est pas adaptée si les performances sont importantes, car l'accès Dom se fait à chaque clic / déplacement. Et getBoundingClientRect existe maintenant et est plus élégant.
GameAlchemist
192

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

function getCursorPosition(canvas, event) {
    const rect = canvas.getBoundingClientRect()
    const x = event.clientX - rect.left
    const y = event.clientY - rect.top
    console.log("x: " + x + " y: " + y)
}

const canvas = document.querySelector('canvas')
canvas.addEventListener('mousedown', function(e) {
    getCursorPosition(canvas, e)
})
patriques
la source
Si la page est déroulée, je suppose que l'on devrait également prendre en compte le décalage de défilement du document.
Peppe LG
8
@ Le rectangle client délimitant PeppeL-G calcule cela pour vous. Vous pouvez facilement le tester dans la console avant de poster un commentaire (c'est ce que j'ai fait).
Tomáš Zato - Rétablir Monica
1
@ TomášZato, oh, getBoundingClientRectrenvoie des positions par rapport au port de vue? Alors ma supposition était fausse. Je ne l'ai jamais testé car cela n'a jamais été un problème pour moi, et je voulais gentiment avertir les autres lecteurs d'un problème potentiel que j'ai vu, mais merci pour votre clarification.
Peppe LG
1
Cela ne fonctionne pas si le canevas est mis à l'échelle. Je ne sais pas si c'est un bug de navigateur.
Jeroen
2
Ajouter une utilisation pour quelqu'un comme moi:var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
SnowOnion
179

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 stylisé par rapport à la page entière, le canvas.offsetLeft/Topne renvoie pas toujours ce dont vous avez besoin. Il renverra le nombre de pixels dont il est décalé par rapport à son élément offsetParent, qui peut être quelque chose comme un divélément contenant le canevas avec un position: relativestyle appliqué. Pour tenir compte de cela, vous devez parcourir la chaîne de offsetParents, en commençant par l'élément canvas lui-même. 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;
Ryan Artecona
la source
8
Non, il utilise l'objet prototype javascript intégré --- developer.mozilla.org/en/…
garg
14
mon Chrome a event.offsetXet event.offsetYattributs, donc je modifie votre solution en ajoutant if (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; }. on dirait que ça marche.
Baczek
3
Baczek a raison sur Chrome event.offsetXet event.offsetYfonctionne également dans IE9. Pour Firefox (testé avec v13), vous pouvez utiliser event.layerXet event.layerY.
mafafu
3
J'ai également ajouté ceci: canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
amirpc
4
Cette version finale sur la réponse n'a pas fonctionné pour moi. Sur Firefox, si tout l'écran défile, j'ai la valeur déplacée en sortie. Ce qui a fonctionné pour moi était une solution très similaire, donnée sur stackoverflow.com/a/10816667/578749 qu'au lieu de event.pageX / Y, il a soustrait le décalage calculé de event.clientX / Y. Quelqu'un pourrait-il revoir cela et expliquer?
lvella
33

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

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 rembourrage, etc. Dans Firefox, layerX / Y et offsetX / Y sont équivalents, mais l'offset didn n'existe pas auparavant. Ainsi, 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 };
}
mafafu
la source
1
intéressant de voir comment layerX, layerY est défini à la fois sur Chrome et Firefox, mais sur Chrome, il est inexact (ou signifie autre chose).
Julian Mann
@JulianMann Merci pour l'info. J'ai mis à jour cette réponse sur la base d'un support plus actuel. Il semble que vous puissiez vous en sortir avec offsetX / Y presque universellement maintenant.
mafafu
18

Selon le nouveau Quirksmode, les méthodes clientXet clientYsont prises en charge dans tous les principaux navigateurs. Donc, c'est parti - le bon code qui fonctionne dans une div à défilement 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 également jQuery pour $(canvas).offset().

Aldekein
la source
Équivalent à la réponse @ N4ppeL.
Paweł Szczur
13

J'ai fait une démonstration 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. Enregistrez-le ensuite sous example.html et, enfin, ouvrez le fichier avec un navigateur.

Manuel Ignacio López Quintero
la source
11

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}
}
Cryptovirus
la source
7

Méfiez-vous lors de la conversion des coordonnées; plusieurs valeurs non inter-navigateurs sont renvoyées dans un événement de clic. Utiliser clientX et clientY seuls ne suffit pas si la fenêtre du navigateur est défilante (vérifiée dans Firefox 3.5 et Chrome 3.0).

Ce mode excentrique article en fournit une fonction plus correcte qui peut utiliser pageX ou pageY ou une combinaison de clientX avec document.body.scrollLeft et clientY avec document.body.scrollTop pour calculer la coordonnée de clic relative à l'origine du document.

MISE À JOUR: En outre, 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 rapportera pas le coin supérieur gauche de sa région de contenu comme offsetLeft. Il existe différentes solutions à ce problème; le plus simple peut être d'effacer tous les styles de bordure, de remplissage, etc. sur la toile elle-même et de les appliquer à la place à une boîte contenant la toile.

fixermark
la source
7

Je ne sais pas quel est le point de toutes ces réponses qui parcourent les éléments parents et font toutes sortes de choses étranges .

La HTMLElement.getBoundingClientRectméthode 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 trucs comme ce scrollTopn'est pas nécessaire:

(à partir de MDN) La quantité de défilement qui a été effectuée de la zone de la fenêtre d'affichage (ou de tout autre élément défilant ) est prise en compte lors du calcul du rectangle englobant

Image normale

L' approche la plus simple a déjà été publiée ici. C'est correct tant qu'aucun CSS sauvage règle n'est impliquée.

Manipulation de la toile / image étirée

Lorsque la largeur des pixels de l'image ne correspond pas à sa largeur CSS, vous devrez appliquer un certain rapport aux valeurs des pixels:

/* 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 le canevas n'a pas de bordure, il fonctionne pour les images étirées (jsFiddle) .

Gestion des bordures CSS

Si la toile a une bordure épaisse, les choses se compliquent peu . Vous devrez littéralement soustraire la bordure du rectangle englobant. 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 penser à rien qui pourrait brouiller cette fonction finale. Rendez-vous à JsFiddle .

Remarques

Si vous n'aimez pas modifier les prototypes natifs , changez simplement la fonction et appelez-la avec (canvas, event)(et remplacez-la thispar canvas).

Tomáš Zato - Réintégrer Monica
la source
6

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);

J'espère que cela t'aides!

FraZer
la source
La solution de Ryan Artecona ne fonctionnait pas pour les navigateurs de tablettes avec zoom. Cependant celui-ci l'a fait.
Andy Meyers
Ne fonctionne pas avec les images qui ont CSS width/ heightsurchargé mais reste l'une des meilleures solutions.
Tomáš Zato - Rétablir Monica
3

En utilisant jQuery en 2016, pour obtenir des coordonnées de clic par rapport au canevas, je fais:

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

Cela fonctionne car les deux zones offset () et jqEvent.pageX / Y sont relatives au document, quelle que soit la position du défilement.

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

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}
Salsepareille
la source
Sensationnel. Comment «jqEvent» est-il défini? Ou «toile»? Ou dans le deuxième exemple «coords»? Avez-vous besoin d'exécuter le premier exemple avant le second? Dans ce cas, pourquoi n'écririez-vous pas "pour les obtenir, vous le feriez AUSSI"? Est-ce que tout cela va dans une fonction onclick ou quoi? Donnez un peu de contexte, mon pote. Et étant donné que la question d'origine a été posée en 2008, je pense que vous devez répondre dans le contexte de la technologie qui était disponible en 2008. Affinez votre réponse en utilisant jQuery valide à partir de la version qui était disponible à l'époque (v1.2). ;)
Ayelis
1
OK, désolé pour ma présomption. Je vais modifier pour supprimer cela. J'avais l'intention de fournir la réponse en utilisant les cadres les plus récents. Et je crois qu'un programmeur n'aurait pas besoin d'expliquer ce que sont jqEvent, canvas et coords.
Salsepareille
Cela semble bon. Merci pour votre contribution! Désolé de vous avoir donné du fil à retordre! ;)
Ayelis
3

C'est donc à la fois simple mais un sujet un peu plus compliqué qu'il n'y paraît.

Tout d'abord, il y a généralement des questions confondues ici

  1. Comment obtenir des coordonnées de souris relatives à un élément

  2. Comment obtenir les coordonnées de la souris pixel pixel pour l'API 2D Canvas ou WebGL

donc, réponses

Comment obtenir des coordonnées de souris relatives à un élément

Que l'élément soit ou non un canevas obtenant les coordonnées relatives de la souris de l'élément est le même pour tous les éléments.

Il y a 2 réponses simples à la question "Comment obtenir des coordonnées de souris relatives au canevas"

Réponse simple # 1 utilisez offsetXetoffsetY

canvas.addEventListner('mousemove', (e) => {
  const x = e.offsetX;
  const y = e.offsetY;
});

Cette réponse fonctionne dans Chrome, Firefox et Safari. Contrairement à toutes les autres valeurs d'événement offsetXet offsetYprenez en compte les transformations CSS.

Le plus gros problème avec offsetXet à offsetYpartir de 2019/05, ils n'existent pas sur les événements tactiles et ne peuvent donc pas être utilisés avec iOS Safari. Ils existent sur les événements de pointeur qui existent dans Chrome et Firefox, mais pas dans Safari, mais apparemment Safari y travaille .

Un autre problème est que les événements doivent être sur la toile elle-même. Si vous les placez sur un autre élément ou sur la fenêtre, vous ne pourrez plus choisir le canevas comme point de référence.

Réponse simple n ° 2 utilisation clientX, clientYetcanvas.getBoundingClientRect

Si vous ne vous souciez pas des transformations CSS, la réponse la plus simple suivante consiste à appeler canvas. getBoundingClientRect()et à soustraire la gauche de clientXet topde la même manière clientYque dans

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
});

Cela fonctionnera tant qu'il n'y aura pas de transformations CSS. Il fonctionne également avec les événements tactiles et fonctionnera donc avec Safari iOS

canvas.addEventListener('touchmove', (e) => {
  const rect = canvas. getBoundingClientRect();
  const x = e.touches[0].clientX - rect.left;
  const y = e.touches[0].clientY - rect.top;
});

Comment obtenir les coordonnées de la souris pixel pixel pour l'API 2D Canvas

Pour cela, nous devons prendre les valeurs ci-dessus et convertir la taille du canevas affichée en nombre de pixels dans le canevas lui-même

avec canvas.getBoundingClientRectet clientXetclientY

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const elementRelativeX = e.clientX - rect.left;
  const elementRelativeY = e.clientY - rect.top;
  const canvasRelativeX = elementRelativeX * canvas.width / rect.width;
  const canvasRelativeY = elementRelativeY * canvas.height / rect.height;
});

ou avec offsetXetoffsetY

canvas.addEventListener('mousemove', (e) => {
  const elementRelativeX = e.offsetX;
  const elementRelativeX = e.offsetY;
  const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
  const canvasRelativeY = elementRelativeX * canvas.height / canvas.clientHeight;
});

Remarque: Dans tous les cas, n'ajoutez pas de rembourrage ou de bordures au canevas. Cela compliquerait considérablement le code. Au lieu de cela, vous voulez qu'une bordure ou un rembourrage entoure le canevas dans un autre élément et ajoute le rembourrage et / ou la bordure à l'élément extérieur.

Exemple de travail utilisant event.offsetX,event.offsetY

Exemple de travail utilisant canvas.getBoundingClientRectet event.clientXetevent.clientY

gman
la source
2

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>

la source
à quoi ça sert x = new Number()? Le code ci-dessous qui réaffecte xce qui signifie que le numéro attribué est immédiatement rejeté
gman
1

Vous pourriez 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.

user2310569
la source
1

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

  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 };
  }
Daniel Patru
la source
0

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};
}
Simon Hi
la source
0

Tout d'abord, comme d'autres l'ont dit, vous avez besoin d'une fonction pour obtenir la position de l'élément canvas . Voici une méthode 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 à celle-ci:

$('#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 findPosfonction générique du code de gestion des événements. ( Comme il se doit . Nous devons essayer de limiter nos fonctions à une tâche chacune.)

Les valeurs de offsetLeftet offsetTopsont relatives à offsetParent, ce qui pourrait être un divnœud wrapper (ou toute autre chose, d'ailleurs). Lorsqu'il n'y a pas d'élément enveloppant, canvasils sont relatifs à 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.

Similaire, e.pageXet e.pageYdonnez la position du curseur par rapport au document. C'est pourquoi nous soustrayons le décalage du canevas de ces valeurs pour arriver à la vraie position.

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

  1. Ces valeurs sont également relatives à 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
Wayne
la source
0

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. Pourrait aider quelqu'un d'autre à poster. Je l'ai d' ici

Aniket Betkikar
la source
0

Voici une solution simplifiée (cela ne fonctionne pas avec les bordures / défilement):

function click(event) {
    const bound = event.target.getBoundingClientRect();

    const xMult = bound.width / can.width;
    const yMult = bound.height / can.height;

    return {
        x: Math.floor(event.offsetX / xMult),
        y: Math.floor(event.offsetY / yMult),
    };
}
Haykam
la source
0

Je créais une application ayant un canevas sur un pdf, qui impliquait beaucoup de redimensionnements du canevas comme Zoomer le pdf en avant et en arrière, et à mon tour à chaque zoom avant / arrière du PDF j'ai dû redimensionner le canevas pour adapter le taille du pdf, j'ai parcouru beaucoup de réponses dans stackOverflow, et je n'ai pas trouvé de solution parfaite qui finira par résoudre le problème.

J'utilisais rxjs et angular 6, et je n'ai trouvé aucune réponse spécifique à la dernière version.

Voici l'intégralité de l'extrait de code qui pourrait être utile à quiconque utilisant rxjs pour dessiner sur le dessus du canevas.

  private captureEvents(canvasEl: HTMLCanvasElement) {

    this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
      .pipe(
        switchMap((e: any) => {

          return fromEvent(canvasEl, 'mousemove')
            .pipe(
              takeUntil(fromEvent(canvasEl, 'mouseup').do((event: WheelEvent) => {
                const prevPos = {
                  x: null,
                  y: null
                };
              })),

              takeUntil(fromEvent(canvasEl, 'mouseleave')),
              pairwise()
            )
        })
      )
      .subscribe((res: [MouseEvent, MouseEvent]) => {
        const rect = this.cx.canvas.getBoundingClientRect();
        const prevPos = {
          x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
          y:  Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
        };
        const currentPos = {
          x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
          y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
        };

        this.coordinatesArray[this.file.current_slide - 1].push(prevPos);
        this.drawOnCanvas(prevPos, currentPos);
      });
  }

Et voici l'extrait qui corrige, les coordonnées de la souris par rapport à la taille du canevas, quelle que soit la façon dont vous zoomez / dézoomez le canevas.

const prevPos = {
  x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
  y:  Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
const currentPos = {
  x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
  y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
Divyanshu Rawat
la source
-1

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

Il devrait être assez évident de le reconvertir 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 cela pourra aider.

Brian Gianforcaro
la source
6
clientX / clientY ne se comportent pas de la même manière entre les navigateurs.
tfwright