【问题标题】:How do I get the coordinates of a mouse click on a canvas element? [duplicate]如何获取鼠标单击画布元素的坐标? [复制]
【发布时间】:2010-09-08 12:19:21
【问题描述】:

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

无需兼容旧版浏览器,Safari、Opera 和 Firefox 都可以。

【问题讨论】:

  • 这与从普通 dom 元素获取鼠标事件应该没有什么不同。 quirksmode 对此有很好的参考。
  • 您上面列出的代码仅在画布不在其他容器深处时才有效。一般来说,您需要使用类似 jquery 偏移函数 [var testDiv = $('#testDiv'); var offset = testDiv.offset();] 以跨浏览器的方式获取正确的偏移量。这是***中的真正痛苦。
  • 如果包含画布的页面滚动,上面发布的更新代码将无法工作。
  • 我删除了作为问题更新包含的旧“答案”。如前所述,它已过时且不完整。
  • 由于这里有 50 个答案,我建议滚动到这些人的答案:patriques - 一个好的简单的 5 班轮。

标签: javascript canvas


【解决方案1】:

所以这既简单又比看起来的要复杂一些。

首先这里通常有混淆的问题

  1. 如何获取元素相对鼠标坐标

  2. 如何获取 2D Canvas API 或 WebGL 的画布像素鼠标坐标

所以,回答

如何获取元素相对鼠标坐标

无论元素是否是画布,获取元素相对鼠标坐标对于所有元素都是相同的。

“如何获取画布相对鼠标坐标”的问题有 2 个简单的答案

简单的答案 #1 使用 offsetX 和 offsetY

canvas.addEventListner('mousemove', (e) => {
  const x = e.offsetX;
  const y = e.offsetY;
});

此答案适用于 Chrome、Firefox 和 Safari。与所有其他事件值 offsetX 和 offsetY 不同,它们考虑了 CSS 转换。

offsetX 和 offsetY 的最大问题是截至 2019/05,它们在触摸事件中不存在,因此不能与 iOS Safari 一起使用。它们确实存在于指针事件中,这些事件存在于 Chrome 和 Firefox 中,但不存在于 Safari 中,尽管 apparently Safari is working on it。

另一个问题是事件必须在画布上。如果您将它们放在其他元素或窗口上,您以后无法选择画布作为您的参考点。

简单的答案 #2 使用 clientX、clientY 和 canvas.getBoundingClientRect

如果您不关心 CSS 转换,那么下一个最简单的答案是调用 canvas. getBoundingClientRect() 并从 clientX 和 top 中减去左侧,如

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
});

只要没有 CSS 转换,这将起作用。它也适用于触摸事件,因此适用于 Safari iOS

canvas.addEventListener('touchmove', (e) => {
  const rect = canvas. getBoundingClientRect();
  const x = e.touches[0].clientX - rect.left;
  const y = e.touches[0].clientY - rect.top;
});

如何为 2D Canvas API 获取画布像素鼠标坐标

为此,我们需要将上面得到的值从画布显示的大小转换为画布本身的像素数

canvas.getBoundingClientRect 和 clientX 和 clientY

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  const elementRelativeX = e.clientX - rect.left;
  const elementRelativeY = e.clientY - rect.top;
  const canvasRelativeX = elementRelativeX * canvas.width / rect.width;
  const canvasRelativeY = elementRelativeY * canvas.height / rect.height;
});

或者offsetX和offsetY

canvas.addEventListener('mousemove', (e) => {
  const elementRelativeX = e.offsetX;
  const elementRelativeY = e.offsetY;
  const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
  const canvasRelativeY = elementRelativeY * canvas.height / canvas.clientHeight;
});

注意:在所有情况下都不要在画布上添加填充或边框。这样做会使代码变得非常复杂。而不是您希望在其他元素中围绕画布使用边框或内边距,并将内边距和/或边框添加到外部元素。

使用event.offsetX、event.offsetY的工作示例

[...document.querySelectorAll('canvas')].forEach((canvas) => {
  const ctx = canvas.getContext('2d');
  ctx.canvas.width  = ctx.canvas.clientWidth;
  ctx.canvas.height = ctx.canvas.clientHeight;
  let count = 0;

  function draw(e, radius = 1) {
    const pos = {
      x: e.offsetX * canvas.width  / canvas.clientWidth,
      y: e.offsetY * canvas.height / canvas.clientHeight,
    };
    document.querySelector('#debug').textContent = count;
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
    ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
    ctx.fill();
  }

  function preventDefault(e) {
    e.preventDefault();
  }

  if (window.PointerEvent) {
    canvas.addEventListener('pointermove', (e) => {
      draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
    });
    canvas.addEventListener('touchstart', preventDefault, {passive: false});
    canvas.addEventListener('touchmove', preventDefault, {passive: false});
  } else {
    canvas.addEventListener('mousemove', draw);
    canvas.addEventListener('mousedown', preventDefault);
  }
});

