Posición real del mouse en el lienzo

10 minutos de lectura

Posicion real del mouse en el lienzo
Confinamiento Solar

Estoy tratando de dibujar con el mouse sobre un lienzo HTML5, pero la única forma en que parece funcionar bien es si el lienzo está en la posición 0,0 (esquina superior izquierda) si cambio la posición del lienzo, por alguna razón. no dibuja como debería. Aquí está mi código.

 function createImageOnCanvas(imageId){
    document.getElementById("imgCanvas").style.display = "block";
    document.getElementById("images").style.overflowY= "hidden";
    var canvas = document.getElementById("imgCanvas");
    var context = canvas.getContext("2d");
    var img = new Image(300,300);
    img.src = document.getElementById(imageId).src;
    context.drawImage(img, (0),(0));
}

function draw(e){
    var canvas = document.getElementById("imgCanvas");
    var context = canvas.getContext("2d");
    posx = e.clientX;
    posy = e.clientY;
    context.fillStyle = "#000000";
    context.fillRect (posx, posy, 4, 4);
}

La parte HTML

 <body>
 <div id="images">
 </div>
 <canvas onmousemove="draw(event)" style="margin:0;padding:0;" id="imgCanvas"
          class="canvasView" width="250" height="250"></canvas> 

He leído que hay una manera de crear una función simple en JavaScript para obtener la posición correcta, pero no tengo idea de cómo hacerlo.

El escenario simple 1:1

Para situaciones en las que el elemento del lienzo es 1:1 en comparación con el tamaño del mapa de bits, puede obtener las posiciones del mouse utilizando este fragmento:

function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
}

Simplemente llámelo desde su evento con el evento y el lienzo como argumentos. Devuelve un objeto con x e y para las posiciones del mouse.

Como la posición del mouse que está obteniendo es relativa a la ventana del cliente, tendrá que restar la posición del elemento del lienzo para convertirlo en relación con el elemento en sí.

Ejemplo de integración en tu código:

//put this outside the event loop..
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");

function draw(evt) {
    var pos = getMousePos(canvas, evt);

    context.fillStyle = "#000000";
    context.fillRect (pos.x, pos.y, 4, 4);
}

Nota: los bordes y el relleno afectarán la posición si se aplican directamente al elemento del lienzo, por lo que deben considerarse a través de getComputedStyle() – o aplique esos estilos a un div principal en su lugar.

Cuando el elemento y el mapa de bits son de diferentes tamaños

Cuando se presente la situación de tener el elemento en un tamaño diferente al del propio mapa de bits, por ejemplo, el elemento se escala mediante CSS o existe una relación de aspecto de píxeles, etc., tendrá que solucionar este problema.

Ejemplo:

function  getMousePos(canvas, evt) {
  var rect = canvas.getBoundingClientRect(), // abs. size of element
      scaleX = canvas.width / rect.width,    // relationship bitmap vs. element for X
      scaleY = canvas.height / rect.height;  // relationship bitmap vs. element for Y

  return {
    x: (evt.clientX - rect.left) * scaleX,   // scale mouse coordinates after they have
    y: (evt.clientY - rect.top) * scaleY     // been adjusted to be relative to element
  }
}

Con transformaciones aplicadas al contexto (escala, rotación, etc.)

Luego está el caso más complicado en el que ha aplicado transformación al contexto, como rotación, sesgo/corte, escala, traslación, etc. Para lidiar con esto, puede calcular la matriz inversa de la matriz actual.

Los navegadores más nuevos le permiten leer la matriz actual a través de la currentTransform propiedad y Firefox (alfa actual) incluso proporcionan una matriz invertida a través de la mozCurrentTransformInverted. Firefox sin embargo, a través de mozCurrentTransformdevolverá un Array y no DOMMatrix como debería. Ni Chrome, cuando está habilitado a través de banderas experimentales, devolverá un DOMMatrix pero un SVGMatrix.

