Jak uzyskać współrzędne kliknięcia myszą elementu canvas?

276

Jaki jest najprostszy sposób dodania modułu obsługi zdarzenia kliknięcia do elementu canvas, który zwróci współrzędne xiy kliknięcia (względem elementu canvas)?

Nie jest wymagana starsza kompatybilność przeglądarki, wystarczy Safari, Opera i Firefox.

Tomek
źródło
Nie powinno to różnić się od pobierania zdarzeń myszy z normalnych elementów dom. quirksmode ma na to dobre odniesienie.
airportyh
4
Powyższy kod działa tylko wtedy, gdy obszar roboczy nie znajduje się głęboko w innych kontenerach. Ogólnie rzecz biorąc, musisz użyć czegoś takiego jak funkcja przesunięcia jquery [var testDiv = $ ('# testDiv'); var offset = testDiv.offset ();], aby uzyskać prawidłowe przesunięcie w przeglądarce. To prawdziwy ból w ***.
Aaron Watters
Kod opublikowany powyżej z aktualizacją nie działa, jeśli strona zawierająca płótno przewija się.
Timothy Kim
Usunąłem moją starą „odpowiedź”, która została uwzględniona jako aktualizacja pytania. Jak wspomniano, był przestarzały i niekompletny.
Tom
3
Ponieważ jest tu około 50 odpowiedzi, polecam przewinięcie do odpowiedzi chłopaków: patriques - dobry i prosty 5 liniowy.
Igor L.

Odpowiedzi:

77

Edycja 2018: Ta odpowiedź jest dość stara i wykorzystuje sprawdzanie starych przeglądarek, które nie są już potrzebne, ponieważ clientXi clientYwłaściwości działają we wszystkich obecnych przeglądarkach. Możesz sprawdzić Patriques Answer, aby uzyskać prostsze, nowsze rozwiązanie.

Oryginalna odpowiedź:
Jak opisano w artykule, który wtedy znalazłem, ale już nie istnieje:

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;

Działa dla mnie idealnie.

N4ppeL
źródło
30
To rozwiązanie nie zawsze działa - odpowiedź Ryana Artecony działa w bardziej ogólnym przypadku, gdy płótno niekoniecznie jest ustawione względem całej strony.
Chris Johnson,
3
To rozwiązanie nie jest dopasowane, jeśli wydajność ma znaczenie, ponieważ dostęp do Dom jest wykonywany za każdym kliknięciem / ruchem. A getBoundingClientRect już istnieje i jest bardziej elegancki.
GameAlchemist
192

Jeśli podoba Ci się prostota, ale nadal chcesz korzystać z wielu przeglądarek, to rozwiązanie okazało się dla mnie najlepsze. Jest to uproszczenie rozwiązania @ Aldekein, ale bez 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)
})
patriques
źródło
Jeśli strona jest przewijana w dół, należy również wziąć pod uwagę przesunięcie przewijania dokumentu.
Peppe LG,
8
@ Prostokąt ograniczający klienta PeppeL-G oblicza to dla Ciebie. Możesz łatwo przetestować go w konsoli przed opublikowaniem komentarza (tak zrobiłem).
Tomáš Zato - Przywróć Monikę
1
@ TomášZato, och, getBoundingClientRectzwraca pozycje względem portu widoku? Więc moje przypuszczenie było błędne. Nigdy tego nie testowałem, ponieważ nigdy nie stanowiło to dla mnie problemu i uprzejmie chciałem ostrzec innych czytelników przed potencjalnym problemem, który widziałem, ale dziękuję za wyjaśnienie.
Peppe LG
1
To nie działa, jeśli płótno jest skalowane. Nie jestem pewien, czy to błąd przeglądarki.
Jeroen
2
Dodaj użycie dla kogoś takiego jak ja:var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
SnowOnion
179

Aktualizacja (5/5/16): zamiast tego należy użyć odpowiedzi patriques , ponieważ jest ona zarówno prostsza, jak i bardziej niezawodna.


Ponieważ płótno nie zawsze jest stylizowane względem całej strony, canvas.offsetLeft/Topnie zawsze zwraca to, czego potrzebujesz. Zwróci liczbę pikseli, które są przesunięte względem elementu offsetParent, który może być czymś w rodzaju divelementu zawierającego płótno z position: relativezastosowanym stylem. Aby to uwzględnić, należy zapętlić łańcuch offsetParents, zaczynając od samego elementu canvas. Ten kod działa dla mnie idealnie, przetestowany w Firefox i Safari, ale powinien działać dla wszystkich.

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;

Ostatni wiersz ułatwia uzyskanie współrzędnych myszy względem elementu canvas. Aby uzyskać przydatne współrzędne, wystarczy

coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;
Ryan Artecona
źródło
8
Nie, używa wbudowanego prototypowego obiektu javascript --- developer.mozilla.org/en/…
garg
14
mój Chrome ma event.offsetXi event.offsetYatrybuty, więc zmodyfikowałem Twoje rozwiązanie, dodając if (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; }. wygląda na to, że to działa.
Baczek
3
Baczek ma rację co do Chrome'a event.offsetXi event.offsetYktóry działa również w IE9. W przeglądarce Firefox (testowanej w / v13) możesz używać event.layerXi event.layerY.
mafafu
3
Dodałem dodatkowo: canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
amirpc
4
Ta ostateczna wersja odpowiedzi nie działała dla mnie. W przeglądarce Firefox, jeśli cały ekran jest przewijany, otrzymałem przesuniętą wartość jako wynik. Dla mnie zadziałało bardzo podobne rozwiązanie, podane na stackoverflow.com/a/10816667/578749, które zamiast event.pageX / Y odjęło obliczone przesunięcie od event.clientX / Y. Czy ktoś może to sprawdzić i wyjaśnić?
lvella
33

Nowoczesna przeglądarka obsługuje teraz to za Ciebie. Chrome, IE9 i Firefox obsługują offsetX / Y w ten sposób, przekazując zdarzenie z modułu obsługi kliknięć.

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

Większość współczesnych przeglądarek obsługuje także warstwy X / Y, jednak Chrome i IE używają warstwy X / Y do bezwzględnego przesunięcia kliknięcia strony, w tym marginesu, wypełnienia itp. W przeglądarce Firefox warstwy X / Y i offset X / Y są równoważne, ale przesunięcie nie wcześniej nie istniały. Aby zapewnić zgodność z nieco starszymi przeglądarkami, możesz użyć:

function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}
mafafu
źródło
1
ciekawe, jak zdefiniowano layerX, layerY w Chrome i Firefox, ale w Chrome jest on niedokładny (lub oznacza coś innego).
Julian Mann
@JulianMann Dzięki za informację. Zaktualizowałem tę odpowiedź w oparciu o bardziej aktualne wsparcie. Wygląda na to, że teraz możesz uciec z offsetX / Y prawie uniwersalnie.
mafafu,
18

Według świeżego Quirks ModeclientX i clientYmetody są obsługiwane we wszystkich głównych przeglądarkach. No i proszę - dobry, działający kod, który działa w przewijanym div na stronie z paskami przewijania:

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

Wymaga to również jQuery dla $(canvas).offset().

Aldekein
źródło
Odpowiednik odpowiedzi @ N4ppeL.
Paweł Szczur
13

Wykonałem pełną demonstrację, która działa w każdej przeglądarce, z pełnym kodem źródłowym rozwiązania tego problemu: współrzędne kliknięcia myszą Canvas w Javascript . Aby wypróbować wersję demonstracyjną, skopiuj kod i wklej go do edytora tekstu. Następnie zapisz go jako example.html, a na koniec otwórz plik za pomocą przeglądarki.

Manuel Ignacio López Quintero
źródło
11

Oto niewielka modyfikacja odpowiedzi Ryana Artecony na płótna o zmiennej (%) szerokości:

 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}
}
Cryptovirus
źródło
7

