162 votes

Position réelle de la souris dans le canevas

J'essaie de dessiner avec la souris sur un canevas HTML5, mais la seule façon dont cela semble fonctionner correctement est si le canevas est dans la position 0,0 (coin supérieur gauche) si je change la position du canevas, pour une raison quelconque, il ne dessine pas comme il le devrait. Voici mon code.

 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 partie HTML

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

J'ai lu qu'il était possible de créer une fonction simple en JavaScript pour obtenir la bonne position, mais je n'ai aucune idée de la façon de procéder.

330voto

Le scénario simple 1:1

Pour les situations où l'élément de toile est 1:1 par rapport à la taille du bitmap, vous pouvez obtenir les positions de la souris en utilisant ce snippet :

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

Il suffit de l'appeler depuis votre événement avec l'événement et le canevas comme arguments. Il retourne un objet avec x y y pour les positions de la souris.

Comme la position de la souris que vous obtenez est relative à la fenêtre du client, vous devrez soustraire la position de l'élément du canevas pour la convertir en position relative à l'élément lui-même.

Exemple d'intégration dans votre code :

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

Note : Les bordures et le rembourrage affecteront la position s'ils sont appliqués directement à l'élément du canevas. getComputedStyle() - ou d'appliquer ces styles à une div parent à la place.

Lorsque l'élément et le bitmap sont de tailles différentes

Lorsque la taille de l'élément est différente de celle de l'image bitmap, par exemple si l'élément est mis à l'échelle à l'aide d'une feuille de style CSS ou s'il existe un rapport pixel-aspect, etc.

Exemple :

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

Avec des transformations appliquées au contexte (échelle, rotation, etc.)

Ensuite, il y a le cas plus compliqué où vous avez appliqué une transformation au contexte, comme une rotation, une inclinaison/un cisaillement, une mise à l'échelle, une translation, etc. Pour traiter ce cas, vous pouvez calculer la matrice inverse de la matrice actuelle.

Les navigateurs les plus récents vous permettent de lire la matrice actuelle via la fonction currentTransform et Firefox (alpha actuel) fournit même une matrice inversée grâce à la propriété mozCurrentTransformInverted . Firefox cependant, via mozCurrentTransform retournera un tableau et non DOMMatrix comme il se doit. Ni Chrome, lorsqu'il est activé par des drapeaux expérimentaux, ne renverra un message de type DOMMatrix mais un SVGMatrix .

Dans la plupart des cas, cependant, vous devrez mettre en œuvre une solution matricielle personnalisée (comme ma propre solution). aquí - projet free/MIT) jusqu'à ce qu'il obtienne un soutien total.

Lorsque vous aurez finalement obtenu la matrice, quel que soit le chemin emprunté pour l'obtenir, vous devrez l'inverser et l'appliquer aux coordonnées de votre souris. Les coordonnées sont ensuite transmises au canevas qui utilisera sa matrice pour les reconvertir où qu'elles se trouvent actuellement.

De cette façon, le point sera dans la position correcte par rapport à la souris. Ici aussi, vous devez ajuster les coordonnées (avant de leur appliquer la matrice inverse) pour qu'elles soient relatives à l'élément.

Un exemple montrant simplement les étapes de la matrice :

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 exemple d'utilisation currentTransform lorsqu'elle sera mise en œuvre, le sera :

  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;

Mise à jour : J'ai créé une solution gratuite (MIT) pour intégrer toutes ces étapes dans un seul objet facile à utiliser, que l'on peut trouver à l'adresse suivante aquí et s'occupe également de quelques autres détails que la plupart des gens ignorent.

63voto

Rafał S Points 683

Vous pouvez obtenir les positions de la souris en utilisant ce snippet :

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

Ce code prend en compte à la fois le changement de coordonnées dans l'espace du canevas ( evt.clientX - rect.left ) et la mise à l'échelle lorsque la taille logique du canevas diffère de sa taille de style ( / (rect.right - rect.left) * canvas.width voir : Largeur et hauteur du canevas en HTML5 ).

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

Source : commentaire de jerryj sur http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/

9voto

markE Points 56623

Vous devez obtenir la position de la souris par rapport à la toile

Pour ce faire, vous devez connaître la position X/Y du canevas sur la page.

C'est ce qu'on appelle le "décalage" du canevas, et voici comment obtenir ce décalage. (J'utilise jQuery pour simplifier la compatibilité entre les navigateurs, mais si vous voulez utiliser du javascript brut, une recherche rapide sur Google vous permettra de le faire).

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

Ensuite, dans votre gestionnaire de souris, vous pouvez obtenir le X/Y de la souris comme ceci :

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

Voici un code d'illustration et un bidouillage qui montre comment suivre avec succès les événements de la souris sur le canevas :

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

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="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 + " / " + mouseY);

      // Put your mousedown stuff here

    }

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

      // Put your mouseup stuff here
    }

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

      // Put your mouseOut stuff here
    }

    function handleMouseMove(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
      $("#movelog").html("Move: "+ mouseX + " / " + 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>

6voto

Martin Wantke Points 26

La façon la plus simple de calculer la position correcte d'un clic ou d'un déplacement de souris sur un événement de toile est d'utiliser cette petite équation :

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 le canevas a padding-left o padding-top soustraire x et y via :

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

3voto

sumitb.mdi Points 769

Référez-vous à cette question : La valeur de mouseEvent.offsetX que je reçois est beaucoup plus grande que la taille réelle de la toile. J'ai donné une fonction qui conviendra parfaitement à votre situation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X