En la mayoría de los casos, sin embargo, tendrá que implementar una solución de matriz personalizada propia (como mi propia solución aquí – proyecto gratuito/MIT) hasta que obtenga soporte completo.

Cuando finalmente haya obtenido la matriz, independientemente del camino que tome para obtener una, deberá invertirla y aplicarla a las coordenadas de su mouse. Luego, las coordenadas se pasan al lienzo, que usará su matriz para convertirlo donde sea que esté en este momento.

De esta manera, el punto estará en la posición correcta en relación con el mouse. También aquí debe ajustar las coordenadas (antes de aplicarles la matriz inversa) para que sean relativas al elemento.

Un ejemplo que solo muestra los pasos de la matriz.

function draw(evt) {
    var pos = getMousePos(canvas, evt);        // get adjusted coordinates as above
    var imatrix = matrix.inverse();            // get inverted matrix somehow
    pos = imatrix.applyToPoint(pos.x, pos.y);  // apply to adjusted coordinate

    context.fillStyle = "#000000";
    context.fillRect(pos.x-1, pos.y-1, 2, 2);
}

Un ejemplo de uso currentTransform cuando se implemente sería:

    var pos = getMousePos(canvas, e);          // get adjusted coordinates as above
    var matrix = ctx.currentTransform;         // W3C (future)
    var imatrix = matrix.invertSelf();         // invert

    // apply to point:
    var x = pos.x * imatrix.a + pos.y * imatrix.c + imatrix.e;
    var y = pos.x * imatrix.b + pos.y * imatrix.d + imatrix.f;

Actualizar Creé una solución gratuita (MIT) para integrar todos estos pasos en un solo objeto fácil de usar que se puede encontrar aquí y también se ocupa de algunas otras cosas esenciales que la mayoría ignora.

  • getBoundingRectangle() proporciona coordenadas más exactas que canvas.getOffset();

    – pobreva

    30 de enero de 2014 a las 6:49

  • Um, estas coordenadas no son perfectas. Obtengo valores incorrectos en el teléfono móvil. ¿Alguien sabe una solución o dónde buscar?

    – Azul amargo

    1 de julio de 2015 a las 10:09

  • @Bitterblue para corregir, verifique la pequeña modificación que proporcioné aquí. Tiene en cuenta que el tamaño del lienzo puede diferir del tamaño del estilo, lo que puede ser un problema.

    – Rafael S.

    15 de junio de 2016 a las 20:13

  • Actualicé la respuesta para abordar los problemas de escalado.

    usuario1693593

    15/06/2016 a las 20:51

  • Esta publicación fue muy útil para mí. Creé un JSFiddle que lo reúne todo: jsfiddle.net/mattdeeds/yqLvza57/37

    – MattD

    28 de octubre de 2020 a las 16:26


Posicion real del mouse en el lienzo
Rafael S

Puede obtener las posiciones del mouse usando este fragmento:

function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: (evt.clientX - rect.left) / (rect.right - rect.left) * canvas.width,
        y: (evt.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
    };
}

Este código tiene en cuenta tanto el cambio de coordenadas al espacio del lienzo (evt.clientX - rect.left) y escalar cuando el tamaño lógico del lienzo difiere de su tamaño de estilo (/ (rect.right - rect.left) * canvas.width ver: Ancho y alto del lienzo en HTML5).

Ejemplo: http://jsfiddle.net/sierawski/4xezb7nL/

