48
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script language="javascript">
function main(){
    var canvas = document.getElementById("canvas");
    canvas.addEventListener("mousemove", function(e){
        if (!e) e = window.event;
        var ctx = canvas.getContext("2d");

        var x = e.offsetX;
        var y = e.offsetY;

        ctx.fillRect(x, y, 1, 1);
    });
}
</script>
</head>
<body onload="main();">
<div style="width: 800px; height: 600px; -webkit-transform: scale(0.75,0.75); -moz-transform: scale(0.75,0.75)">
    <canvas id="canvas" width="400px" height="400px" style="background-color: #cccccc;"></canvas>
</div>
</body>
</html>

请考虑上面的快速而肮脏的例子。请注意,我的画布包含在应用了缩放变换的 div 中。上面的代码在任何基于 webkit 的浏览器上都能完美运行。移动鼠标时,它会在画布上绘制点。不幸的是,它不在 Firefox 中,因为它的事件模型不支持 offsetX / Y 属性。考虑到画布位置、变换等,如何将鼠标坐标从(可能)event.clientX(Firefox 也支持)转换为画布相对坐标?谢谢,卢卡。

4

12 回答 12

78

从一个JQuery 错误跟踪页面- 一个不错的 polyfill 是这样的:

var offX  = (e.offsetX || e.pageX - $(e.target).offset().left);

.. 其中 e 是从 jquery 事件返回的事件。显然,只有当您的项目中已经有 Jquery 时,否则将不得不offset()手动完成这些工作。

于 2013-02-14T10:02:25.990 回答
36

试试layerX,layerY

var x = (e.offsetX === undefined) ? e.layerX : e.offsetX;
var y = (e.offsetY === undefined) ? e.layerY : e.offsetY;

小提琴

于 2012-07-04T19:32:10.583 回答
21

不幸的是,没有解决方案对我有用。

我在这里找到了一个很好的实现:

var target  = e.target || e.srcElement,
              rect    = target.getBoundingClientRect(),
              offsetX = e.clientX - rect.left,
              offsetY  = e.clientY - rect.top;

e.offsetX   = offsetX;
e.offsetY   = offsetY;
于 2014-08-20T07:40:03.493 回答
15

不幸的是,offsetX 和 layerX 并不完全相同,因为 offsetX 是当前元素内的偏移量,而 layerX 是页面的偏移量。以下是我目前正在为此使用的修复程序:

function fixEvent(e) {
    if (! e.hasOwnProperty('offsetX')) {
        var curleft = curtop = 0;
        if (e.offsetParent) {
           var obj=e;
           do {
              curleft += obj.offsetLeft;
              curtop += obj.offsetTop;
           } while (obj = obj.offsetParent);
        }
        e.offsetX=e.layerX-curleft;
        e.offsetY=e.layerY-curtop;
    }
    return e;
}
于 2013-02-12T20:23:45.927 回答
11

Musa 的解决方案中有一个错误:想想如果e.offsetX === 0e.layerX === undefined...会发生什么

var x = e.offsetX || e.layerX; // x is now undefined!

更健壮的版本如下:

var x = e.hasOwnProperty('offsetX') ? e.offsetX : e.layerX;
var y = e.hasOwnProperty('offsetY') ? e.offsetY : e.layerY;

或者,因为我们可以假设 ifoffsetX已定义,offsetY所以也会:

var hasOffset = e.hasOwnProperty('offsetX'),
    x         = hasOffset ? e.offsetX : e.layerX,
    y         = hasOffset ? e.offsetY : e.layerY;
于 2012-07-10T09:39:24.050 回答
4

offset实际上并没有直接翻译成layer; 该offset属性不考虑元素的边距。下面的代码应该解释这一点。

function(e) {
    var x = e.offsetX, y = e.offsetY;
    if(e.hasOwnProperty('layerX')) {
      x = e.layerX - e.currentTarget.offsetLeft;
      y = e.layerY - e.currentTarget.offsetTop;
    }
}
于 2013-05-06T16:43:11.523 回答
3

我发现这里发布的所有答案除了EnotionZ 和 laz brannigan 的最后两个答案(以前每个答案都是零票)在 div 中包含多个元素的情况下都是错误的。在我的情况下,我在一个 div 中有几个画布元素,我正在分别收听每个画布。

经过大量的试验和错误,我得出的最终正确答案在 FireFox 和 Chrome 中对我来说完美且相同,如下所示:

//inside my mouse events handler:
var anOffsetX = (inEvent.offsetX !== undefined) ? inEvent.offsetX : (inEvent.layerX - inEvent.target.offsetLeft);
var anOffsetY = (inEvent.offsetY !== undefined) ? inEvent.offsetY : (inEvent.layerY - inEvent.target.offsetTop);

据推测,这也适用于利润,例如 EnotionZ 在他的帖子中指出的,但我没有尝试过。

于 2014-02-14T13:36:09.777 回答
3

由于各种原因,没有一个非 jquery 版本可以完全工作。然而,在你的帮助下,我得到了这个工作:

if(!event.hasOwnProperty('offsetX')) {
    event.offsetX = event.layerX - event.currentTarget.offsetLeft;
    event.offsetY = event.layerY - event.currentTarget.offsetTop;
}
于 2013-05-13T11:47:42.517 回答
2

但是,如果没有字段layerX,你会怎么做?layerY

 var xe=e.offsetX,ye=e.offsetY;
  if(!xe){
     xe=e.clientX - $(e.target).offset().left;
  }
  if(!ye){
    ye= e.clientY - $(e.target).offset().top;
  }
于 2013-09-21T13:57:04.543 回答
1

OffsetX/Y 行为在 FireFox、Chrome/Edge 中有所不同。您可能需要通过添加代码片段来计算值:

const offsetX = evt.clientX - this.myRef.current.getBoundingClientRect().left;
const offsetY = evt.clientY - this.myRef.current.getBoundingClientRect().top;

myRef是它最近的父元素的引用。所以偏移量将是这个元素(clientX)减去它的父元素之间的距离。

于 2020-11-28T13:26:48.837 回答
1

这个问题需要一个更新的答案。


首先,正如我在评论中提到的,关于所有使用layerXand的旧答案layerY

“此功能是非标准的,不在标准轨道上。请勿在面向 Web 的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且行为可能会在未来发生变化。”

(来源:developer.mozilla.org/en-US/docs/Web/API/UIEvent/layerX


其次,我发现当你console.log(event)在 Firefox中时offsetXoffsetY显示 0,但当你时console.log(event.offsetX),它不是 0。所以要小心,因为你可能被骗了。

此处解释了此行为:

记录对象

不使用console.log(obj),使用console.log(JSON.parse(JSON.stringify(obj)))

这样您就可以确定您在记录时看到了 obj 的值。否则,许多浏览器会提供实时视图,该视图会随着值的变化而不断更新。这可能不是你想要的。

(另请注意,JSON.stringify()这里不会做你想做的事......它不会对整个事件对象进行字符串化。)

于 2020-08-07T19:47:31.990 回答
0

如果有些人仍然需要解决方案,那么这个在 Firefox 中完美运行:

var x = e.offsetX || e.originalEvent.layerX;
var y = e.offsetY || e.originalEvent.layerY;
于 2015-04-30T23:22:11.247 回答