我如何获得鼠标点击一个canvas元素的坐标?

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

不需要传统的浏览器兼容性,Safari,Opera和Firefox都可以。

如此处所述:

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; 

对我来说工作得很好。

更新 (5/5/16):应该使用patriques的答案 ,因为它既简单又可靠。


由于canvas并不总是相对于整个页面的样式,所以canvas.offsetLeft/Top并不总是返回你所需要的。 它将返回相对于其offsetParent元素偏移的像素的数量,可以像div元素一样包含具有position: relative的canvasposition: relative应用position: relative样式。 为了解决这个问题,你需要循环通过offsetParent的链,从canvas元素本身开始。 这段代码完全适合我,在Firefox和Safari中testing,但应该为所有工作。

 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; 

如果你喜欢简单,但仍然需要跨浏览器function,我发现这个解决scheme最适合我。 这是@ Aldekein的解决scheme的简化,但没有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); } 

现代浏览器现在为您处理这个。 Chrome,IE9和Firefox都支持这样的offsetX / Y,从click处理器传入事件。

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

根据新的Quirksmode ,在所有主stream浏览器中都支持clientXclientY方法。 所以,在这里,它是一个很好的工作代码,在滚动条的页面上使用滚动条工作:

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

这也需要$(canvas).offset()的 jQuery。

我做了一个完整的演示,可以在每个浏览器中使用这个问题的解决scheme的完整源代码: 鼠标的坐标在Javascript中的Canvas上单击 。 要尝试演示,请复制代码并将其粘贴到文本编辑器中。 然后保存为example.html,最后用浏览器打开这个文件。

这里是Ryan Artecona对于具有variables(%)宽度的canvas的回答的小修改:

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

做坐标转换时要小心。 在点击事件中返回了多个非跨浏览器值。 如果浏览器窗口滚动(在Firefox 3.5和Chrome 3.0中validation),单独使用clientX和clientY是不够的。

这个怪癖模式文章提供了一个更正确的function,可以使用pageX或pageY,或者将clientX与document.body.scrollLeft和clientY结合使用,以document.body.scrollTop来计算相对于文档原点的点击坐标。

更新:此外,offsetLeft和offsetTop是相对于元素的填充大小,而不是内部大小。 应用了padding:style的canvas不会将其内容区域的左上angular报告为offsetLeft。 这个问题有多种解决scheme。 最简单的方法可能是清除canvas上的所有边框,填充等样式,而是将其应用于包含canvas的框。

这是一个非常好的教程 –

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

希望这可以帮助!

我不确定所有这些回答的重点是什么, 通过父元素循环 ,做各种奇怪的东西 。

HTMLElement.getBoundingClientRect方法用于处理任何元素的实际屏幕位置。 这包括滚动,所以像scrollTop不需要的东西:

(来自MDN)计算边界矩形时将考虑视口区域(或任何其他可滚动元素 )的滚动量

正常的图像

最简单的方法已经发布在这里。 只要没有涉及到CSS规则,这是正确的。

处理拉伸的canvas/图像

当图像像素宽度与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]; } 

只要canvas没有边框, 它适用于拉伸的图像(jsFiddle)

处理CSS边界

如果canvas边框很粗糙, 事情就会变得复杂 。 你会真的需要从边界矩形减去边框。 这可以使用.getComputedStyle完成。 这个答案描述了这个过程 。

该function然后成长一点:

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

我想不出任何会混淆这个最终function的东西。 在JsFiddle看看自己。

笔记

如果您不喜欢修改原生的prototype ,只需更改该函数,然后使用(canvas, event)调用它(并将其replace为canvas )。

在2016年使用jQuery,获取相对于canvas的点击坐标,我做:

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

这是可行的,因为无论滚动位置如何,canvas offset()和jqEvent.pageX / Y都是相对于文档的。

请注意,如果您的canvas被缩放,那么这些坐标与canvas逻辑坐标不同 。 为了得到这些,你也可以这样做:

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

我推荐这个链接 – 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> 

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

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; 

这会给你鼠标指针的确切位置。

请参阅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 }; } 

嘿,这是在道场,只是因为这是我的代码已经为一个项目。

它应该是相当明显的如何将其转换回非dojo香草JavaScript。

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

希望有所帮助。

以上是瑞安Artecona解决scheme的一些修改。

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

首先,正如其他人所说,你需要一个函数来获取canvas元素的位置 。 这里有一个方法比这个页面上的其他一些更加优雅(恕我直言)。 您可以传递任何元素并在文档中获得它的位置:

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

现在计算光标的相对位置:

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

请注意,我已经将通用findPos函数与事件处理代码分开了。 ( 应该是这样 ,我们应该尽量保持我们的function,每个任务。)

offsetLeftoffsetTop的值是相对于offsetParent ,这可能是一些包装div节点(或其他任何东西)。 当没有元素包装canvas它们与body相对,所以没有要减去的偏移量。 这就是为什么我们需要确定canvas的位置,然后才能做其他事情。

e.pageXe.pageXe.pageY给出了光标相对于文档的位置。 这就是为什么我们从这些值中减去canvas的偏移量以达到真实的位置。

定位元素的替代方法是直接使用e.layerXe.layerY的值。 由于以下两个原因,这比上述方法更不可靠:

  1. 当事件不在定位的元素内时,这些值也是相对于整个文档的
  2. 他们不是任何标准的一部分

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; 

尝试了很多解决scheme后 这对我有效。 可能帮助其他人张贴。 从这里得到它