找到鼠标相对于元素的位置

389

我想使用画布(canvas)制作一个小的绘画应用程序。因此,我需要在画布上找到鼠标的位置。


3
完整解决方案:http://acko.net/blog/mouse-handling-and-absolute-positions-in-javascript/您好,我会尽力为您提供准确的翻译。以上链接是有关JavaScript中鼠标操作和绝对位置的博客文章。 - edA-qa mort-ora-y
30个回答

5
canvas.onmousedown = function(e) {
    pos_left = e.pageX - e.currentTarget.offsetLeft;
    pos_top = e.pageY - e.currentTarget.offsetTop;
    console.log(pos_left, pos_top)
}

HTMLElement.offsetLeft

HTMLElement.offsetLeft是一个只读属性,返回当前元素左上角相对于HTMLElement.offsetParent节点的左侧偏移量(以像素为单位)。

对于块级元素,offsetTopoffsetLeftoffsetWidthoffsetHeight 描述的是相对于 offsetParent 的边框盒子。

然而,对于可以从一行换到下一行的内联元素(比如 span),offsetTopoffsetLeft 描述的是第一个边框盒子的位置(使用Element.getClientRects()获取其宽度和高度),而 offsetWidthoffsetHeight 描述的是边界边框盒子的尺寸(使用Element.getBoundingClientRect()获取其位置)。

HTMLElement.offsetTop

HTMLElement.offsetTop 是一个只读属性,返回当前元素相对于offsetParent节点顶部的距离(以像素为单位)。

MouseEvent.pageX

pageX 是一个只读属性,返回事件相对于整个文档的水平像素 X 坐标。该属性会考虑页面的任何水平滚动。

MouseEvent.pageY

pageY 是一个只读属性,返回事件相对于整个文档的垂直像素 Y 坐标。该属性会考虑页面的任何垂直滚动。

如需进一步解释,请参阅Mozilla开发者网络:

https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/pageX https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent/pageY https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/offsetLeft https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/offsetTop


虽然这段代码片段可能解决了问题,但包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议原因。 - Patrick Hund
我真的希望未来的读者有一个JS API可以为他们计算这个。;-) 这可能是最简单、完整、自包含的答案。注释自说明的代码只会分散注意力,浪费时间。 - kungfooman
我支持@PatrickHund的请求,希望能够得到解释。我不是CSS/JS专家,更多了解例如“page”和“offset”之间的关系对我非常有帮助。谢谢考虑这个请求! - cxw
@cxw 好的,你现在是第二个踩我的人了(之前有一个赞)...我现在添加了一个长长的解释,比那4行代码长10倍。不客气,希望你阅读愉快。我考虑了你的请求! :p - kungfooman
谢谢!非常有帮助 - 取消了踩。最后一个问题:MDN说offsetParent是最近的定位元素。这是否意味着如果除了整个文档之外还有其他定位父级,代码将会出错? - cxw
1
@cxw 我乱搞了一下,添加了一个额外的绝对定位元素,并强制将画布元素本身也移出屏幕,所以我不得不滚动到它,就像将其左侧/顶部偏移2000像素。无法使此代码出现问题。 - kungfooman

5

我认为以上答案都不令人满意,因此这是我使用的方法:

// Cross-browser AddEventListener
function ael(e, n, h){
    if( e.addEventListener ){
        e.addEventListener(n, h, true);
    }else{
        e.attachEvent('on'+n, h);
    }
}

var touch = 'ontouchstart' in document.documentElement; // true if touch device
var mx, my; // always has current mouse position IN WINDOW

if(touch){
    ael(document, 'touchmove', function(e){var ori=e;mx=ori.changedTouches[0].pageX;my=ori.changedTouches[0].pageY} );
}else{
    ael(document, 'mousemove', function(e){mx=e.clientX;my=e.clientY} );
}

// local mouse X,Y position in element
function showLocalPos(e){
    document.title = (mx - e.getBoundingClientRect().left)
        + 'x'
        + Math.round(my - e.getBoundingClientRect().top);
}

如果您需要知道页面当前的垂直滚动位置:

var yscroll = window.pageYOffset
        || (document.documentElement && document.documentElement.scrollTop)
        || document.body.scrollTop; // scroll Y position in page

5

来自这个教程,在顶部评论的帮助下进行了修正:

function getMousePos( canvas, evt ) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: Math.floor( ( evt.clientX - rect.left ) / ( rect.right - rect.left ) * canvas.width ),
        y: Math.floor( ( evt.clientY - rect.top ) / ( rect.bottom - rect.top ) * canvas.height )
    };
}

在画布上使用如下:

var canvas = document.getElementById( 'myCanvas' );
canvas.addEventListener( 'mousemove', function( evt ) {
    var mousePos = getMousePos( canvas, evt );
} );

4