Zachowaj ostrożność podczas konwersji współrzędnych; w zdarzeniu kliknięcia zwracanych jest wiele wartości niepochodzących z różnych przeglądarek. Używanie samych clientX i clientY nie jest wystarczające, jeśli okno przeglądarki jest przewijane (zweryfikowane w Firefox 3.5 i Chrome 3.0).

W tym artykule o dziwactwach podano bardziej poprawną funkcję, która może używać pageX lub pageY lub kombinacji clientX z document.body.scrollLeft i clientY z document.body.scrollTop do obliczenia współrzędnej kliknięcia względem początku dokumentu.

AKTUALIZACJA: Dodatkowo offsetLeft i offsetTop odnoszą się do wielkości wypełnienia elementu, a nie do rozmiaru wnętrza. Kanwa z zastosowanym dopełnieniem: styl nie będzie raportować lewego górnego rogu swojego obszaru zawartości jako offsetLeft. Istnieją różne rozwiązania tego problemu; najprostszym może być usunięcie wszystkich stylów obramowania, wypełnienia itp. z samego płótna i zastosowanie ich do pudełka zawierającego płótno.

fixermark
źródło
7

Nie jestem pewien, jaki jest sens tych wszystkich odpowiedzi, które przechodzą przez elementy nadrzędne i robią różnego rodzaju dziwne rzeczy .

HTMLElement.getBoundingClientRectSposób jest przeznaczony do obsługiwać rzeczywiste położenie ekranu w elemencie. Obejmuje to przewijanie, więc takie rzeczy scrollTopnie są potrzebne:

(z MDN) Przyobliczaniu prostokąta granicznego brana jest pod uwagęwielkość przewijania obszaru rzutni (lub dowolnego innego przewijalnego elementu )

Normalny obraz

Bardzo najprostsze podejście zostało już wysłane tutaj. Jest to poprawne, o ile nie są zaangażowane żadne dzikie reguły CSS .

Obsługa rozciągniętego płótna / obrazu

Gdy szerokość piksela obrazu nie jest dopasowana do szerokości CSS, musisz zastosować pewien stosunek wartości pikseli:

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

Tak długo, jak płótno nie ma granicy, działa na rozciągnięte obrazy (jsFiddle) .

Obsługa granic CSS

Jeśli płótno ma grubą ramkę, sprawy stają się mało skomplikowane . Musisz dosłownie odjąć ramkę od prostokąta ograniczającego. Można to zrobić za pomocą .getComputedStyle . Ta odpowiedź opisuje proces .

Funkcja następnie trochę rośnie:

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

Nie mogę wymyślić niczego, co mogłoby pomylić tę ostateczną funkcję. Zobacz siebie w JsFiddle .

Notatki

Jeśli nie lubisz modyfikować natywnych prototype, po prostu zmień funkcję i wywołaj ją za pomocą (canvas, event)(i zastąp dowolną thisz canvas).

Tomáš Zato - Przywróć Monikę
źródło
6

Oto bardzo fajny samouczek

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

mam nadzieję że to pomoże!

FraZer
źródło
Rozwiązanie Ryana Artecony nie działało w przeglądarkach tabletów z zoomem. Jednak ten zrobił.
Andy Meyers,
Nie działa z obrazami, które mają CSS width/ heightprzesłonięcie, ale nadal jest jednym z najlepszych rozwiązań.
Tomáš Zato - Przywróć Monikę
3

Korzystając z jQuery w 2016 roku, aby uzyskać współrzędne kliknięcia względem obszaru roboczego, wykonuję:

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

Działa to, ponieważ zarówno canvas offset (), jak i jqEvent.pageX / Y są względem dokumentu niezależnie od położenia przewijania.

Zauważ, że jeśli twoje płótno jest skalowane, wówczas te współrzędne nie są takie same jak współrzędne logiczne płótna . Aby je uzyskać, wykonaj także :