Fuente: comentario de jerryj en http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/

  • Tuve que usar esta versión, ya que el tamaño de mi lienzo cambió al escalar y/o cambiar el tamaño de la ventana. Ya había resuelto el problema escalando con el ancho y alto calculados usando getComputedStyle(), pero esta solución es más elegante. Recomendaría esta respuesta a aquellos que quieren una solución más general.

    – osolmaz

    21 de diciembre de 2016 a las 8:57

  • Asume que no hay borde en el lienzo (si lo hay, es un PITA, así que es mejor no poner bordes en los lienzos)

    – gman

    11 de abril de 2017 a las 14:22

  • @Rafal S: Yo uso var bRect = theCanvas.getBoundingClientRect(); mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width); mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height); En el navegador Chrome, IE en PC, Safari en iOS está bien, pero en Chrome Web, Android está muy equivocado. ¿Puedo arreglarlo con tu código?

    – Adán

    10 de agosto de 2017 a las 1:54


  • Esta es una gran solución, pero pensé que podría agregar una solución para desplazarse por las páginas con un lienzo. Es posible que esto no funcione en todas las situaciones, pero funcionó para mi proyecto: y: ((event.pageY - rect.top) / (rect.bottom - rect.top) * canvas.height) - window.scrollY

    – Forseth11

    10 de febrero de 2020 a las 2:45

  • Muchas gracias por esto.

    – Arshad

    25 de abril de 2020 a las 20:34

Necesitas obtener la posición del mouse. en relación con el lienzo

Para hacerlo, necesita conocer la posición X/Y del lienzo en la página.

Esto se llama el “desplazamiento” del lienzo, y aquí se explica cómo obtener el desplazamiento. (Estoy usando jQuery para simplificar la compatibilidad entre navegadores, pero si desea usar javascript sin procesar, Google también lo obtendrá).

    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

Luego, en el controlador de su mouse, puede obtener el mouse X/Y de esta manera:

  function handleMouseDown(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
}

Aquí hay un código ilustrativo y un violín que muestra cómo rastrear con éxito los eventos del mouse en el lienzo:

http://jsfiddle.net/m1erickson/WB7Zu/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="https://stackoverflow.com/questions/17130395/css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    function handleMouseDown(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      $("#downlog").html("Down: "+ mouseX + "https://stackoverflow.com/" + mouseY);

      // Put your mousedown stuff here

    }

    function handleMouseUp(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      $("#uplog").html("Up: "+ mouseX + "https://stackoverflow.com/" + mouseY);

      // Put your mouseup stuff here
    }

    function handleMouseOut(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      $("#outlog").html("Out: "+ mouseX + "https://stackoverflow.com/" + mouseY);

      // Put your mouseOut stuff here
    }

    function handleMouseMove(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      $("#movelog").html("Move: "+ mouseX + "https://stackoverflow.com/" + mouseY);

      // Put your mousemove stuff here

    }

    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});

}); // end $(function(){});
</script>

</head>

<body>
    <p>Move, press and release the mouse</p>
    <p id="downlog">Down</p>
    <p id="movelog">Move</p>
    <p id="uplog">Up</p>
    <p id="outlog">Out</p>
    <canvas id="canvas" width=300 height=300></canvas>

</body>
</html>

  • En lugar de $(“#canvas”).offset(); y luego tomando izquierda y arriba estaba usando $(“#canvas”).offsetLeft , ¡así que la posición relativa no era la correcta!

    – pobreva

    28 de enero de 2014 a las 10:57

La forma más fácil de calcular el clic correcto del mouse o la posición del movimiento del mouse en un evento de lienzo es usar esta pequeña ecuación:

canvas.addEventListener('click', event =>
{
    let bound = canvas.getBoundingClientRect();

    let x = event.clientX - bound.left - canvas.clientLeft;
    let y = event.clientY - bound.top - canvas.clientTop;

    context.fillRect(x, y, 16, 16);
});

Si el lienzo tiene relleno-izquierda o acolchado superiorresta x e y a través de:

x -= parseFloat(style['padding-left'].replace('px'));
y -= parseFloat(style['padding-top'].replace('px'));

Posicion real del mouse en el lienzo
sumitb.mdi

Consulte esta pregunta: El mouseEvent.offsetX que obtengo es mucho más grande que el tamaño real del lienzo. He dado una función allí que se adaptará exactamente a su situación

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad