web-dev-qa-db-fra.com

Position réelle de la souris dans la toile

J'essaie de dessiner avec la souris sur un canevas HTML5, mais la seule manière dont cela semble bien fonctionner est que le canevas se trouve dans la position 0,0 (coin supérieur gauche) si je change de position, pour une raison quelconque cela ne dessine pas comme il se doit. 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 existe un moyen de créer une fonction simple en JavaScript pour obtenir le bon poste, mais je ne sais pas comment le faire.

74
Solar Confinement

Le scénario simple 1: 1

Pour les situations dans lesquelles l'élément canvas est 1: 1 par rapport à la taille du bitmap, vous pouvez obtenir les positions de la souris à l'aide de cet extrait:

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

Appelez-le simplement à partir de votre événement avec l'événement et le canevas comme arguments. Il retourne un objet avec x et y pour les positions de la souris.

Comme la position de la souris que vous obtenez est relative par rapport à la fenêtre du client, vous devrez soustraire la position de l’élément de la zone de dessin pour le convertir par rapport à 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);
}

Violon avec modifications

Remarque: les bordures et les marges de remplissage affecteront la position si elles sont appliquées directement à l'élément de canevas; elles doivent donc être prises en compte via getComputedStyle() - ou d'appliquer ces styles à un div parent.

Quand Element et Bitmap ont des tailles différentes

Lorsque l'élément a une taille différente de celle du bitmap lui-même, par exemple, l'élément est mis à l'échelle à l'aide de CSS ou il existe un rapport hauteur/largeur en pixels, etc., vous devrez le résoudre.

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

Violon utilisant une gamme

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, telle que rotation, inclinaison/cisaillement, échelle, translation, etc. Pour résoudre ce problème, vous pouvez calculer la matrice inverse de la matrice actuelle.

Les nouveaux navigateurs vous permettent de lire la matrice actuelle via la propriété currentTransform et Firefox (alpha actuel) fournit même une matrice inversée via le mozCurrentTransformInverted. Cependant, Firefox, via mozCurrentTransform, renverra un tableau et non pas DOMMatrix comme il se doit. Ni Chrome, lorsqu'il est activé via des indicateurs expérimentaux, renverra un DOMMatrix mais un SVGMatrix.

Dans la plupart des cas, vous devrez toutefois implémenter votre propre solution matricielle (telle que ma propre solution ici - projet libre/MIT) jusqu'à ce que celui-ci reçoive une assistance complète.

Lorsque vous avez finalement obtenu la matrice, quel que soit le chemin que vous prenez pour en obtenir une, vous devez l’inverser et l’appliquer aux coordonnées de votre souris. Les coordonnées sont ensuite transmises au canevas qui utilisera sa matrice pour le convertir en arrière, où qu’il se trouve.

De cette façon, le point sera dans la position correcte par rapport à la souris. Ici aussi, vous devez ajuster les coordonnées (avant d’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);
}

Exemple d'utilisation de la solution liée ci-dessus (à remplacer par la solution de navigateur natif lorsqu'elle est plus largement prise en charge).

Un exemple d'utilisation de currentTransform lors de son implémentation serait:

    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 libre (MIT) pour incorporer toutes ces étapes dans un seul objet facile à utiliser qui peut être trouvé ici et s’occupe également de quelques autres choses importantes que la plupart ignorent.

168
user1693593

Vous pouvez obtenir les positions de la souris en utilisant cet extrait:

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 modification des coordonnées en espace de la zone de dessin (evt.clientX - rect.left) Et la mise à l'échelle lorsque la taille logique de la zone de dessin diffère de la taille de son style (/ (rect.right - rect.left) * canvas.width voir: largeur et hauteur de la toile en HTML5). ).

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

Source: jerryj comment on http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/

24
Rafał S

Vous devez obtenir la position de la souris par rapport au canevas

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

Ceci s’appelle le "décalage" de la toile et voici comment obtenir le décalage. (J'utilise jQuery afin de simplifier la compatibilité entre navigateurs, mais si vous souhaitez utiliser du javascript brut, un outil rapide de Google le comprendra également).

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

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

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

Voici un code illustrant le violon qui montre comment suivre avec succès les événements de 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>
5
markE

Le moyen le plus simple de calculer le clic ou le déplacement correct de la souris sur un événement de type canvas consiste à 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 ou padding-top, soustrayez x et y via:

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

3
Martin Wantke

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

3
sumitb.mdi