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

不需要传统浏览器兼容性,Safari、Opera和Firefox就可以了。


当前回答

三JS 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;

在尝试了许多解决方案后。这对我很管用。可能会帮助其他人,因此发帖。从这里开始

其他回答

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

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

你可以这样做:

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

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

我在创建一个应用程序,在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 )
};

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

首先,正如其他人所说,您需要一个函数来获取canvas元素的位置。这里有一个方法,它比本页上的其他一些方法更优雅。你可以将任何元素传递给它,并获得它在文档中的位置:

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节点(或者其他任何东西)。当没有元素包裹画布时,它们是相对于主体的,因此没有要减去的偏移量。这就是为什么我们需要在做其他事情之前确定画布的位置。

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

定位元素的另一种选择是直接使用e.layerX和e.layerY的值。这种方法不如上面的方法可靠,有两个原因:

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