¿Cómo obtengo las coordenadas de un clic del mouse en un elemento de lienzo?

276

¿Cuál es la forma más sencilla de agregar un controlador de eventos de clic a un elemento del lienzo que devolverá las coordenadas x e y del clic (en relación con el elemento del lienzo)?

No se requiere compatibilidad de navegador heredada, Safari, Opera y Firefox lo harán.

Tom
fuente
Esto no debería ser diferente de obtener eventos del mouse de elementos dom normales. quirksmode tiene una buena referencia en eso.
airportyh
44
El código que enumera arriba solo funciona cuando el lienzo no está dentro de otros contenedores. En general, necesita usar algo como la función de desplazamiento jquery [var testDiv = $ ('# testDiv'); var offset = testDiv.offset ();] para obtener el desplazamiento correcto de forma cruzada. Este es un verdadero dolor en el culo.
Aaron Watters
El código publicado anteriormente con Actualización no funciona si la página que contiene el lienzo se desplaza.
Timothy Kim
Eliminé mi "respuesta" anterior que se incluyó como una actualización de la pregunta. Como se mencionó, estaba desactualizado e incompleto.
Tom
3
Como hay como 50 respuestas aquí, recomiendo desplazarse a esta respuesta de los chicos: patriques: un buen y simple 5 líneas.
Igor L.

Respuestas:

77

Editar 2018: esta respuesta es bastante antigua y utiliza comprobaciones para navegadores antiguos que ya no son necesarios, ya que las propiedades clientXy clientYfuncionan en todos los navegadores actuales. Es posible que desee consultar Patriques Answer para obtener una solución más simple y más reciente.

Respuesta original:
como se describe en un artículo que encontré en ese entonces pero ya no existe:

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;

Funcionó perfectamente bien para mí.

N4ppeL
fuente
30
Esta solución no siempre funciona: la respuesta de Ryan Artecona funciona en el caso más general cuando el lienzo no está necesariamente posicionado en relación con toda la página.
Chris Johnson
3
Esta solución no se ajusta si el rendimiento es importante, ya que el acceso Dom se realiza en cada clic / movimiento. Y getBoundingClientRect existe ahora, y es más elegante.
GameAlchemist
192

Si le gusta la simplicidad pero aún desea la funcionalidad de navegador cruzado, encontré que esta solución funcionó mejor para mí. Esta es una simplificación de la solución de @ Aldekein pero sin 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
fuente
Si la página se desplaza hacia abajo, supongo que también se debe tener en cuenta el desplazamiento de desplazamiento del documento.
Peppe LG
8
El rectángulo de cliente delimitador @ PeppeL-G calcula eso por usted. Puede probarlo fácilmente en la consola antes de publicar un comentario (eso es lo que hice).
Tomáš Zato - Restablece a Mónica el
1
@ TomášZato, oh, ¿ getBoundingClientRectdevuelve posiciones relativas al puerto de visualización? Entonces mi suposición estaba equivocada. Nunca lo probé, ya que esto nunca fue un problema para mí, y amablemente quería advertir a otros lectores de un problema potencial que vi, pero gracias por su aclaración.
Peppe LG
1
Esto no funciona si el lienzo está escalado. No estoy seguro si eso es un error del navegador.
Jeroen
2
Agregue uso para alguien como yo:var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
SnowOnion
179

Actualización (5/5/16): la respuesta de patriques debería usarse en su lugar, ya que es más simple y más confiable.


Dado que el lienzo no siempre tiene un estilo relativo a toda la página, canvas.offsetLeft/Topno siempre devuelve lo que necesita. Devolverá el número de píxeles que está desplazado en relación con su elemento offsetParent, que puede ser algo así como un divelemento que contiene el lienzo con un position: relativeestilo aplicado. Para dar cuenta de esto, debe recorrer la cadena de offsetParents, comenzando con el elemento de lienzo en sí. Este código funciona perfectamente para mí, probado en Firefox y Safari, pero debería funcionar para todos.

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 última línea facilita las cosas para obtener las coordenadas del mouse en relación con un elemento de lienzo. Todo lo que se necesita para obtener las coordenadas útiles es

coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;
Ryan Artecona
fuente
8
No, está utilizando el objeto prototipo javascript incorporado --- developer.mozilla.org/en/…
garg
14
mi Chrome tiene event.offsetXy event.offsetYatributos, por lo que modifiqué su solución agregando if (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; }. Parece que funciona.
Baczek
3
Baczek tiene razón sobre Chrome event.offsetXy event.offsetYtambién funciona en IE9. Para Firefox (probado con v13) puede usar event.layerXy event.layerY.
mafafu
3
También agregué esto: canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
amirpc
44
Esta versión final en respuesta no funcionó para mí. En Firefox, si se desplaza toda la pantalla, obtengo el valor desplazado como salida. Lo que funcionó para mí fue una solución muy similar, dada en stackoverflow.com/a/10816667/578749 que en lugar de event.pageX / Y, restaba el desplazamiento calculado de event.clientX / Y. ¿Alguien podría revisar esto y explicar?
lvella
33

El navegador moderno ahora maneja esto por usted. Chrome, IE9 y Firefox admiten el offsetX / Y de esta manera, pasando el evento desde el controlador de clics.

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

La mayoría de los navegadores modernos también admiten layerX / Y, sin embargo, Chrome e IE usan layerX / Y para el desplazamiento absoluto del clic en la página, incluido el margen, el relleno, etc. En Firefox, layerX / Y y offsetX / Y son equivalentes, pero el offset no No existe previamente. Entonces, para compatibilidad con navegadores un poco más antiguos, puede usar:

function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}
mafafu
fuente
1
Es interesante cómo se define layerX, layerY tanto en Chrome como en Firefox, pero en Chrome es inexacto (o significa algo más).
Julian Mann
@JulianMann Gracias por la información. He actualizado esta respuesta en base a un soporte más actual. Parece que ahora puedes salirte con offsetX / Y casi universalmente.
mafafu
18

Según el nuevo Quirksmode, los métodos clientXy clientYson compatibles con todos los principales navegadores. Entonces, aquí va: el código bueno y funcional que funciona en un div de desplazamiento en una página con barras de desplazamiento:

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];
}

Esto también requiere jQuery para $(canvas).offset().

Aldekein
fuente
Equivalente a la respuesta @ N4ppeL.
Paweł Szczur
13

Hice una demostración completa que funciona en todos los navegadores con el código fuente completo de la solución de este problema: coordenadas de un clic del mouse en Canvas en Javascript . Para probar la demostración, copie el código y péguelo en un editor de texto. Luego guárdelo como example.html y, finalmente, abra el archivo con un navegador.

Manuel Ignacio López Quintero
fuente
11

Aquí hay una pequeña modificación a la respuesta de Ryan Artecona para lienzos con un ancho 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}
}
Criptovirus
fuente
7

Tenga cuidado al hacer la conversión de coordenadas; Hay varios valores que no son de navegador cruzado devueltos en un evento de clic. Usar clientX y clientY solo no son suficientes si se desplaza la ventana del navegador (verificado en Firefox 3.5 y Chrome 3.0).

Este modo peculiar artículo sobre el ofrece una función más correcta que puede usar pageX o pageY o una combinación de clientX con document.body.scrollLeft y clientY con document.body.scrollTop para calcular la coordenada de clic relativa al origen del documento.

ACTUALIZACIÓN: Además, offsetLeft y offsetTop son relativos al tamaño acolchado del elemento, no al tamaño interior. Un lienzo con el relleno: el estilo aplicado no informará la parte superior izquierda de su región de contenido como offsetLeft. Hay varias soluciones a este problema; el más simple puede ser borrar todos los estilos de borde, relleno, etc. en el lienzo y, en su lugar, aplicarlos a un cuadro que contiene el lienzo.

marca de reparación
fuente
7

No estoy seguro de cuál es el punto de todas estas respuestas que recorren los elementos principales y hacen todo tipo de cosas raras .

El HTMLElement.getBoundingClientRectmétodo está diseñado para manejar la posición real de la pantalla de cualquier elemento. Esto incluye el desplazamiento, por lo que scrollTopno se necesitan cosas como :

(de MDN) La cantidad de desplazamiento que se ha realizado en el área de la ventana gráfica (o cualquier otro elemento desplazable ) se tiene en cuenta al calcular el rectángulo delimitador

Imagen normal

El enfoque más simple ya se publicó aquí. Esto es correcto siempre que no existan reglas CSS salvajes .

Manejo de lienzo / imagen estirada