var logicalCoords = {
    x: coords.x * (canvas.width / $(canvas).width()),
    y: coords.y * (canvas.height / $(canvas).height())
}
Sarsaparilla
źródło
Łał. Jak definiuje się „jqEvent”? Czy „płótno”? Lub w drugim przykładzie „coords”? Czy musisz uruchomić pierwszy przykład przed drugim? W takim razie, dlaczego nie napisałbyś „aby je zdobyć, zrobiłbyś TAKŻE”? Czy to wszystko działa w funkcji onclick czy co? Daj trochę kontekstu, kolego. Biorąc pod uwagę, że pierwotne pytanie zostało zadane w 2008 roku, myślę, że musisz odpowiedzieć w kontekście technologii, która była dostępna w 2008 roku. Uściślij swoją odpowiedź, używając poprawnego jQuery z wersji, która była wówczas dostępna (v1.2). ;)
Ayelis,
1
OK, przepraszam za moją zarozumiałość. Będę edytować, aby usunąć to. Zamierzałem udzielić odpowiedzi przy użyciu najnowszych ram. I wierzę, że programista nie musiałby wyjaśniać, czym są jqEvent, canvas i coord.
Sarsaparilla 21.04.16
Wygląda dobrze. Dzięki za wkład! Przepraszam, że sprawiłem ci trudność! ;)
Ayelis 21.04.16
3

Jest to więc zarówno prosty, ale nieco bardziej skomplikowany temat, niż się wydaje.

Po pierwsze, najczęściej są tu pytania skonfliktowane

  1. Jak uzyskać względne współrzędne myszy elementu

  2. Jak uzyskać współrzędne myszy Canvas Canvas dla API 2D Canvas lub WebGL

więc odpowiedzi

Jak uzyskać względne współrzędne myszy elementu

Niezależnie od tego, czy element jest płótnem otrzymującym element, względne współrzędne myszy są takie same dla wszystkich elementów.

Istnieją 2 proste odpowiedzi na pytanie „Jak uzyskać względne współrzędne myszy względem obszaru roboczego”

Prosta odpowiedź nr 1 użycie offsetXioffsetY

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

Ta odpowiedź działa w Chrome, Firefox i Safari. W przeciwieństwie do wszystkich innych wartości zdarzeń offsetXi offsetYuwzględniają transformacje CSS.

Największym problemem offsetXi offsetYjest od 2019/05, że nie istnieją one na zdarzeniach dotykowych, więc nie można ich używać z iOS Safari. Istnieją one w Zdarzeniach Wskaźnikowych, które istnieją w Chrome i Firefox, ale nie w Safari, chociaż najwyraźniej Safari na tym działa .

Inną kwestią jest to, że wydarzenia muszą znajdować się na samym płótnie. Jeśli umieścisz je na jakimś innym elemencie lub oknie, nie będziesz mógł później wybrać płótna jako punktu odniesienia.

Prosta odpowiedź nr 2 clientX, clientYicanvas.getBoundingClientRect

Jeśli nie obchodzi Cię transformacja CSS, następną najprostszą odpowiedzią jest wywołanie canvas. getBoundingClientRect()i odjęcie lewej od clientXi topod clientYjak w

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

Będzie to działać, dopóki nie będzie transformacji CSS. Działa również ze zdarzeniami dotykowymi, więc będzie działać z 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;
});

Jak uzyskać współrzędne myszy Canvas Canvas dla interfejsu API 2D Canvas

W tym celu musimy wziąć wartości, które otrzymaliśmy powyżej, i przekonwertować z rozmiaru wyświetlanego płótna na liczbę pikseli w samym płótnie

z canvas.getBoundingClientRecti clientXaclientY

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

lub z offsetXioffsetY

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

Uwaga: we wszystkich przypadkach nie dodawaj do kanwy wypełnienia ani obramowań. Może to znacznie skomplikować kod. Zamiast tego chcesz, aby obramowanie lub wypełnienie otaczały płótno w jakimś innym elemencie i dodaj wypełnienie i / lub granicę do elementu zewnętrznego.

Przykład roboczy przy użyciu event.offsetX,event.offsetY

