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

StackOverflow https://stackoverflow.com/questions/55677

  •  09-06-2019
  •  | 
  •  

Pregunta

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

No se requiere compatibilidad con navegadores antiguos; Safari, Opera y Firefox son suficientes.

¿Fue útil?

Solución

Editar 2018: Esta respuesta es bastante antigua y utiliza comprobaciones de navegadores antiguos que ya no son necesarios, como el clientX y clientY Las propiedades funcionan en todos los navegadores actuales.Quizás quieras echar un vistazo Patriques Respuesta para una solución más simple y más reciente.

Respuesta original:
Como se describe en un artículo que encontré en aquel entonces pero que 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í.

Otros consejos

Actualizar (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, el canvas.offsetLeft/Top no siempre devuelve lo que necesitas.Devolverá el número de píxeles que está desplazado en relación con su elemento offsetParent, que puede ser algo así como un div elemento que contiene el lienzo con un position: relative estilo aplicado.Para tener en cuenta esto es necesario recorrer la cadena de offsetParents, comenzando con el propio elemento del lienzo.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 hace que sea conveniente obtener las coordenadas del mouse en relación con un elemento del lienzo.Todo lo que se necesita para obtener las coordenadas útiles es

coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;

Si le gusta la simplicidad pero aún desea la funcionalidad entre navegadores, 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) {
    var rect = canvas.getBoundingClientRect();
    var x = event.clientX - rect.left;
    var y = event.clientY - rect.top;
    console.log("x: " + x + " y: " + y);
}

Los navegadores modernos ahora manejan esto por usted.Chrome, IE9 y Firefox admiten offsetX/Y de esta manera, pasando el evento desde el controlador de clic.

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 desplazamiento no existía anteriormente.Entonces, para compatibilidad con navegadores un poco más antiguos, puedes usar:

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

Según fresco En modo capricho el clientX y clientY Los métodos son compatibles con todos los principales navegadores.Entonces, aquí va: el buen código 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().

Hice una demostración completa que funciona en todos los navegadores con el código fuente completo de la solución a 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 ejemplo.html y, finalmente, abra el archivo con un navegador.

Aquí hay una pequeña modificación para 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}
}

Tenga cuidado al realizar la conversión de coordenadas;hay varios valores que no son de varios navegadores devueltos en un evento de clic.Usar clientX y clientY por sí solos no es suficiente si se desplaza la ventana del navegador (verificado en Firefox 3.5 y Chrome 3.0).

Este modo peculiar El artículo proporciona 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.

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

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

El HTMLElement.getBoundingClientRect El método está diseñado para manejar la posición real en la pantalla de cualquier elemento.Esto incluye el desplazamiento, por lo que cosas como scrollTop no es necesario:

(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 muy simple ya fue publicado aquí.Esto es correcto siempre y cuando sin CSS salvaje reglas están involucradas.

Manejo de lienzo/imagen estirada

Cuando el ancho de píxel de la imagen no coincide con el 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 necesitarás restar el borde del rectángulo delimitador.Esto se puede hacer usando .getComputedStyle.Este la respuesta describe el proceso.

La función entonces 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 a ti mismo en JsFiddle.

Notas

Si no te gusta modificar el nativo prototypes, simplemente cambia la función y llámala con (canvas, event) (y reemplazar cualquier this con canvas).

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!

Usando jQuery en 2016, para obtener las 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 canvas offset() 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 del lienzo. coordenadas lógicas.Para conseguirlos, tendrías que también hacer:

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}

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>

En Prototype, use cumulativeOffset() para realizar la suma recursiva como lo mencionó Ryan Artecona anteriormente.

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

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.

Ver demostración en http://jsbin.com/ApuJOSA/1/edit?html,salida .

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

Así que este es un tema simple pero un poco más complicado de lo que parece.

En primer lugar, aquí suelen haber preguntas combinadas.

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

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

entonces, respuestas

Cómo obtener las coordenadas relativas del mouse del elemento

Si el elemento es o no un lienzo, las coordenadas relativas del mouse del elemento son las mismas para todos los elementos.

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

Respuesta simple #1 uso offsetX y offsetY

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 offsetX y offsetY tenga en cuenta las transformaciones CSS.

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

Otra cuestión es que los acontecimientos deben estar en el propio lienzo.Si los pones sobre algún otro elemento o la ventana no podrás luego elegir el lienzo como punto de referencia.

Respuesta simple #2 uso clientX, clientY y canvas.getBoundingClientRect

Si no le importan las transformaciones CSS, la siguiente respuesta más simple es llamar canvas. getBoundingClientRect() y restar la izquierda de clientX y top de clientY como 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 también 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 las coordenadas del mouse del píxel del lienzo para la API 2D Canvas

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

con canvas.getBoundingClientRect y clientX y clientY

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 offsetX y offsetY

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 querer un borde o relleno, rodee el lienzo con algún otro elemento y agregue el relleno o el borde al elemento exterior.

Ejemplo de trabajo usando event.offsetX, event.offsetY

