Wie erhalte ich die Koordinaten eines Mausklicks auf ein Canvas-Element?


276

Was ist der einfachste Weg, einem Canvas-Element einen Click-Event-Handler hinzuzufügen, der die x- und y-Koordinaten des Klicks zurückgibt (relativ zum Canvas-Element)?

Safari, Opera und Firefox sind nicht erforderlich.


Dies sollte sich nicht vom Abrufen von Mausereignissen von normalen dom-Elementen unterscheiden. Quirksmode hat eine gute Referenz dazu.
Airportyh

4
Der oben aufgeführte Code funktioniert nur, wenn sich die Zeichenfläche nicht tief in anderen Containern befindet. Im Allgemeinen müssen Sie so etwas wie die jquery-Offset-Funktion verwenden [var testDiv = $ ('# testDiv'); var offset = testDiv.offset ();], um den korrekten Offset browserübergreifend zu erhalten. Dies ist ein echter Schmerz in der ***.
Aaron Watters

Der oben mit Update veröffentlichte Code funktioniert nicht, wenn die Seite mit der Zeichenfläche gescrollt wird.
Timothy Kim

Ich habe meine alte "Antwort" entfernt, die als Update für die Frage enthalten war. Wie bereits erwähnt, war es veraltet und unvollständig.
Tom

3
Da es hier ungefähr 50 Antworten gibt, empfehle ich, zu dieser Antwort zu scrollen: patriques - ein guter und einfacher 5-Liner.
Igor L.

Antworten:


77

Edit 2018: Diese Antwort ist ziemlich alt und verwendet Überprüfungen für alte Browser, die nicht mehr benötigt werden, da die Eigenschaften clientXund clientYin allen aktuellen Browsern funktionieren. Vielleicht möchten Sie Patriques Answer für eine einfachere, neuere Lösung überprüfen .

Ursprüngliche Antwort:
Wie in einem Artikel beschrieben, den ich damals gefunden habe, aber nicht mehr existiert:

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;

Hat für mich perfekt funktioniert.


30
Diese Lösung funktioniert nicht immer - die Antwort von Ryan Artecona funktioniert im allgemeineren Fall, wenn die Leinwand nicht unbedingt relativ zur gesamten Seite positioniert ist.
Chris Johnson

3
Diese Lösung ist nicht geeignet, wenn die Leistung von Bedeutung ist, da der Dom-Zugriff bei jedem Klick / jeder Bewegung erfolgt. Und getBoundingClientRect existiert jetzt und ist eleganter.
GameAlchemist

192

Wenn Sie Einfachheit mögen, aber dennoch browserübergreifende Funktionen wünschen, hat diese Lösung für mich am besten funktioniert. Dies ist eine Vereinfachung der @ Aldekein-Lösung, jedoch ohne 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)
})

Wenn die Seite nach unten gescrollt wird, sollte man auch den Bildlaufversatz des Dokuments berücksichtigen.
Peppe LG

8
@ PeppeL-G Bounding Client-Rechteck berechnet das für Sie. Sie können es einfach in der Konsole testen, bevor Sie einen Kommentar veröffentlichen (das habe ich getan).
Tomáš Zato - Wiedereinsetzung Monica

1
@ TomášZato, oh, getBoundingClientRectgibt Positionen relativ zum Ansichtsfenster zurück? Dann war meine Vermutung falsch. Ich habe es nie getestet, da dies für mich nie ein Problem war, und ich wollte andere Leser freundlich vor einem potenziellen Problem warnen, das ich gesehen habe, aber ich danke Ihnen für Ihre Klarstellung.
Peppe LG

1
Dies funktioniert nicht, wenn die Zeichenfläche skaliert ist. Ich bin mir nicht sicher, ob das ein Browser-Fehler ist.
Jeroen

2
Fügen Sie Verwendung für jemanden wie mich hinzu:var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
SnowOnion

179

Update (05.05.16): Die Antwort von patriques sollte stattdessen verwendet werden, da sie sowohl einfacher als auch zuverlässiger ist.


Da die Leinwand nicht immer relativ zur gesamten Seite gestaltet ist, canvas.offsetLeft/Topgibt sie nicht immer das zurück, was Sie benötigen. Es wird die Anzahl der Pixel zurückgegeben, um die es relativ zu seinem offsetParent-Element versetzt ist. Dies kann so etwas wie ein divElement sein, das die Zeichenfläche mit einem position: relativeangewendeten Stil enthält. Um dies zu berücksichtigen, müssen Sie die Kette von offsetParents durchlaufen , beginnend mit dem Canvas-Element selbst. Dieser Code funktioniert perfekt für mich, wurde in Firefox und Safari getestet, sollte aber für alle funktionieren.

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;

Die letzte Zeile erleichtert das Abrufen der Mauskoordinaten relativ zu einem Canvas-Element. Alles, was benötigt wird, um die nützlichen Koordinaten zu erhalten, ist

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

8
Nein, es wird das eingebaute Javascript-Prototypobjekt verwendet --- developer.mozilla.org/en/…
garg

14
Mein Chrome hat event.offsetXund event.offsetYAttribute, daher habe ich Ihre Lösung durch Hinzufügen geändert if (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; }. sieht aus wie es funktioniert.
Baczek

3
Baczek hat Recht mit Chrome event.offsetXund event.offsetYdas funktioniert auch in IE9. Für Firefox (getestet mit v13) können Sie event.layerXund verwenden event.layerY.
Mafafu

3
Ich fügte zusätzlich hinzu: canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
amirpc

4
Diese endgültige Version auf Antwort hat bei mir nicht funktioniert. Wenn in Firefox der gesamte Bildschirm gescrollt wird, wird der verschobene Wert als Ausgabe angezeigt. Was für mich funktionierte, war eine sehr ähnliche Lösung, die auf stackoverflow.com/a/10816667/578749 angegeben wurde und anstelle von event.pageX / Y den berechneten Offset von event.clientX / Y subtrahierte. Könnte jemand dies überprüfen und erklären?
lvella

33

Moderne Browser erledigen dies jetzt für Sie. Chrome, IE9 und Firefox unterstützen das OffsetX / Y wie folgt und übergeben das Ereignis vom Click-Handler.

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

Die meisten modernen Browser unterstützen auch LayerX / Y, Chrome und IE verwenden jedoch LayerX / Y für den absoluten Versatz des Klicks auf die Seite, einschließlich Rand, Auffüllung usw. In Firefox sind LayerX / Y und OffsetX / Y gleichwertig, der Offset jedoch nicht existiert bisher nicht. Aus Gründen der Kompatibilität mit etwas älteren Browsern können Sie Folgendes verwenden:

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

1
Interessant, wie LayerX, LayerY sowohl in Chrome als auch in Firefox definiert ist, aber in Chrome ist es ungenau (oder bedeutet etwas anderes).
Julian Mann

@ JulianMann Danke für die Info. Ich habe diese Antwort basierend auf aktuellerer Unterstützung aktualisiert. Sieht so aus, als könnten Sie mit OffsetX / Y jetzt fast universell davonkommen.
Mafafu

18

Nach frischer Quirksmode der clientXund clientYsind Methoden in allen gängigen Browsern unterstützt. Also, hier ist es - der gute, funktionierende Code, der in einem Bildlauf-Div auf einer Seite mit Bildlaufleisten funktioniert:

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

Dies erfordert auch jQuery für $(canvas).offset().


Entspricht der Antwort @ N4ppeL.
Paweł Szczur


11

Hier ist eine kleine Modifikation der Antwort von Ryan Artecona für Leinwände mit variabler (%) Breite:

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

7

Seien Sie vorsichtig bei der Koordinatenkonvertierung. In einem Klickereignis werden mehrere nicht browserübergreifende Werte zurückgegeben. Die Verwendung von clientX und clientY allein reicht nicht aus, wenn das Browserfenster gescrollt wird (überprüft in Firefox 3.5 und Chrome 3.0).

Dieser Artikel im Quirks-Modus bietet eine korrektere Funktion, die entweder pageX oder pageY oder eine Kombination von clientX mit document.body.scrollLeft und clientY mit document.body.scrollTop verwenden kann, um die Klickkoordinate relativ zum Dokumentursprung zu berechnen.

UPDATE: Außerdem beziehen sich offsetLeft und offsetTop auf die gepolsterte Größe des Elements und nicht auf die Innengröße. Eine Zeichenfläche mit dem angewendeten Stil padding: meldet den oberen linken Rand ihres Inhaltsbereichs nicht als offsetLeft. Es gibt verschiedene Lösungen für dieses Problem. Am einfachsten ist es, alle Rahmen-, Auffüll- usw. Stile auf der Leinwand selbst zu löschen und sie stattdessen auf ein Feld anzuwenden, das die Leinwand enthält.


7

Ich bin mir nicht sicher, wozu all diese Antworten gut sind, die übergeordnete Elemente durchlaufen und alle möglichen seltsamen Dinge tun .

Die HTMLElement.getBoundingClientRectMethode wurde entwickelt, um die tatsächliche Bildschirmposition eines Elements zu handhaben. Dies beinhaltet das Scrollen, so dass Dinge wie scrollTopnicht benötigt werden:

(von MDN) Der Umfang des Bildlaufs im Ansichtsfensterbereich (oder einem anderen scrollbaren Element ) wird bei der Berechnung des Begrenzungsrechtecks ​​berücksichtigt

Normales Bild

Der einfachste Ansatz wurde bereits hier veröffentlicht. Dies ist korrekt, solange keine wilden CSS- Regeln beteiligt sind.

Umgang mit gestreckter Leinwand / Bild

Wenn die Bildpixelbreite nicht mit der CSS-Breite übereinstimmt, müssen Sie ein gewisses Verhältnis auf die Pixelwerte anwenden:

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

Solange die Leinwand keinen Rand hat, funktioniert sie für gestreckte Bilder (jsFiddle) .

Umgang mit CSS-Rändern

Wenn die Leinwand einen dicken Rand hat, werden die Dinge wenig kompliziert . Sie müssen den Rand buchstäblich vom Begrenzungsrechteck abziehen. Dies kann mit .getComputedStyle erfolgen . Diese Antwort beschreibt den Prozess .

Die Funktion wächst dann ein wenig:

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

Ich kann mir nichts vorstellen, was diese endgültige Funktion verwirren würde. Sehen Sie sich bei JsFiddle .

Anmerkungen

Wenn Sie die nativen prototypes nicht ändern möchten, ändern Sie einfach die Funktion und rufen Sie sie auf (canvas, event)(und ersetzen Sie sie thisdurch canvas).


6

Hier ist ein sehr schönes Tutorial-

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

hoffe das hilft!


Die Lösung von Ryan Artecona funktionierte nicht für Tablet-Browser mit Zoom. Dieser tat es jedoch.
Andy Meyers

Funktioniert nicht mit Bildern mit CSS width/ heightOverriden, ist aber dennoch eine der besten Lösungen.
Tomáš Zato - Wiedereinsetzung Monica

3

Mit jQuery im Jahr 2016 mache ich Folgendes, um Klickkoordinaten relativ zur Zeichenfläche zu erhalten:

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

Dies funktioniert, da sowohl Canvas Offset () als auch jqEvent.pageX / Y unabhängig von der Bildlaufposition relativ zum Dokument sind.

Beachten Sie, dass diese Koordinaten nicht mit den logischen Canvas- Koordinaten übereinstimmen, wenn Ihre Zeichenfläche skaliert ist . Um diese zu erhalten, würden Sie auch tun:

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