const findMousePositionRelativeToElement = (e) => {
    const xClick = e.clientX - e.currentTarget.offsetLeft;
    const yClick = e.clientY - e.currentTarget.offsetTop;
    console.log(`x: ${xClick}`);
    console.log(`y: ${yClick}`);

    // or
    const rect = e.currentTarget.getBoundingClientRect();
    const xClick2 = e.clientX - rect.left;
    const yClick2 = e.clientY - rect.top;
    console.log(`x2: ${xClick2}`);
    console.log(`y2: ${yClick2}`);
}


3

基于@Spider的解决方案,我的非JQuery版本如下:

// Get the container element's bounding box
var sides = document.getElementById("container").getBoundingClientRect();

// Apply the mouse event listener
document.getElementById("canvas").onmousemove = (e) => {
  // Here 'self' is simply the current window's context
  var x = (e.clientX - sides.left) + self.pageXOffset;
  var y = (e.clientY - sides.top) + self.pageYOffset;
}

这适用于滚动和缩放(在这种情况下有时返回浮点数)。


我会点赞的,但是页面偏移量不应该是减号吗?要不然就是第二部分周围的括号? - Chris Sunami

3

我知道我有点晚了,但这个方法使用纯JavaScript实现,即使元素比视口大并且用户已经滚动,它也能给你指针在元素内的坐标。

var element_offset_x ; // The distance from the left side of the element to the left of the content area


....// some code here (function declaration or element lookup )



element_offset_x = element.getBoundingClientRect().left  -  document.getElementsByTagName("html")[0].getBoundingClientRect().left  ;

....// code here 




function mouseMoveEvent(event) 
{
   var pointer_location = (event.clientX + window.pageXOffset) - element_offset_x ; 
}

它的工作原理如下。
首先,我们要获取HTML元素(内容区域)相对于当前视口的位置。如果页面有滚动条并且已滚动,则对于HTML标记返回的 getBoundingClientRect().left 的数字将为负数。然后我们使用此数字计算元素与内容区域左侧之间的距离。使用 element_offset_x = element.getBoundingClientRect().left......; 了解元素与内容区域的距离后,event.clientX 就可以给出指针与视口的距离。重要的是要了解视口和内容区域是两个不同的实体,如果页面滚动,视口会移动。因此,即使页面滚动,clientX也将返回相同的数字。
为了弥补这一点,我们需要将指针的x位置(相对于视口)加上视口的x位置(相对于内容区域)。视口的X位置可以通过window.pageXOffset找到。

1
感谢您作为唯一考虑滚动的回答者。请考虑修改您的答案,同时添加y轴。 - frabjous

2
您可以使用相对定位父元素的 getBoundingClientRect() 方法。
document.addEventListener("mousemove", (e) => {
  let xCoord = e.clientX - e.target.getBoundingClientRect().left + e.offsetX
  let yCoord = e.clientY - e.target.getBoundingClientRect().top + e.offsetY
  console.log("xCoord", xCoord, "yCoord", yCoord)
})

2

使用此方法快速获取鼠标位置:

Object.defineProperty(MouseEvent.prototype, "mouseX", {
   get() {
      return this.clientX - this.currentTarget.getBoundingClientRect().left;
   }
});
Object.defineProperty(MouseEvent.prototype, "mouseY", {
   get() {
      return this.clientY - this.currentTarget.getBoundingClientRect().top;
   }
});

例子:

document.body.onmousemove=function(e){console.log(e.mouseX,e.mouseY)}

1
你可以使用jQuery的event.pageXevent.pageY方法,结合jQuery的offset()方法,获取鼠标相对于元素的位置。
  $(document).ready(function() {
    $("#myDiv").mousemove(function(event){            
      var X = event.pageX - $(this).offset().left;
      var Y = event.pageY - $(this).offset().top;
      $(".cordn").text("(" + X + "," + Y + ")");
    });
  });

你可以在这里看到一个例子:如何寻找鼠标相对于元素的位置

1
并不是每个人都使用jQuery。 - Hg0428

1

我实现了另一种解决方案,我认为非常简单,所以想和大家分享。

对我来说,问题是拖动的div会跳到鼠标光标的0,0位置。因此,我需要捕获div上鼠标的位置来调整div的新位置。

我读取了div的PageX和PageY,并根据它们设置了相应的top和left,然后为了获取值以调整坐标以保持光标在可拖动div中的初始位置,我使用onDragStart监听器并存储e.nativeEvent.layerXe.nativeEvent.layerY,只有在初始触发时才会给出鼠标在可拖动div内的位置。

示例代码:

 onDrag={(e) => {
          let newCoords;
          newCoords = { x: e.pageX - this.state.correctionX, y: e.pageY - this.state.correctionY };
          this.props.onDrag(newCoords, e, item.id);
        }}
        onDragStart={
          (e) => {
            this.setState({
              correctionX: e.nativeEvent.layerX,
              correctionY: e.nativeEvent.layerY,
            });
          }

我希望这能帮助那些经历了与我相同问题的人 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接