向canvas元素添加一个单击事件处理程序,返回单击的x和y坐标(相对于canvas元素),最简单的方法是什么?

不需要传统浏览器兼容性,Safari、Opera和Firefox就可以了。


当前回答

我不确定所有这些遍历父元素和做各种奇怪事情的答案的意义是什么。

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见。

笔记

如果您不喜欢修改本机原型,只需更改函数并使用(canvas, event)调用它(并将任何此替换为canvas)。

其他回答

在Prototype中,使用cumulativeOffset()执行上面Ryan Artecona提到的递归求和。

http://www.prototypejs.org/api/element/cumulativeoffset

你可以这样做:

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

这将为您提供鼠标指针的确切位置。

三JS 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;

在尝试了许多解决方案后。这对我很管用。可能会帮助其他人,因此发帖。从这里开始

这是一个非常好的教程-

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

希望这能有所帮助!

这里是一个简化的解决方案(这并不适用于边框/滚动):

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