Cuando el ancho de píxel de la imagen no coincide con su ancho de CSS, deberá aplicar alguna proporción en los valores de píxel:

/* 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];
}

Mientras el lienzo no tenga borde, funciona para imágenes estiradas (jsFiddle) .

Manejo de bordes CSS

Si el lienzo tiene un borde grueso, las cosas se complican un poco . Literalmente, deberá restar el borde del rectángulo delimitador. Esto se puede hacer usando .getComputedStyle . Esta respuesta describe el proceso. .

La función luego crece un poco:

/* 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
  ...
}

No se me ocurre nada que pueda confundir esta función final. Mírate en JsFiddle .

Notas

Si no le gusta modificar los prototypes nativos , simplemente cambie la función y llámela con (canvas, event)(y reemplace cualquiera thiscon canvas).

Tomáš Zato - Restablece a Monica
fuente
6

Aquí hay un tutorial muy bueno.

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

¡espero que esto ayude!

FraZer
fuente
La solución de Ryan Artecona no funcionó para navegadores de tabletas con zoom. Sin embargo, este sí.
Andy Meyers
No funciona con imágenes que tienen CSS width/ heightanulación, pero sigue siendo una de las mejores soluciones.
Tomáš Zato - Restablece a Mónica el
3

Usando jQuery en 2016, para obtener coordenadas de clic relativas al lienzo, hago:

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

Esto funciona ya que tanto el desplazamiento del lienzo () como jqEvent.pageX / Y son relativos al documento, independientemente de la posición de desplazamiento.

Tenga en cuenta que si su lienzo está escalado, estas coordenadas no son las mismas que las coordenadas lógicas del lienzo . Para obtenerlos, también harías:

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}
Zarzaparrilla
fuente
Guau. ¿Cómo se define 'jqEvent'? O 'lienzo'? ¿O en el segundo ejemplo 'coords'? ¿Necesita ejecutar el primer ejemplo antes del segundo? En ese caso, ¿por qué no escribirías "para obtenerlos, TAMBIÉN harías"? ¿Todo esto va en una función onclick o qué? Dale un poco de contexto, amigo. Y teniendo en cuenta que la pregunta original se hizo en 2008, creo que debe responder en el contexto de la tecnología que estaba disponible en 2008. Refine su respuesta utilizando jQuery válido de la versión que estaba disponible en ese momento (v1.2). ;)
Ayelis
1
OK, perdón por mi presunción. Lo editaré para eliminar eso. Tenía la intención de proporcionar la respuesta utilizando los marcos más recientes. Y creo que un programador no necesitaría explicarse qué son jqEvent, canvas y coords.
Zarzaparrilla
Se ve bien. ¡Gracias por tu contribución! ¡Perdón por haberte hecho pasar un mal rato! ;)
Ayelis
3

Este es un tema simple pero un poco más complicado de lo que parece.

En primer lugar, generalmente hay preguntas combinadas aquí

  1. Cómo obtener las coordenadas relativas del mouse del elemento

  2. Cómo obtener coordenadas de mouse de píxeles de lienzo para la API de lienzo 2D o WebGL

entonces, respuestas

Cómo obtener las coordenadas relativas del mouse del elemento

Si el elemento es o no un lienzo que obtiene coordenadas relativas del mouse del elemento es el mismo para todos los elementos.

Hay 2 respuestas simples a la pregunta "Cómo obtener coordenadas relativas del mouse del lienzo"

Respuesta simple # 1 uso offsetXyoffsetY

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

Esta respuesta funciona en Chrome, Firefox y Safari. A diferencia de todos los demás valores de eventos offsetXy offsetYtener en cuenta las transformaciones CSS.

El mayor problema con offsetXy offsetYes a partir de 2019/05 que no existen en eventos táctiles y, por lo tanto, no se pueden usar con iOS Safari. Existen en los eventos de puntero que existen en Chrome y Firefox pero no en Safari, aunque aparentemente Safari está trabajando en ello .

Otro problema es que los eventos deben estar en el lienzo en sí. Si los coloca en otro elemento o en la ventana, no podrá elegir el lienzo como punto de referencia.

Respuesta simple # 2 uso clientX, clientYycanvas.getBoundingClientRect

Si no le importan las transformaciones CSS, la siguiente respuesta más simple es llamar canvas. getBoundingClientRect()y restar la izquierda de clientXy topde clientYcomo en

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

Esto funcionará siempre que no haya transformaciones CSS. También funciona con eventos táctiles y funcionará con 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;
});

Cómo obtener coordenadas de mouse de píxeles de lienzo para la API de lienzo 2D

Para esto, necesitamos tomar los valores que obtuvimos arriba y convertirlos del tamaño del lienzo que se muestra al número de píxeles en el lienzo mismo

con canvas.getBoundingClientRecty clientXyclientY

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

o con offsetXyoffsetY

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

Nota: en todos los casos, no agregue relleno ni bordes al lienzo. Hacerlo complicará enormemente el código. En lugar de que desee un borde o relleno, rodee el lienzo en algún otro elemento y agregue el relleno o el borde al elemento exterior.

Ejemplo de trabajo usando event.offsetX,event.offsetY

Ejemplo de trabajo usando canvas.getBoundingClientRecty event.clientXyevent.clientY

gman
fuente
2

Recomiendo este enlace: 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>

fuente
¿De qué sirve x = new Number()? El código a continuación que reasigna, lo xque significa que el Número asignado se descarta inmediatamente
gman
1

Podrías simplemente hacer:

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

Esto le dará la posición exacta del puntero del mouse.

usuario2310569
fuente
1

Vea la demostración en 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
fuente
0

Aquí hay algunas modificaciones de la solución de Ryan Artecona anterior.

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 hola
fuente
0

Primero, como han dicho otros, necesita una función para obtener la posición del elemento de lienzo . Aquí hay un método que es un poco más elegante que algunos de los otros en esta página (en mi humilde opinión). Puede pasarle cualquier elemento y obtener su posición en el documento:

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

Ahora calcule la posición actual del cursor con respecto a eso:

$('#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);
});

Tenga en cuenta que he separado la findPosfunción genérica del código de manejo de eventos. ( Como debería ser . Deberíamos tratar de mantener nuestras funciones en una tarea cada una).

Los valores de offsetLefty offsetTopson relativos a offsetParent, que podrían ser algún divnodo contenedor (o cualquier otra cosa, para el caso). Cuando no hay ningún elemento que los envuelva canvas, son relativos al body, por lo que no hay desplazamiento para restar. Es por eso que necesitamos determinar la posición del lienzo antes de poder hacer cualquier otra cosa.

De forma similar, e.pageXy e.pageYdar la posición del cursor con respecto al documento. Es por eso que restamos el desplazamiento del lienzo de esos valores para llegar a la posición verdadera.

Una alternativa para los elementos posicionados es usar directamente los valores de e.layerXy e.layerY. Esto es menos confiable que el método anterior por dos razones:

  1. Estos valores también son relativos a todo el documento cuando el evento no tiene lugar dentro de un elemento posicionado
  2. No son parte de ningún estándar.
Wayne
fuente
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;

Después de probar muchas soluciones. Esto funcionó para mí. Podría ayudar a alguien más, por lo tanto, publicar. Lo tengo desde aqui

Aniket Betkikar
fuente
0

Aquí hay una solución simplificada (esto no funciona con bordes / desplazamiento):

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
fuente
0

Estaba creando una aplicación que tenía un lienzo sobre un pdf, que involucraba muchos cambios de tamaño del lienzo, como acercar y alejar el pdf, y a su vez en cada acercamiento / alejamiento del PDF tenía que cambiar el tamaño del lienzo para adaptar el tamaño del pdf, revisé muchas respuestas en stackOverflow, y no encontré una solución perfecta que eventualmente resuelva el problema.

Estaba usando rxjs y angular 6, y no encontré ninguna respuesta específica para la versión más nueva.

Aquí está el fragmento de código completo que sería útil, para cualquiera que aproveche los rxjs para dibujar sobre el lienzo.

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

Y aquí está el fragmento que arregla, las coordenadas del mouse en relación con el tamaño del lienzo, independientemente de cómo acerque / aleje el lienzo.

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
fuente
-1

Oye, esto está en el dojo, solo porque es lo que ya tenía el código para un proyecto.

Debería ser bastante obvio cómo convertirlo de nuevo a JavaScript no dojo vanilla.

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

Espero que ayude.

Brian Gianforcaro
fuente
66
clientX / clientY no se comportan de manera similar en todos los navegadores.
tfwright