[...document.querySelectorAll('canvas')].forEach((canvas) => {
  const ctx = canvas.getContext('2d');
  ctx.canvas.width  = ctx.canvas.clientWidth;
  ctx.canvas.height = ctx.canvas.clientHeight;
  let count = 0;

  function draw(e, radius = 1) {
    const pos = {
      x: e.offsetX * canvas.width  / canvas.clientWidth,
      y: e.offsetY * canvas.height / canvas.clientHeight,
    };
    document.querySelector('#debug').textContent = count;
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
    ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
    ctx.fill();
  }

  function preventDefault(e) {
    e.preventDefault();
  }

  if (window.PointerEvent) {
    canvas.addEventListener('pointermove', (e) => {
      draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
    });
    canvas.addEventListener('touchstart', preventDefault, {passive: false});
    canvas.addEventListener('touchmove', preventDefault, {passive: false});
  } else {
    canvas.addEventListener('mousemove', draw);
    canvas.addEventListener('mousedown', preventDefault);
  }
});

function hsl(h, s, l) {
  return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
.scene {
  width: 200px;
  height: 200px;
  perspective: 600px;
}

.cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  animation-duration: 16s;
  animation-name: rotate;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes rotate {
  from { transform: translateZ(-100px) rotateX(  0deg) rotateY(  0deg); }
  to   { transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); }
}

.cube__face {
  position: absolute;
  width: 200px;
  height: 200px;
  display: block;
}

.cube__face--front  { background: rgba(255, 0, 0, 0.2); transform: rotateY(  0deg) translateZ(100px); }
.cube__face--right  { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); }
.cube__face--back   { background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); }
.cube__face--left   { background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); }
.cube__face--top    { background: rgba(0, 255, 255, 0.2); transform: rotateX( 90deg) translateZ(100px); }
.cube__face--bottom { background: rgba(255, 0, 255, 0.2); transform: rotateX(-90deg) translateZ(100px); }
<div class="scene">
  <div class="cube">
    <canvas class="cube__face cube__face--front"></canvas>
    <canvas class="cube__face cube__face--back"></canvas>
    <canvas class="cube__face cube__face--right"></canvas>
    <canvas class="cube__face cube__face--left"></canvas>
    <canvas class="cube__face cube__face--top"></canvas>
    <canvas class="cube__face cube__face--bottom"></canvas>
  </div>
</div>
<pre id="debug"></pre>

Ejemplo de trabajo usando canvas.getBoundingClientRect y event.clientX y event.clientY

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.canvas.width  = ctx.canvas.clientWidth;
ctx.canvas.height = ctx.canvas.clientHeight;
let count = 0;

function draw(e, radius = 1) {
  const rect = canvas.getBoundingClientRect();
  const pos = {
    x: (e.clientX - rect.left) * canvas.width  / canvas.clientWidth,
    y: (e.clientY - rect.top) * canvas.height / canvas.clientHeight,
  };
  ctx.beginPath();
  ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
  ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
  ctx.fill();
}

function preventDefault(e) {
  e.preventDefault();
}

if (window.PointerEvent) {
  canvas.addEventListener('pointermove', (e) => {
    draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
  });
  canvas.addEventListener('touchstart', preventDefault, {passive: false});
  canvas.addEventListener('touchmove', preventDefault, {passive: false});
} else {
  canvas.addEventListener('mousemove', draw);
  canvas.addEventListener('mousedown', preventDefault);
}

function hsl(h, s, l) {
  return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
canvas { background: #FED; }
<canvas width="400" height="100" style="width: 300px; height: 200px"></canvas>
<div>canvas deliberately has differnt CSS size vs drawingbuffer size</div>

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

Debería ser bastante obvio cómo convertirlo nuevamente 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.

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

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

Primero, como han dicho otros, necesitas una función para obtener el posición del elemento del 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).puedes pasarlo 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 en relación con 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);
});

Note que he separado el genérico. findPos función del código de manejo de eventos.(Como debería ser.Deberíamos intentar mantener nuestras funciones en una tarea cada una).

los valores de offsetLeft y offsetTop son relativos a offsetParent, que podría ser algún envoltorio div nodo (o cualquier otra cosa, para el caso).Cuando no hay ningún elemento que envuelva el canvas son relativos al body, por lo que no hay compensación que restar.Es por eso que necesitamos determinar la posición del lienzo antes de poder hacer cualquier otra cosa.

De manera similar, e.pageX y e.pageY proporcione la posición del cursor en relación con el documento.Es por eso que restamos el desplazamiento del lienzo de esos valores para llegar a la posición verdadera.

Una alternativa para posicionado elementos es utilizar directamente los valores de e.layerX y e.layerY.Este 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 forman parte de ningún estándar.

TresJS 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 de aquí

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

Estaba creando una aplicación que tenía un lienzo sobre un pdf, eso implicó muchos cambios de tamaño del lienzo, como acercar y alejar el pdf, y a su vez, en cada acercamiento/alejamiento del PDF tuve 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.

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

Aquí está el fragmento de código completo que sería útil para cualquiera que aproveche 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 corrige las coordenadas del mouse en relación con el tamaño del lienzo, independientemente de cómo acerca o aleja 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 )
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top