Как получить координаты щелчка мышью на элементе canvas? - PullRequest
256 голосов
/ 11 сентября 2008

Какой самый простой способ добавить обработчик события щелчка к элементу холста, который будет возвращать координаты x и y клика (относительно элемента холста)?

Не требуется совместимость с устаревшими браузерами, подойдут Safari, Opera и Firefox.

Ответы [ 22 ]

175 голосов
/ 09 мая 2011

Обновление (5/5/16): ответ патриархов следует использовать вместо этого, так как он проще и надежнее.


Поскольку холст не всегда стилизован относительно всей страницы, canvas.offsetLeft/Top не всегда возвращает то, что вам нужно. Он вернет количество пикселей, на которые он смещен, относительно его элемента offsetParent, который может быть чем-то вроде элемента div, содержащего холст с примененным стилем position: relative. Чтобы учесть это, вам нужно перебрать цепочку offsetParent s, начиная с самого элемента canvas. Этот код отлично работает для меня, протестирован в Firefox и Safari, но должен работать для всех.

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;

Последняя строка упрощает получение координат мыши относительно элемента canvas. Все, что нужно для получения полезных координат, это

coords = canvas.relMouseCoords(event);
canvasX = coords.x;
canvasY = coords.y;
153 голосов
/ 05 августа 2013

Если вам нравится простота, но все же требуется кросс-браузерная функциональность, я обнаружил, что это решение работает лучше всего для меня. Это упрощение решения @ Aldekein, но без jQuery .

function getCursorPosition(canvas, event) {
    var rect = canvas.getBoundingClientRect();
    var x = event.clientX - rect.left;
    var y = event.clientY - rect.top;
    console.log("x: " + x + " y: " + y);
}
71 голосов
/ 13 декабря 2010

Редактировать 2018: Этот ответ довольно старый и использует проверки для старых браузеров, которые больше не нужны, поскольку свойства clientX и clientY работают во всех текущих браузерах. Возможно, вы захотите проверить Ответ Патрика для более простого, более свежего решения.

Оригинальный ответ:
Как описано в статье, которую я нашел тогда, но больше не существует:

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;

Отлично сработало для меня.

27 голосов
/ 24 августа 2012

Современный браузер теперь справится с этим для вас. Chrome, IE9 и Firefox поддерживают смещение X / Y следующим образом, передавая событие из обработчика щелчков.

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

Большинство современных браузеров также поддерживают layerX / Y, однако Chrome и IE используют layerX / Y для абсолютного смещения клика по странице, включая поля, отступы и т. Д. В Firefox layerX / Y и offsetX / Y эквивалентны но смещение ранее не существовало. Итак, для совместимости с чуть более старыми браузерами вы можете использовать:

function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}
17 голосов
/ 24 марта 2011

Согласно свежему Quirksmode методы clientX и clientY поддерживаются во всех основных браузерах. Итак, все готово - хороший рабочий код, который работает в элементе прокрутки на странице с полосами прокрутки:

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

Это также требует jQuery для $(canvas).offset().

13 голосов

Я сделал полную демонстрацию, которая работает в каждом браузере с полным исходным кодом решения этой проблемы: Координаты щелчка мышью по Canvas в Javascript . Чтобы попробовать демо, скопируйте код и вставьте его в текстовый редактор. Затем сохраните его как example.html и, наконец, откройте файл в браузере.

10 голосов
/ 01 апреля 2012

Вот небольшая модификация Ответ Райана Артеконы для полотен с переменной (%) шириной:

 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 голосов
/ 29 ноября 2014

Я не уверен, какой смысл всех этих ответов, что перебирает родительские элементы и делает все виды странных вещей .

Метод HTMLElement.getBoundingClientRect предназначен для обработки фактического положения экрана любого элемента. Это включает в себя прокрутку, поэтому такие вещи, как scrollTop не нужны:

(из MDN) Количество выполненной прокрутки в области области просмотра (или любой другой прокручиваемый элемент ) учитывается при вычислении ограничительный прямоугольник

Нормальное изображение

очень простой подход уже был размещен здесь. Это правильно до тех пор, пока не задействованы дикие правила CSS .

Обработка натянутого холста / изображения

Если ширина пикселя изображения не совпадает с шириной CSS, вам нужно применить некоторое отношение к значениям пикселей:

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

Пока холст не имеет границы, он работает для растянутых изображений (jsFiddle) .

Обработка границ CSS

Если холст имеет толстую рамку, все становится немного сложнее . Вам буквально нужно вычесть границу из ограничивающего прямоугольника. Это можно сделать с помощью .getComputedStyle . Этот ответ описывает процесс .

Затем функция немного растет:

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

Я не могу думать ни о чем, что могло бы спутать эту последнюю функцию. Смотрите на JsFiddle .

Примечания

Если вам не нравится изменять собственные prototype s, просто измените функцию и вызовите ее с помощью (canvas, event) (и замените любой this на canvas).

7 голосов
/ 04 октября 2009

Будьте осторожны при выполнении преобразования координат; в событии щелчка возвращено несколько значений, не относящихся к браузеру. Использование только clientX и clientY недостаточно, если прокручивается окно браузера (проверено в Firefox 3.5 и Chrome 3.0).

В этом странном режиме статья предоставляет более правильную функцию, которая может использовать pageX или pageY или комбинацию clientX с document.body.scrollLeft и clientY с document.body.scrollTop для вычисления относительной координаты клика к происхождению документа.

ОБНОВЛЕНИЕ: Кроме того, offsetLeft и offsetTop относятся к дополнительному размеру элемента, а не к внутреннему размеру. Холст с примененным padding: style не будет отображать верхний левый угол своей области содержимого как offsetLeft. Существуют различные решения этой проблемы; самый простой может быть очистить все стили границ, отступов и т. д. на самом холсте и вместо этого применить их к блоку, содержащему холст.

6 голосов
/ 01 июля 2013

Вот очень хороший учебник-

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

надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...