将单击事件处理程序添加到将返回单击的x和y坐标(相对于canvas元素)的canvas元素的最简单方法是什么?
不需要传统的浏览器兼容性,Safari,Opera和Firefox都可以.
更新(5/5/16):应该使用patriques的答案,因为它既简单又可靠.
由于画布并不总是相对于整个页面设置样式,因此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;
如果您喜欢简单但仍想要跨浏览器功能,我发现这个解决方案最适合我.这是@Aldekein解决方案的简化,但没有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) })
编辑2018:这个答案非常陈旧,它使用了对不再需要的旧浏览器的检查,因为clientX
和clientY
属性适用于所有当前浏览器.您可能需要查看Patriques Answer以获得更简单,更新的解决方案.
原始答案:
正如我在当时发现的一篇文章所述,但不再存在:
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;
对我来说工作得很好.
现代浏览器现在可以为您处理.Chrome,IE9和Firefox支持offsetX/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 }; }
根据新鲜怪异模式将clientX
和clientY
方法,在所有主要的浏览器都支持.所以,在这里它是 - 在带有滚动条的页面上的滚动div中工作的好的,有效的代码:
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()
.
我做了一个完整的演示,可以在每个浏览器中使用此问题的解决方案的完整源代码:鼠标在Javascript中单击Canvas的坐标.要尝试演示,请复制代码并将其粘贴到文本编辑器中.然后将其保存为example.html,最后,使用浏览器打开该文件.
以下是对Ryan Artecona对宽度可变(%)的画布的回答的一个小修改:
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中验证),仅使用clientX和clientY是不够的.
这个怪癖模式文章提供了一个更正确的函数,可以使用pageX或pageY或clientX与document.body.scrollLeft和clientY与document.body.scrollTop的组合来计算相对于文档原点的单击坐标.
更新:此外,offsetLeft和offsetTop是相对于元素的填充大小,而不是内部大小.应用了padding:style的画布不会将其内容区域的左上角报告为offsetLeft.这个问题有各种解决方案; 最简单的可能是清除画布本身上的所有边框,填充等样式,而是将它们应用到包含画布的框中.
我不确定所有这些答案的重点在于循环通过父元素并做各种奇怪的事情.
该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).
如果画布边框很粗,那么事情就会变得复杂.你真的需要从边界矩形中减去边界.这可以使用.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
with canvas
).
这是一个非常好的教程 -
http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
Tags | 热门标签RankList | 热门文章
- 1使用Angular2处理404
- 2Laravel验证单选按钮输入
- 3process.env vs app.get('env')获取express.js环境
- 4如何在R中粘贴项目列表
- 5如何使用泛型参数获取Scala运行时类?
- 6在TMemo中锁定文本的开头
- 7如何以这种格式验证字符串:p [1或更多数字]?
- 8如果a的值是5,那么a ++ + a的值是多少?
- 9如何将命令行参数转换为双数组以计算总和?
- 10iOS有类似Android的RecyclerView吗?
- 11在Haskell中生成下一个词典字符串
- 12使用rustc_serialize并获取不带引号的字符串
- 13skimage调整大小给出奇怪的输出
- 14在更新面板内自动将文件上载到服务器第一次不起作用
- 15C++体系结构x86_64的未定义符号
- 16有没有办法在Haskell中模拟线性类型?
- 17按条件跨多个列交换值
- 18如何在matplotlib中更改科学记数法的字体大小?
- 19解决错误“与返回的局部变量关联的堆栈内存地址”
- 20Windows命令:如何使用chdir推送当前目录以便以后弹出?