关于javascript:如何获取在画布元素上单击鼠标的坐标?

关于javascript:如何获取在画布元素上单击鼠标的坐标?

How do I get the coordinates of a mouse click on a canvas element?

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

不需要旧版浏览器兼容性,Safari,Opera和Firefox都可以。


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

由于画布并非总是相对于整个页面设置样式,因此canvas.offsetLeft/Top并不总是返回所需的内容。它将返回相对于其offsetParent元素偏移的像素数,该像素数可以类似于div元素,其中包含应用了position: relative样式的画布。为了解决这个问题,您需要遍历offsetParent的链,从canvas元素本身开始。该代码对我来说非常有效,已经在Firefox和Safari中进行了测试,但应该对所有人都有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;

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

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

如果您喜欢简单性,但仍然想要跨浏览器功能,那么我发现此解决方案最适合我。这是@Aldekein解决方案的简化,但没有jQuery。

1
2
3
4
5
6
7
8
9
10
11
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:这个答案已经很老了,它使用检查不再需要的旧浏览器,因为clientXclientY属性在所有当前浏览器中都有效。您可能想查看Patriques Answer,以获得更简单,最新的解决方案。

原始答案:
正如我当时发现的一篇文章中所述,但不再存在:

1
2
3
4
5
6
7
8
9
10
11
12
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,并通过点击处理程序传递事件。

1
2
3
function getRelativeCoords(event) {
    return { x: event.offsetX, y: event.offsetY };
}

大多数现代浏览器也支持layerX / Y,但是Chrome和IE使用layerX / Y来表示页面上的点击的绝对偏移量,包括边距,填充等。在Firefox中,layerX / Y和offsetX / Y是等效的,但是offset并没有以前不存在。因此,为了与稍旧的浏览器兼容,可以使用:

1
2
3
function getRelativeCoords(event) {
    return { x: event.offsetX || event.layerX, y: event.offsetY || event.layerY };
}


根据最新的Quirksmode,所有主要浏览器均支持clientXclientY方法。
因此,它来了-良好的工作代码可在带有滚动条的页面上的滚动div中工作:

1
2
3
4
5
6
7
8
9
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对于宽度(%)可变的画布的答案的一个小修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 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}
}

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

这篇古怪的方式文章提供了一个更正确的功能,可以使用pageX或pageY或将clientX与document.body.scrollLeft结合使用,以及将clientY与document.body.scrollTop结合使用,以计算相对于文档原点的单击坐标。

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


我不确定所有这些通过父元素循环并做各种奇怪事情的答案的意义何在。

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

(from MDN) The amount of scrolling that has been done of the viewport area (or
any other scrollable element) is taken into account when computing the
bounding rectangle

正常影像

最简单的方法已在此处发布。只要不涉及任何野生CSS规则,这都是正确的。

处理拉伸的画布/图像

当图像像素宽度与CSS宽度不匹配时,您需要对像素值应用一些比率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 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)。

处理CSS边框

如果画布的边框很粗,那么事情就会变得复杂起来。您实际上需要从边界矩形中减去边框。可以使用.getComputedStyle完成。此答案描述了过程。

然后,该函数会长大一些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* 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,只需更改该函数并用(canvas, event)调用(并将任何this替换为canvas)即可。


这是一个非常不错的教程-

HTML5 Canvas Mouse Coordinates Tutorial

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 <canvas id="myCanvas" width="578" height="200"></canvas>

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

希望这可以帮助!


因此,这既简单,又比看起来要复杂一些。