function hsl(h, s, l) {
  return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
.scene {
  width: 200px;
  height: 200px;
  perspective: 600px;
}

.cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  animation-duration: 16s;
  animation-name: rotate;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes rotate {
  from { transform: translateZ(-100px) rotateX(  0deg) rotateY(  0deg); }
  to   { transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); }
}

.cube__face {
  position: absolute;
  width: 200px;
  height: 200px;
  display: block;
}

.cube__face--front  { background: rgba(255, 0, 0, 0.2); transform: rotateY(  0deg) translateZ(100px); }
.cube__face--right  { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); }
.cube__face--back   { background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); }
.cube__face--left   { background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); }
.cube__face--top    { background: rgba(0, 255, 255, 0.2); transform: rotateX( 90deg) translateZ(100px); }
.cube__face--bottom { background: rgba(255, 0, 255, 0.2); transform: rotateX(-90deg) translateZ(100px); }
<div class="scene">
  <div class="cube">
    <canvas class="cube__face cube__face--front"></canvas>
    <canvas class="cube__face cube__face--back"></canvas>
    <canvas class="cube__face cube__face--right"></canvas>
    <canvas class="cube__face cube__face--left"></canvas>
    <canvas class="cube__face cube__face--top"></canvas>
    <canvas class="cube__face cube__face--bottom"></canvas>
  </div>
</div>
<pre id="debug"></pre>

使用canvas.getBoundingClientRect 和event.clientX 和event.clientY 的工作示例

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.canvas.width  = ctx.canvas.clientWidth;
ctx.canvas.height = ctx.canvas.clientHeight;
let count = 0;

function draw(e, radius = 1) {
  const rect = canvas.getBoundingClientRect();
  const pos = {
    x: (e.clientX - rect.left) * canvas.width  / canvas.clientWidth,
    y: (e.clientY - rect.top) * canvas.height / canvas.clientHeight,
  };
  ctx.beginPath();
  ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
  ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
  ctx.fill();
}

function preventDefault(e) {
  e.preventDefault();
}

if (window.PointerEvent) {
  canvas.addEventListener('pointermove', (e) => {
    draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
  });
  canvas.addEventListener('touchstart', preventDefault, {passive: false});
  canvas.addEventListener('touchmove', preventDefault, {passive: false});
} else {
  canvas.addEventListener('mousemove', draw);
  canvas.addEventListener('mousedown', preventDefault);
}

function hsl(h, s, l) {
  return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
canvas { background: #FED; }
<canvas width="400" height="100" style="width: 300px; height: 200px"></canvas>
<div>canvas deliberately has differnt CSS size vs drawingbuffer size</div>

【讨论】:

  • 这对我来说效果很好。一条评论,代码的 offsetX/Y 版本在处理 Y 值时有两个拼写错误。固定行是: const elementRelativeY = e.offsetY; const canvasRelativeY = elementRelativeY * canvas.height / canvas.clientHeight;
  • 谢谢。固定...注意:您可以编辑答案。您的修改将被审核
  • 另见 Mozilla 的 mousedown event 示例
  • 2D 画布 API 上的第一个对我很有效 :)
【解决方案2】:

如果您喜欢简单但仍需要跨浏览器功能,我发现此解决方案最适合我。这是@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)
})

【讨论】:

  • 如果页面向下滚动,我想还应该考虑文档的滚动偏移量。
  • @PeppeL-G 边界客户矩形为您计算。您可以在发表评论之前轻松地在控制台中对其进行测试(这就是我所做的)。
  • @TomášZato,哦,getBoundingClientRect 返回相对于视口的位置?然后我的猜测是错误的。我从来没有测试过,因为这对我来说从来都不是问题,我很想警告其他读者我看到的一个潜在问题,但感谢您的澄清。
  • 如果画布被缩放,这不起作用。不确定这是否是浏览器错误。
  • 为像我这样的人添加用法:var canvas = document.getElementById('canvasID'); canvas.addEventListener("mousedown", function (e) { getCursorPosition(canvas, e);});
【解决方案3】:

我正在创建一个在 pdf 上具有 canvas 的应用程序,其中涉及大量调整画布的大小,例如放大和缩小 pdf,以及每次放大/缩小PDF 我不得不调整画布的大小以适应 pdf 的大小,我在 stackOverflow 中查看了很多答案,但没有找到最终解决问题的完美解决方案。

我使用的是 rxjs 和 angular 6,但没有找到任何特定于最新版本的答案。

