【问题标题】:event.offsetX in FirefoxFirefox 中的 event.offsetX
【发布时间】:2012-07-05 06:35:13
【问题描述】:
<!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 也支持)转换为画布相对坐标? 谢谢,卢卡。

【问题讨论】:

  • 谢天谢地,Firefox 40 支持 offsetX!

标签: javascript html events firefox html5-canvas


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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


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

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

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


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

    此行为解释here

    记录对象

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

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

    (另请注意,JSON.stringify() 不会在此处执行您想要的操作...它不会对整个事件对象进行字符串化。)

    【讨论】:

    【解决方案3】:

    试试 layerX, layerY

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

    FIDDLE

    【讨论】:

    • 不幸的是,Firefox 的 layerX 和 layerY 属性与其他浏览器中的不同。页面有相对左上角,但没有elemenet。
    • false,它们是相对于最近的 offsetParent 包括元素本身,只需将“position:relative”添加到要用于坐标的元素。
    • 对我不起作用,但这对我有用 stackoverflow.com/questions/12704686/…
    • 不适用于 FF 32.0.3 。事件中没有 layerX 和 layerY。
    • layerX 和 offsetX 不一样,如果目标是内联元素,偏移量给出了光标在元素中的位置,但是返回一个相对于他的非内联父容器的 layerX。
    【解决方案4】:

    很遗憾,没有解决方案对我有用。

    我找到了一个很好的实现here:

    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;
    

    【讨论】:

      【解决方案5】:

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

      var x = e.offsetX || e.originalEvent.layerX;
      var y = e.offsetY || e.originalEvent.layerY;
      

      【讨论】:

      • 应该注意,这意味着在 jquery 事件处理程序中使用,因为 jquery 将原始事件包装在自定义对象中,并通过“originalEvent”访问前者
      【解决方案6】:

      来自JQuery bug tracker page - 这是一个不错的 polyfill:

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

      .. 其中 e 是从 jquery 事件返回的事件。显然,只有当您的项目中已经有 Jquery 时,否则必须手动执行 offset() 的内容。

      【讨论】:

      • 哇。从所有答案中,只有这个对我有用。我已经用绝对定位的元素尝试过这个,非常感谢你!本主题最佳答案
      • var eoffsetX = (e.offsetX || e.clientX - $(e.target).offset().left + window.pageXOffset ), eoffsetY = (e.offsetY || e.clientY - $(e.target).offset().top + window.pageYOffset );如果您滚动了页面,此修复将有所帮助
      • 感谢@Denis,您添加y position 的回答确实帮助我解决了我的问题
      • 自回答以来,错误跟踪页面已更改。它说你应该使用e.pageX 而不是e.clientX。它也解决了滚动页面的问题。适用于最新的 Chrome、FF 和 IE。
      • 似乎FF,从39版开始,现在支持这个属性。好消息!
      【解决方案7】:

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

      经过大量试验和错误后,我得出的最终正确答案如下:

      //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,但我没有尝试过。

      【讨论】:

        【解决方案8】:

        但是,如果没有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;
          }
        

        【讨论】:

          【解决方案9】:

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

          if(!event.hasOwnProperty('offsetX')) {
              event.offsetX = event.layerX - event.currentTarget.offsetLeft;
              event.offsetY = event.layerY - event.currentTarget.offsetTop;
          }
          

          【讨论】:

          • @AdrianMaire - 你愿意指定哪些吗?
          【解决方案10】:

          offset 实际上并不直接翻译成layeroffset 属性不考虑元素的边距。下面的代码应该说明这一点。

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

          【讨论】:

            【解决方案11】:

            很遗憾,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;
            }
            

            【讨论】:

            • +1 表示非 jQuery 解决方案,它与 offsetXoffsetY 保持相同的语义。
            • 在 Firefox 26.0 下,事件 (e) 中没有名为“offsetParent”的字段,因此初始 e.offsetParent 不起作用(“if”被简单地忽略)。一个解决方案可能是让 e.target.offsetParent
            • @Alex e.target.offsetParent 也不起作用。你想出解决办法了吗?
            • + 1 - 这是唯一对我有用的解决方案,唯一的变化是使用 e.target.offsetParent 而不是 e.offsetParent。我正在使用 RaphaelJS 在地图中绘制对象。使用此代码,即使 e.target 有时是正在绘制的圆圈,有时是地图画布,我也总是能获得正确的鼠标坐标。
            【解决方案12】:

            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;
            

            或者,因为我们可以假设如果定义了offsetXoffsetY 也将是:

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

            【讨论】:

            • 这对我不起作用,因为 firefox 事件有 'offsetX/Y' 但两者都未定义。物有所值。
            • 糟糕的Mozilla!在这种情况下,请适当调整 - 感谢您的举报。
            • var x = e.offsetX || e.layerX || 0;是一个更简单的解决方案。
            • layerX 和 offsetX 不一样,如果目标是内联元素,偏移量给出了光标在元素中的位置,但是返回一个相对于他的非内联父容器的 layerX。
            猜你喜欢
            • 2013-06-22
            • 1970-01-01
            • 2017-02-28
            • 1970-01-01
            • 1970-01-01
            • 2021-08-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多