首先,这里通常会混淆一些问题

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

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

  • 所以,答案

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

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

    对于"如何获取画布相对鼠标坐标"这个问题,有2个简单的答案

    简单答案#1使用offsetXoffsetY

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

    此答案适用于Chrome,Firefox和Safari。与所有其他事件值不同,offsetXoffsetY将CSS转换考虑在内。

    offsetXoffsetY的最大问题是从2019/05开始,它们在触摸事件上不存在,因此不能与iOS Safari一起使用。它们确实存在于Chrome和Firefox中存在的Pointer Events中,但Safari中不存在,尽管Safari显然正在使用它。

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

    简单答案2使用clientXclientYcanvas.getBoundingClientRect

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

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

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

    1
    2
    3
    4
    5
    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.getBoundingClientRectclientXclientY

    1
    2
    3
    4
    5
    6
    7
    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;
    });

    或使用offsetXoffsetY

    1
    2
    3
    4
    5
    6
    canvas.addEventListener('mousemove', (e) => {
      const elementRelativeX = e.offsetX;
      const elementRelativeX = e.offsetY;
      const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
      const canvasRelativeY = elementRelativeX * canvas.height / canvas.clientHeight;
    });

    注意:在所有情况下,请勿在画布上添加填充或边框。这样做会使代码复杂化。而不是您希望边框或填充物将画布包裹在其他元素中,然后将填充物或边框添加到外部元素。

    使用event.offsetXevent.offsetY的工作示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    [...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}%)`;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    .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); }
    1
    2
    3
    4
    5
    6
    7
    8
    9
        <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>
     

    [cc lang="javascript"]

    使用canvas.getBoundingClientRectevent.clientXevent.clientY的工作示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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}%)`;
    }
    1
    canvas { background: #FED; }
    1
    2
    <canvas width="400" height="100" style="width: 300px; height: 200px"></canvas>
    canvas deliberately has differnt CSS size vs drawingbuffer size


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

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

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

    请注意,如果缩放画布,则这些坐标与画布逻辑坐标不同。要获得这些,您还可以这样做:

    1
    2
    3
    4
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <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);
      }

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

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


    您可以这样做:

    1
    2
    3
    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的演示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      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 };
      }

    我正在创建一个在pdf上具有画布的应用程序,其中涉及到许多画布的调整大小,例如放大和缩小pdf,然后依次打开每个PDF放大/缩小,我必须调整画布的大小以适应pdf的大小,我在stackOverflow中经历了很多答案,但没有找到最终解决问题的完美解决方案。

    我正在使用rxjs和angular 6,但未找到最新版本的任何答案。

    对于使用rxjs在画布上绘制的任何人来说,这都是有用的完整代码段。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
      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);
          });
      }

    此处是固定的代码段,无论您如何放大/缩小画布,鼠标相对于画布大小的坐标。

    1
    2
    3
    4
    5
    6
    7
    8
    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 )
    };

    首先,正如其他人所说,您需要一个函数来获取canvas元素的位置。这是一种比此页面上的其他方法(IMHO)更优雅的方法。您可以将其传递给任何元素,并获得其在文档中的位置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;
    }

    现在,计算光标相对于该光标的当前位置:

    1
    2
    3
    4
    5
    6
    7
    $('#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函数与事件处理代码分开。 (应该如此。我们应该尽量将我们的功能保持在一项任务之内。)

    offsetLeftoffsetTop的值是相对于offsetParent的,而offsetParent可能是包装器div节点(就此而言,可能是其他任何节点)。当没有元素包装canvas时,它们相对于body是相对的,因此没有要减去的偏移量。这就是为什么我们需要确定画布的位置之后才能做其他任何事情。

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

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

  • 当事件不在已定位的元素内发生时,这些值也相对于整个文档
  • 它们不属于任何标准

  • ThreeJS r77

    1
    2
    3
    4
    5
    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;

    经过尝试许多解决方案。这对我有用。可能会帮助其他人,因此发布。从这里得到


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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),
        };
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    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};
    }

    嘿,这是在dojo中,只是因为这就是我已经在一个项目中使用的代码。

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

    1
    2
    3
    4
    5
    6
      function onMouseClick(e) {
          var x = e.clientX;
          var y = e.clientY;
      }
      var canvas = dojo.byId(canvasId);
      dojo.connect(canvas,"click",onMouseClick);

    希望能有所帮助。


    推荐阅读