ChatGPT解决这个技术问题 Extra ChatGPT

如何获取鼠标单击画布元素的坐标? [复制]

这个问题在这里已经有了答案:Real mouse position in canvas (5 answers) Closed 12 months ago。

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

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

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

p
patriques

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

更新(2016 年 5 月 5 日):应该改用 patriques' answer,因为它更简单、更可靠。

由于画布的样式并不总是相对于整个页面,因此 canvas.offsetLeft/Top 并不总是返回您需要的内容。它将返回相对于其 offsetParent 元素偏移的像素数,该元素可能类似于包含应用了 position: relative 样式的画布的 div 元素。考虑到这一点,您需要遍历 offsetParent 链,从画布元素本身开始。这段代码非常适合我,在 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.offsetXevent.offsetY 属性,因此我通过添加 if (event.offsetX !== undefined && event.offsetY !== undefined) { return {x:event.offsetX, y:event.offsetY}; } 修改了您的解决方案。看起来它有效。
Baczek 关于 Chrome 的 event.offsetXevent.offsetY 是正确的,这也适用于 IE9。对于 Firefox(使用 v13 测试),您可以使用 event.layerXevent.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 中减去计算出的偏移量。有人可以对此进行审查并解释吗?
N
N4ppeL

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;

对我来说工作得很好。


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

现代浏览器现在为您处理这个问题。 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 了。
A
Aliaksandr Sushkevich

根据新的 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


相当于@N4ppeL 答案。
g
gman

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

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

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

所以,答案

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

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

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

简单答案 #1 使用 offsetX 和 offsetY

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

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

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

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

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

如果您不关心 CSS 转换,下一个最简单的答案是调用 canvas. getBoundingClientRect() 并从 clientX 中减去左侧,从 clientY 中减去 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.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 elementRelativeY = e.offsetY;
  const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
  const canvasRelativeY = elementRelativeY * 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); } });函数 hsl(h, s, l) { return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`; } .scene { 宽度:200px;高度:200px;视角:600px; } .cube { 宽度:100%;高度:100%;位置:相对;变换样式:preserve-3d;动画持续时间:16s;动画名称:旋转;动画迭代计数:无限;动画定时功能:线性; } @keyframes 旋转 { 从 { 变换: translateZ(-100px) rotateX( 0deg) rotateY( 0deg); } 到 { 变换: translateZ(-100px) rotateX(360deg) rotateY(720deg); } } .cube__face { 位置:绝对;宽度:200px;高度:200px;显示:块; } .cube__face--front { 背景:rgba(255, 0, 0, 0.2);变换:rotateY(0deg) translateZ(100px); } .cube__face--right { 背景:rgba(0, 255, 0, 0.2);变换:rotateY(90deg) translateZ(100px); } .cube__face--back { 背景:rgba(0, 0, 255, 0.2);变换:rotateY(180deg) translateZ(100px); } .cube__face--left { 背景:rgba(255, 255, 0, 0.2);变换:rotateY(-90deg) translateZ(100px); } .cube__face--top { 背景:rgba(0, 255, 255, 0.2);变换:rotateX(90deg) translateZ(100px); } .cube__face--bottom { 背景:rgba(255, 0, 255, 0.2);变换:rotateX(-90deg) translateZ(100px); }

< canvas class="cube__face cube__face--right">

                			                   			

使用 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;让计数 = 0;函数绘制(e,半径= 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, 半径, 0, Math.PI * 2); ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5); ctx.fill(); } 功能 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); } 函数 hsl(h, s, l) { return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`; } 画布 { 背景:#FED; }

canvas 故意有不同的 CSS 大小和绘图缓冲区大小


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

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


C
Community

下面是对具有可变 (%) 宽度的画布的 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}
}

s
scunliffe

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

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

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


C
Community

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

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

(来自 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)。


F
FraZer

这是一个非常好的教程-

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 但仍是最佳解决方案之一的图像。
S
Sarsaparilla

在 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 是什么。
看起来不错。感谢您的输入!对不起,我让你难了! ;)
佚名

我推荐这个链接 - 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,这意味着分配的 Number 立即被丢弃
J
Jesse Smith

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

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


u
user2310569

你可以这样做:

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

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


D
Daniel Patru

请参阅 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 };
  }

D
Divyanshu Rawat

我正在创建一个在 pdf 上具有画布的应用程序,这涉及到很多画布的大小调整,例如放大和缩小 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 )
};

S
Simon Hi

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

W
Wayne

首先,正如其他人所说,您需要一个函数来获取 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 函数与事件处理代码分开。 (应该是这样。我们应该尽量让我们的功能每个都完成一项任务。)

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

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

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

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


A
Aniket Betkikar

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得到它


b
bb216b3acfd8f72cbc8f899d4d6963

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

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

B
Brian Gianforcaro

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

如何将其转换回非 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 在浏览器中的行为不同。