Przykład pracuje używając canvas.getBoundingClientRecti event.clientXaevent.clientY

gman
źródło
2

Polecam ten 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>

źródło
o co chodzi x = new Number()? Poniższy kod ponownie przypisuje, xco oznacza, że ​​przydzielony numer jest natychmiast odrzucany
gman
1

Możesz po prostu zrobić:

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

To da ci dokładne położenie wskaźnika myszy.

użytkownik2310569
źródło
1

Zobacz demo na 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 };
  }
Daniel Patru
źródło
0

Oto kilka modyfikacji powyższego rozwiązania Ryana Artecony.

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};
}
Simon Cześć
źródło
0

Po pierwsze, jak powiedzieli inni, potrzebujesz funkcji, aby uzyskać pozycję elementu canvas . Oto metoda, która jest nieco bardziej elegancka niż niektóre inne na tej stronie (IMHO). Możesz przekazać dowolny element i uzyskać jego pozycję w dokumencie:

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

Teraz obliczyć bieżącą pozycję kursora względem tego:

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

Zauważ, że oddzieliłem funkcję ogólną findPosod kodu obsługi zdarzeń. ( Tak jak powinno być . Powinniśmy starać się, aby nasze funkcje spełniały jedno zadanie).

Wartości offsetLeftioffsetTop są względem offsetParent, które mogą być jakimś divwęzłem otoki (lub cokolwiek innego, jeśli o to chodzi). Kiedy nie ma zawijania elementów, canvassą one względem siebie body, więc nie ma przesunięcia do odjęcia. Dlatego musimy ustalić pozycję płótna, zanim będziemy mogli zrobić cokolwiek innego.

Podobnie e.pageXi e.pageYpodaj pozycję kursora względem dokumentu. Dlatego odejmujemy przesunięcie obszaru roboczego od tych wartości, aby dojść do prawdziwej pozycji.

Alternatywa dla pozycjonowanego elementów jest bezpośrednie użycie wartości e.layerXi e.layerY. Jest to mniej niezawodne niż powyższa metoda z dwóch powodów:

  1. Wartości te odnoszą się również do całego dokumentu, gdy zdarzenie nie ma miejsca w elemencie pozycjonowanym
  2. Nie są częścią żadnego standardu
Wayne
źródło
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;

Po wypróbowaniu wielu rozwiązań. To zadziałało dla mnie. Może więc pomóc komuś innemu w tym zakresie. Mam to stąd

Aniket Betkikar
źródło
0

Oto uproszczone rozwiązanie (nie działa z ramkami / przewijaniem):

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),
    };
}
Haykam
źródło
0

Tworzyłem aplikację mającą obszar roboczy nad plikiem pdf, który wymagał wielu zmian rozmiaru obszaru roboczego, takich jak powiększanie i pomniejszanie formatu pdf, a następnie przy każdym powiększaniu / zmniejszaniu formatu PDF musiałem zmieniać rozmiar obszaru roboczego, aby dostosować rozmiar pliku pdf, przejrzałem wiele odpowiedzi w stackOverflow i nie znalazłem idealnego rozwiązania, które ostatecznie rozwiąże problem.

Używałem rxjs i kątowego 6, i nie znaleziono żadnej odpowiedzi wprowadzonych w najnowszej wersji.

Oto cały fragment kodu, który byłby pomocny dla każdego, kto wykorzystuje rxjs do rysowania na płótnie.

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

A oto fragment, który naprawia, współrzędne myszy w stosunku do rozmiaru płótna, niezależnie od tego, jak powiększasz / zmniejszasz płótno.

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 )
};
Divyanshu Rawat
źródło
-1

Hej, to jest w dojo, tylko dlatego, że mam już kod do projektu.

Powinno być dość oczywiste, jak przekonwertować go z powrotem na JavaScript JavaScript bez dojo.

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

Mam nadzieję, że to pomaga.

Brian Gianforcearo
źródło
6
clientX / clientY nie zachowują się podobnie w różnych przeglądarkach.
tfwright