如何在画布元素上获得鼠标点击的坐标?

时间:2008-09-11 02:07:45

标签: javascript canvas

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

不需要传统的浏览器兼容性,Safari,Opera和Firefox都可以。

22 个答案:

答案 0 :(得分:175)

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


由于画布并不总是相对于整个页面设置样式,canvas.offsetLeft/Top并不总是返回您需要的内容。它将返回相对于其offsetParent元素偏移的像素数,这可能类似于包含应用了div样式的画布的position: relative元素。为了解决这个问题,你需要循环遍历offsetParent的链,从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;

答案 1 :(得分:153)

如果你喜欢简单但又想要跨浏览器功能,我发现这个解决方案最适合我。这是@Aldekein解决方案的简化,但没有jQuery

function getCursorPosition(canvas, event) {
    var rect = canvas.getBoundingClientRect();
    var x = event.clientX - rect.left;
    var y = event.clientY - rect.top;
    console.log("x: " + x + " y: " + y);
}

答案 2 :(得分:71)

编辑2018:此答案相当陈旧,它使用了对不再需要的旧浏览器的检查,因为clientXclientY属性适用于所有当前浏览器。您可能需要查看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;

对我来说工作得非常好。

答案 3 :(得分:27)

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

答案 4 :(得分:17)

根据新的Quirksmode,所有主流浏览器都支持clientXclientY方法。 所以,在这里它是 - 在带有滚动条的页面上的滚动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

答案 5 :(得分:13)

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

答案 6 :(得分:10)

以下是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}
}

答案 7 :(得分:7)

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

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

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

答案 8 :(得分:7)

我不确定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

注释

如果您不想修改原生prototype,只需更改该功能并使用(canvas, event)进行调用(并将所有this替换为canvas

答案 9 :(得分:6)

这是一个非常好的教程 -

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

希望这有帮助!

答案 10 :(得分:3)

在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())
}

答案 11 :(得分:2)

我建议这个链接 - 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>

答案 12 :(得分:1)

你可以这样做:

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

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

答案 13 :(得分:1)

请参阅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 :(得分:1)

在Prototype中,使用cumulativeOffset()进行Ryan Artecona上面提到的递归求和。

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

答案 15 :(得分:1)

这既是简单的话题,又比看起来要复杂的话题。

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

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

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

所以,答案

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

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

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

简单答案#1使用offsetXoffsetY

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

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

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

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

简单答案#2使用clientXclientYcanvas.getBoundingClientRect

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

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.getBoundingClientRectclientXclientY

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

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

的工作示例

[...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.getBoundingClientRectevent.clientXevent.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>

答案 16 :(得分:0)

嘿,这是在dojo中,只是因为我已经为项目提供了代码。

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

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

希望有所帮助。

答案 17 :(得分:0)

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

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函数与事件处理代码分开。 (因为它应该是。我们应该尝试将每个函数保持为一个任务。)

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

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

定位元素的替代方法是直接使用e.layerXe.layerY的值。由于两个原因,这不如上述方法可靠:

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

答案 18 :(得分:0)

以上是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};
}

答案 19 :(得分:0)

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

获得

答案 20 :(得分:0)

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

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

答案 21 :(得分:0)

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

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

对于那些利用 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);
      });
  }

这是修复的代码段,无论您如何放大/缩小画布,鼠标相对于画布大小的坐标。

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