这里是完整的 sn-p 代码,对任何使用 rxjs 在画布上绘图的人都有帮助。

  private captureEvents(canvasEl: HTMLCanvasElement) {

    this.drawingSubscription = fromEvent(canvasEl, 'mousedown')
      .pipe(
        switchMap((e: any) => {

          return fromEvent(canvasEl, 'mousemove')
            .pipe(
              takeUntil(fromEvent(canvasEl, 'mouseup').do((event: WheelEvent) => {
                const prevPos = {
                  x: null,
                  y: null
                };
              })),

              takeUntil(fromEvent(canvasEl, 'mouseleave')),
              pairwise()
            )
        })
      )
      .subscribe((res: [MouseEvent, MouseEvent]) => {
        const rect = this.cx.canvas.getBoundingClientRect();
        const prevPos = {
          x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
          y:  Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
        };
        const currentPos = {
          x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
          y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
        };

        this.coordinatesArray[this.file.current_slide - 1].push(prevPos);
        this.drawOnCanvas(prevPos, currentPos);
      });
  }

这里是固定的 sn-p,鼠标坐标相对于画布的大小,不管你如何放大/缩小画布。

const prevPos = {
  x: Math.floor( ( res[0].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
  y:  Math.floor( ( res[0].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};
const currentPos = {
  x: Math.floor( ( res[1].clientX - rect.left ) / ( rect.right - rect.left ) * this.cx.canvas.width ),
  y: Math.floor( ( res[1].clientY - rect.top ) / ( rect.bottom - rect.top ) * this.cx.canvas.height )
};

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      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;
      

      对我来说工作得很好。

      【讨论】:

      • 此解决方案并不总是有效 - Ryan Artecona 的答案适用于更一般的情况,即画布不一定相对于整个页面定位。
      • 如果性能很重要,则不适合此解决方案,因为每次单击/移动时都会完成 Dom 访问。而 getBoundingClientRect 现在已经存在,而且更加优雅。
      【解决方案6】:

      根据新鲜的Quirksmode 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];
      }
      

      对于$(canvas).offset(),这也需要jQuery。

      【讨论】:

      • 等同于@N4ppeL 答案。
      【解决方案7】:

      现代浏览器现在可以为您处理这个问题。 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 };
      }
      

      【讨论】:

      • 有趣的是如何在 Chrome 和 Firefox 上定义 layerX、layerY,但在 Chrome 上它是不准确的(或其他意思)。
      • @JulianMann 感谢您提供的信息。我已根据更多当前支持更新了此答案。看起来您现在几乎可以普遍使用 offsetX/Y。
      【解决方案8】:

      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;
      

      在尝试了许多解决方案之后。这对我有用。可能会帮助其他人因此发布。从here得到它

      【讨论】:

        【解决方案9】:

        更新(5/5/16):应该改用patriques' answer,因为它更简单、更可靠。


        由于画布的样式并不总是相对于整个页面,canvas.offsetLeft/Top 并不总是返回您需要的内容。它将返回相对于它的 offsetParent 元素偏移的像素数,它可以类似于 div 元素,其中包含应用了 position: relative 样式的画布。为了解决这个问题,您需要遍历offsetParents 链,从画布元素本身开始。这段代码非常适合我,在 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;
        

        最后一行便于获取相对于画布元素的鼠标坐标。获得有用坐标所需的只是

        coords = canvas.relMouseCoords(event);
        canvasX = coords.x;
        canvasY = coords.y;
        

        【讨论】:

        • 不,它使用的是内置的javascript原型对象---developer.mozilla.org/en/…
        • 我的 Chrome 具有 event.offsetX 和 event.offsetY 属性,因此我通过添加 if (event.offsetX !== undefined &amp;&amp; event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; } 修改了您的解决方案。看起来很有效。
        • Baczek 关于 Chrome 的 event.offsetX 和 event.offsetY 是正确的,这也适用于 IE9。对于 Firefox(经过 v13 测试),您可以使用 event.layerX 和 event.layerY。
        • 我还添加了这个:canvasX = event.pageX - totalOffsetX - document.body.scrollLeft; canvasY = event.pageY - totalOffsetY - document.body.scrollTop;
        • 答案的最终版本对我不起作用。在 Firefox 上,如果滚动整个屏幕,我将置换值作为输出。对我有用的是一个非常相似的解决方案,在stackoverflow.com/a/10816667/578749 上给出,而不是 event.pageX/Y,它从 event.clientX/Y 中减去计算出的偏移量。有人可以对此进行审查并解释吗?
        【解决方案10】:

        在 2016 年使用 jQuery,以获取相对于画布的点击坐标,我这样做:

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

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

        请注意,如果您的画布被缩放,那么这些坐标与画布逻辑坐标不同。要获得这些,您也会这样做:

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

        【讨论】:

        • 哇。 'jqEvent' 是如何定义的?还是“画布”?还是在第二个示例中的“坐标”?您是否需要在第二个示例之前运行第一个示例?在那种情况下,你为什么不写“为了得到那些,你也会这样做”?这一切都在 onclick 函数中还是什么?给一点背景,伙计。考虑到最初的问题是在 2008 年提出的,我认为您需要在 2008 年可用的技术背景下回答。使用当时可用版本(v1.2)中的有效 jQuery 优化您的答案。 ;)
        • 好的,对不起我的冒昧。我将编辑以删除它。我确实打算使用最新的框架来提供答案。而且我相信程序员不需要解释 jqEvent、canvas 和 coords 是什么。
        • 看起来不错。感谢您的输入!对不起,我让你难了! ;)
        【解决方案11】:

        我不确定loop through parent elements 和各种weird stuff 的所有这些答案有什么意义。

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

        (from MDN)视口区域(或 任何其他可滚动元素)在计算 边界矩形

        普通图片

        very simplest approach 已在此处发布。只要不涉及野 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];
        }
        

        只要画布没有边框,it works for stretched images (jsFiddle)。

        处理 CSS 边框

        如果画布有粗边框,the things get little complicated。您实际上需要从边界矩形中减去边框。这可以使用.getComputedStyle 来完成。这个answer describes the process。

        然后函数又长大了一点:

        /* 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。

        注意事项

        如果您不喜欢修改原生的prototypes,只需更改函数并使用(canvas, event) 调用它(并将任何this 替换为canvas)。

        【讨论】:

          【解决方案12】:

          首先,正如其他人所说,您需要一个函数来获取position of the canvas element。这是一种比此页面上的其他一些方法更优雅的方法(恕我直言)。您可以将 any 元素传递给它并获取它在文档中的位置:

          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 函数与事件处理代码分开。 (应该是这样。我们应该尽量让我们的功能每个都完成一项任务。)

          offsetLeft 和 offsetTop 的值与 offsetParent 相关,这可能是一些包装器 div 节点(或其他任何东西)。当没有包裹 canvas 的元素时,它们是相对于 body 的,因此没有要减去的偏移量。这就是为什么我们需要在做其他事情之前确定画布的位置。

          类似地,e.pageX 和e.pageY 给出了光标相对于文档的位置。这就是为什么我们从这些值中减去画布的偏移量以到达真实位置。

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

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

          【讨论】:

            【解决方案13】:

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

            【讨论】:

              【解决方案14】:

              你可以这样做:

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

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

              【讨论】:

                【解决方案15】:

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

                【讨论】:

                • x = new Number() 的意义何在?下面的代码重新分配了x,这意味着分配的号码立即被丢弃
                【解决方案16】:

                这是一个非常好的教程-

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

                希望这会有所帮助!

                【讨论】:

                • Ryan Artecona 的解决方案不适用于具有缩放功能的平板电脑浏览器。然而,这个做到了。
                • 不适用于覆盖了 CSS width/height 但仍然是最佳解决方案之一的图像。
                【解决方案17】:

                这是对上述 Ryan Artecona 解决方案的一些修改。

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

                【讨论】:

                  【解决方案18】:

                  以下是对具有可变 (%) 宽度的画布的 Ryan Artecona's answer 的小修改:

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

                  【讨论】:

                    【解决方案19】:

                    在 Prototype 中,如上面 Ryan Artecona 所述,使用cumulativeOffset() 进行递归求和。

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

                    【讨论】:

                      【解决方案20】:

                      我做了一个完整的演示,可以在每个浏览器中使用这个问题的解决方案的完整源代码:Coordinates of a mouse click on Canvas in Javascript。要尝试演示,请复制代码并将其粘贴到文本编辑器中。然后保存为example.html,最后用浏览器打开文件。

                      【讨论】:

                        【解决方案21】:

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

                        This quirks modearticle 提供了一个更正确的函数,可以使用 pageX 或 pageY 或 clientX 与 document.body.scrollLeft 和 clientY 与 document.body.scrollTop 的组合来计算相对于文档原点的点击坐标。

                        更新:此外,offsetLeft 和 offsetTop 是相对于元素的填充大小,而不是内部大小。应用了 padding: 样式的画布不会将其内容区域的左上角报告为 offsetLeft。这个问题有多种解决方案;最简单的可能是清除画布本身上的所有边框、填充等样式,然后将它们应用到包含画布的框。

                        【讨论】:

                          【解决方案22】:

                          嘿,这是在道场中,只是因为这是我已经为项目准备的代码。

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

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

                          希望对您有所帮助。

                          【讨论】:

                          • clientX/clientY 在浏览器中的行为不同。
                          猜你喜欢
                          • 1970-01-01
                          • 2018-11-29
                          • 1970-01-01
                          • 1970-01-01
                          • 2017-05-28
                          • 2022-08-18
                          • 1970-01-01
                          • 1970-01-01
                          • 2019-02-14
                          相关资源
                          最近更新 更多