Beeindruckend. Wie wird 'jqEvent' definiert? Oder "Leinwand"? Oder im zweiten Beispiel "Koordinaten"? Müssen Sie das erste Beispiel vor dem zweiten ausführen? Warum würden Sie in diesem Fall nicht schreiben, "um diese zu bekommen, würden Sie das auch tun"? Geht das alles in einer Onclick-Funktion oder was? Gib einen kleinen Kontext, Kumpel. Angesichts der Tatsache, dass die ursprüngliche Frage im Jahr 2008 gestellt wurde, müssen Sie sie im Kontext der 2008 verfügbaren Technologie beantworten. Verfeinern Sie Ihre Antwort mit gültigem jQuery aus der zu diesem Zeitpunkt verfügbaren Version (v1.2). ;)
Ayelis

1
OK, entschuldige meine Anmaßung. Ich werde bearbeiten, um das zu entfernen. Ich wollte die Antwort mit den neuesten Frameworks liefern. Und ich glaube, ein Programmierer müsste nicht erklärt werden, was jqEvent, canvas und coords sind.
Sarsaparilla

Sieht gut aus. Danke für deinen Beitrag! Entschuldigung, ich habe es dir schwer gemacht! ;)
Ayelis

3

Das ist also sowohl einfach als auch etwas komplizierter als es scheint.

Zunächst einmal gibt es hier meist zusammengeführte Fragen

  1. So erhalten Sie elementbezogene Mauskoordinaten

  2. So erhalten Sie Canvas-Pixel-Mauskoordinaten für die 2D-Canvas-API oder WebGL

Also, Antworten

So erhalten Sie elementbezogene Mauskoordinaten

Ob das Element eine Zeichenfläche ist, die relative Mauskoordinaten für Elemente abruft, ist für alle Elemente gleich.

Es gibt 2 einfache Antworten auf die Frage "Wie man Canvas-relative Mauskoordinaten erhält"

Einfache Antwort # 1 verwenden offsetXundoffsetY

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

Diese Antwort funktioniert in Chrome, Firefox und Safari. Im Gegensatz zu allen anderen Ereigniswerten offsetXundoffsetY berücksichtigen CSS-Transformationen.

Das größte Problem mit offsetXund offsetYist, dass sie ab 2019/05 bei Touch-Ereignissen nicht mehr vorhanden sind und daher nicht mit iOS Safari verwendet werden können. Sie existieren auf Zeigerereignissen, die in Chrome und Firefox existieren, aber nicht in Safari, obwohl Safari anscheinend daran arbeitet .

Ein weiteres Problem ist, dass sich die Ereignisse auf der Leinwand selbst befinden müssen. Wenn Sie sie auf ein anderes Element oder das Fenster setzen, können Sie die Leinwand später nicht als Bezugspunkt auswählen.

Einfache Antwort # 2 verwenden clientX, clientYundcanvas.getBoundingClientRect

Wenn Sie nicht die nächste einfachste Antwort über CSS - Transformationen ist es egal ist zu rufen canvas. getBoundingClientRect()und subtrahiert links ab clientXund topvon clientYda in

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

Dies funktioniert, solange keine CSS-Transformationen vorhanden sind. Es funktioniert auch mit Touch-Ereignissen und damit auch mit 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;
});

So erhalten Sie Canvas-Pixel-Mauskoordinaten für die 2D-Canvas-API

Dazu müssen wir die oben angegebenen Werte verwenden und die Größe der angezeigten Leinwand in die Anzahl der Pixel in der Leinwand selbst konvertieren

mit canvas.getBoundingClientRectund clientXundclientY

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

oder mit offsetXundoffsetY

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

Hinweis: Fügen Sie der Leinwand in keinem Fall Auffüllungen oder Ränder hinzu. Dadurch wird der Code massiv kompliziert. Anstatt dass Sie einen Rand oder eine Polsterung wünschen, umgeben Sie die Leinwand in einem anderen Element und fügen Sie die Polsterung und / oder den Rand zum äußeren Element hinzu.

Arbeitsbeispiel mit event.offsetX,event.offsetY

Arbeitsbeispiel mit canvas.getBoundingClientRectund event.clientXundevent.clientY


2

Ich empfehle diesen Link - 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>

Was ist der Sinn von x = new Number()? Der Code darunter, der neu zugewiesen wird, xwas bedeutet, dass die zugewiesene Nummer sofort verworfen wird
gman


1

Sie könnten einfach tun:

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

Dadurch erhalten Sie die genaue Position des Mauszeigers.


1

Siehe Demo unter 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 };
  }

0

Hier sind einige Modifikationen der obigen Lösung von 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};
}

0

Zunächst benötigen Sie, wie andere gesagt haben, eine Funktion, um die Position des Canvas-Elements zu ermitteln . Hier ist eine Methode, die etwas eleganter ist als einige der anderen auf dieser Seite (IMHO). Sie können jedes Element übergeben und seine Position im Dokument abrufen:

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

Berechnen Sie nun die aktuelle Position des Cursors relativ dazu:

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

Beachten Sie, dass ich die generische findPosFunktion vom Ereignisbehandlungscode getrennt habe . ( Wie es sein sollte . Wir sollten versuchen, unsere Funktionen auf jeweils eine Aufgabe zu beschränken.)

Die Werte von offsetLeftund offsetTopsind relativ zu offsetParent, was ein Wrapper- divKnoten sein könnte (oder irgendetwas anderes). Wenn es kein Element gibt, das das Element umschließt, sind canvassie relativ zum Element body, sodass kein Versatz zum Subtrahieren vorhanden ist. Aus diesem Grund müssen wir die Position der Leinwand bestimmen, bevor wir etwas anderes tun können.

Ähnlich e.pageXunde.pageY geben Sie die Position des Cursors relativ zum Dokument an. Aus diesem Grund subtrahieren wir den Versatz der Leinwand von diesen Werten, um die wahre Position zu erhalten.

Eine Alternative für positionierte Elemente besteht darin, die Werte von e.layerXund direkt zu verwenden e.layerY. Dies ist aus zwei Gründen weniger zuverlässig als die oben beschriebene Methode:

  1. Diese Werte beziehen sich auch auf das gesamte Dokument, wenn das Ereignis nicht in einem positionierten Element stattfindet
  2. Sie sind nicht Teil eines Standards

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;

Nachdem ich viele Lösungen ausprobiert habe. Das hat bei mir funktioniert. Könnte jemand anderem helfen, also zu posten. Habe es von hier bekommen


0

Hier ist eine vereinfachte Lösung (dies funktioniert nicht mit Rahmen / Scrollen):

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

0

Ich habe eine Anwendung mit einer Leinwand über einem PDF erstellt, die viele Größenänderungen der Leinwand beinhaltete, wie z. B. das Vergrößern und Verkleinern des PDF-Dokuments, und im Gegenzug musste ich bei jedem Vergrößern / Verkleinern von PDF die Größe der Leinwand ändern, um die Größe anzupassen Größe des PDFs, ich habe viele Antworten in stackOverflow durchgesehen und keine perfekte Lösung gefunden, die das Problem schließlich lösen wird.

Ich habe rxjs und Angular 6 verwendet und keine spezifische Antwort für die neueste Version gefunden.

Hier ist das gesamte Code-Snippet, das für jeden hilfreich wäre, der rxjs nutzt, um auf Leinwand zu zeichnen.

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

Und hier ist das Snippet, das die Mauskoordinaten im Verhältnis zur Größe der Leinwand korrigiert, unabhängig davon, wie Sie die Leinwand vergrößern / verkleinern.

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

-1

Hey, das ist im Dojo, nur weil ich den Code bereits für ein Projekt hatte.

Es sollte ziemlich offensichtlich sein, wie man es wieder in Nicht-Dojo-Vanille-JavaScript konvertiert.

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

Hoffentlich hilft das.


6
clientX / clientY verhalten sich in allen Browsern nicht ähnlich.
tfwright
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.