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.
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.
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 offsetParent
s, 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;
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);
}
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 };
}
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()
.
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.
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}
}
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.
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!
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 prototype
natifs, changez simplement la fonction et appelez-la avec (canvas, event)
(et remplacez tout this
par canvas
).
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())
}
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>
Dans Prototype, utilisez cumulativeOffset () pour faire la sommation récursive comme mentionné par Ryan Artecona ci-dessus.
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.
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 };
}
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.
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};
}
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:
- 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é
- ils ne font partie d'aucune norme
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