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

我想做一个小绘画应用程序使用画布。所以我需要找到鼠标在画布上的位置。

419069 次浏览

你可以买到它

var element = document.getElementById(canvasId);
element.onmousemove = function(e) {
var xCoor = e.clientX;
var yCoor = e.clientY;
}

关于这个问题的困难可以在这里找到:http://www.quirksmode.org/js/events_properties.html#position

使用这里描述的技术,您可以在文档中找到鼠标的位置。然后你只需检查它是否在元素的包围框内,你可以通过调用element.getBoundingClientRect()来找到它,它将返回一个具有以下属性的对象:{ bottom, height, left, right, top, width }。由此,判断偶数是否发生在元素内部是很简单的。

下面计算鼠标位置与canvas元素的关系:

const example = document.getElementById('example');


example.onmousemove = function(e) {
const x = e.pageX - e.currentTarget.offsetLeft;
const y = e.pageY - e.currentTarget.offsetTop;
}

对于使用JQuery的人:

有时,当您嵌套元素时,其中一个元素附加了事件,可能会让您难以理解浏览器将什么视为父元素。在这里,您可以指定哪个父节点。

取鼠标位置,然后从父元素的偏移位置中减去它。

var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;

如果你想在滚动窗格中获取页面上的鼠标位置:

var x = (evt.pageX - $('#element').offset().left) + self.frame.scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + self.frame.scrollTop();

或相对于页面的位置:

var x = (evt.pageX - $('#element').offset().left) + $(window).scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();

注意以下性能优化:

var offset = $('#element').offset();
// Then refer to
var x = evt.pageX - offset.left;

这样,JQuery就不必为每一行查找#element

更新

在@anytimecoder的一个答案中有一个更新的、仅支持javascript的版本——也可以参见浏览器对getBoundingClientRect()的支持

Mark van Wyk的回答让我找到了正确的方向,但并没有完全解决我的问题。我仍然在另一个元素中包含的元素中绘画。

以下解决了它为我:

        x = e.pageX - this.offsetLeft - $(elem).offset().left;
y = e.pageY - this.offsetTop - $(elem).offset().top;

换句话说,我简单地堆叠了所有嵌套元素的偏移量

在纯javascript中没有答案,当reference元素嵌套在其他可以具有绝对定位的元素中时,返回相对坐标。下面是针对这种情况的解决方案:

function getRelativeCoordinates (event, referenceElement) {


const position = {
x: event.pageX,
y: event.pageY
};


const offset = {
left: referenceElement.offsetLeft,
top: referenceElement.offsetTop
};


let reference = referenceElement.offsetParent;


while(reference){
offset.left += reference.offsetLeft;
offset.top += reference.offsetTop;
reference = reference.offsetParent;
}


return {
x: position.x - offset.left,
y: position.y - offset.top,
};


}

在我看来,上述答案都不令人满意,所以我用的是:

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

如果你需要知道页面当前的Y轴滚动位置:

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

因为我没有找到一个jquery免费的答案,我可以复制/粘贴,这里是我使用的解决方案:

document.getElementById('clickme').onclick = function clickEvent(e) {
// e = Mouse click event.
var rect = e.target.getBoundingClientRect();
var x = e.clientX - rect.left; //x position within the element.
var y = e.clientY - rect.top;  //y position within the element.
console.log("Left? : " + x + " ; Top? : " + y + ".");
}
#clickme {
margin-top: 20px;
margin-left: 100px;
border: 1px solid black;
cursor: pointer;
}
<div id="clickme">Click Me -<br>
(this box has margin-left: 100px; margin-top: 20px;)</div>

JSFiddle的完整示例

摘自本教程,由于上面的注释做了更正:

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

你必须知道你的页面的结构,因为如果你的canvas是一个div的子div又是另一个div的子…然后,故事变得更加复杂。下面是我在2层div中创建一个canvas的代码:

canvas.addEventListener("click", function(event) {
var x = event.pageX - (this.offsetLeft + this.parentElement.offsetLeft);
var y = event.pageY - (this.offsetTop + this.parentElement.offsetTop);
console.log("relative x=" + x, "relative y" + y);

});

我意识到我有点晚了,但这与纯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位置(相对于内容区域)。viewport的X位置是通过 window.pageXOffset.找到的

原来的答案是把它放在一个iframe中。更好的解决方案是在填充设置为0px的画布上使用事件offsetX和offsetY。

<html>
<body>
<script>


var main=document.createElement('canvas');
main.width="200";
main.height="300";
main.style="padding:0px;margin:30px;border:thick dashed red";
document.body.appendChild(main);


// adding event listener


main.addEventListener('mousemove',function(e){
var ctx=e.target.getContext('2d');
var c=Math.floor(Math.random()*0xFFFFFF);
c=c.toString(16); for(;c.length<6;) c='0'+c;
ctx.strokeStyle='#'+c;
ctx.beginPath();
ctx.arc(e.offsetX,e.offsetY,3,0,2*Math.PI);
ctx.stroke();
e.target.title=e.offsetX+' '+e.offsetY;
});


// it worked! move mouse over window


</script>
</body>
</html>

通过事件可以获得画布内的鼠标坐标。offsetX和事件。下面是一个小片段来证明我的观点:

c=document.getElementById("c");
ctx=c.getContext("2d");
ctx.fillStyle="black";
ctx.fillRect(0,0,100,100);
c.addEventListener("mousemove",function(mouseEvt){
// the mouse's coordinates on the canvas are just below
x=mouseEvt.offsetX;
y=mouseEvt.offsetY;
// the following lines draw a red square around the mouse to prove it
ctx.fillStyle="black";
ctx.fillRect(0,0,100,100);
ctx.fillStyle="red";
ctx.fillRect(x-5,y-5,10,10);
});

body {
background-color: blue;
}


canvas {
position: absolute;
top: 50px;
left: 100px;
}
<canvas id="c" width="100" height="100"></canvas>

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()获得其位置)。因此,一个左、上、宽、高分别为offsetLeftoffsetTopoffsetWidthoffsetHeight的框,对于包含换行文本的span来说,将不是一个边界框。

HTMLElement.offsetTop

HTMLElement.offsetTop只读属性返回当前元素相对于offsetParent节点顶部的距离。

MouseEvent.pageX

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

MouseEvent.pageY

MouseEvent.pageY只读属性返回事件相对于整个文档的Y(垂直)像素坐标。此属性考虑页面的任何垂直滚动。

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

https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop < / p >

基于@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;
}

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

我遇到了这个问题,但为了使它工作在我的情况下(使用拖拽dom元素(不是画布在我的情况下)),我发现你只需要使用offsetXoffsetY在拖拽鼠标事件。

onDragOver(event){
var x = event.offsetX;
var y = event.offsetY;
}

对于那些为移动设备和/或笔记本电脑/触摸屏显示器开发常规网站或PWAs(渐进式Web应用程序)的人来说,你可能已经登陆这里了,因为你可能已经习惯了鼠标事件,对触摸事件的痛苦体验还不熟悉……耶!

只有3条规则:

  1. mousemovetouchmove事件期间尽量少做。
  2. 尽可能在mousedowntouchstart事件期间进行。
  3. 取消传播并防止触摸事件的默认值,以防止鼠标事件也在混合设备上触发。

不用说,使用touch事件会更复杂,因为可以有多个事件,而且它们比鼠标事件更灵活(复杂)。这里我只讲一个触碰。是的,我很懒,但这是最常见的触摸方式。

var posTop;
var posLeft;
function handleMouseDown(evt) {
var e = evt || window.event; // Because Firefox, etc.
posTop = e.target.offsetTop;
posLeft = e.target.offsetLeft;
e.target.style.background = "red";
// The statement above would be better handled by CSS
// but it's just an example of a generic visible indicator.
}
function handleMouseMove(evt) {
var e = evt || window.event;
var x = e.offsetX; // Wonderfully
var y = e.offsetY; // Simple!
e.target.innerHTML = "Mouse: " + x + ", " + y;
if (posTop)
e.target.innerHTML += "<br>" + (x + posLeft) + ", " + (y + posTop);
}
function handleMouseOut(evt) {
var e = evt || window.event;
e.target.innerHTML = "";
}
function handleMouseUp(evt) {
var e = evt || window.event;
e.target.style.background = "yellow";
}
function handleTouchStart(evt) {
var e = evt || window.event;
var rect = e.target.getBoundingClientRect();
posTop = rect.top;
posLeft = rect.left;
e.target.style.background = "green";
e.preventDefault(); // Unnecessary if using Vue.js
e.stopPropagation(); // Same deal here
}
function handleTouchMove(evt) {
var e = evt || window.event;
var pageX = e.touches[0].clientX; // Touches are page-relative
var pageY = e.touches[0].clientY; // not target-relative
var x = pageX - posLeft;
var y = pageY - posTop;
e.target.innerHTML = "Touch: " + x + ", " + y;
e.target.innerHTML += "<br>" + pageX + ", " + pageY;
e.preventDefault();
e.stopPropagation();
}
function handleTouchEnd(evt) {
var e = evt || window.event;
e.target.style.background = "yellow";
// Yes, I'm being lazy and doing the same as mouseout here
// but obviously you could do something different if needed.
e.preventDefault();
e.stopPropagation();
}
div {
background: yellow;
height: 100px;
left: 50px;
position: absolute;
top: 80px;
user-select: none; /* Disable text selection */
-ms-user-select: none;
width: 100px;
}
<div
onmousedown="handleMouseDown()"
onmousemove="handleMouseMove()"
onmouseout="handleMouseOut()"
onmouseup="handleMouseUp()"
ontouchstart="handleTouchStart()"
ontouchmove="handleTouchMove()"
ontouchend="handleTouchEnd()">
</div>
Move over box for coordinates relative to top left of box.<br>
Hold mouse down or touch to change color.<br>
Drag to turn on coordinates relative to top left of page.

更喜欢使用Vue.js?我做!然后你的HTML看起来是这样的:

<div @mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@touchstart.stop.prevent="handleTouchStart"
@touchmove.stop.prevent="handleTouchMove"
@touchend.stop.prevent="handleTouchEnd">

我尝试了所有这些解决方案,由于我的特殊设置与矩阵转换容器(panzoom库)没有工作。这将返回正确的值,即使缩放和窗格:

mouseevent(e) {
const x = e.offsetX,
y = e.offsetY
}

但前提是没有子元素。这可以通过使用CSS使它们对事件“不可见”来规避:

.child {
pointer-events: none;
}

这是我得到的。

    $(".some-class").click(function(e) {


var posx = 0;
var posy = 0;


posx = e.pageX;
posy = e.pageY;


alert(posx);
alert(posy);
});

我实现了另一个解决方案,我认为很简单,所以我想和你们分享一下。

所以,对我来说,问题是拖动的div将跳转到0,0的鼠标光标。所以我需要捕捉鼠标在div上的位置来调整div的新位置。

我读取div PageX和PageY,并设置的顶部和左侧的根据,然后得到的值调整坐标,以保持光标在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,
});
}

我希望这能帮助那些和我经历过同样问题的人:)

function myFunction(e) {
var x =  e.clientX - e.currentTarget.offsetLeft ;
var y = e.clientY - e.currentTarget.offsetTop ;
}

这可以正常工作!

你可以使用父类relativegetBoudingClientRect()

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

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


基于@帕特里克·布斯的解决方案,但修复了中间滚动条的潜在问题。

export function getRelativeCoordinates(event: MouseEvent, referenceElement: HTMLElement) {
const position = {
x: event.pageX,
y: event.pageY,
};


const offset = {
left: referenceElement.offsetLeft,
top: referenceElement.offsetTop,
};


let reference = referenceElement.offsetParent as HTMLElement;


while (reference) {
offset.left += reference.offsetLeft;
offset.top += reference.offsetTop;
reference = reference.offsetParent as HTMLElement;
}


const scrolls = {
left: 0,
top: 0,
};


reference = event.target as HTMLElement;
while (reference) {
scrolls.left += reference.scrollLeft;
scrolls.top += reference.scrollTop;
reference = reference.parentElement as HTMLElement;
}


return {
x: position.x + scrolls.left - offset.left,
y: position.y + scrolls.top - offset.top,
};
}

我必须得到光标的位置在一个非常宽的div与滚动条。目标是将元素拖动到div的任意位置。

将鼠标位置放在滚动画面深处的较远位置。

$('.canvas').on('mousemove', function(e){
$(dragElement).parent().css('top', e.currentTarget.scrollTop + e.originalEvent.clientY );
$(dragElement).parent().css('left', e.currentTarget.scrollLeft + e.originalEvent.clientX )
});

你可以简单地使用jQuery的event.pageXevent.pageY,并使用jQuery的抵消()方法来获得鼠标相对于元素的位置。

  $(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 + ")");
});
});

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

如果你想获得与一个元素相关的layerX和layerY,也许你可以尝试:

let bbox_rect = document.getElementById("dom-ID").getBoundingClientRect()
let layerX = e.clientX-bbox_rect.left
let layerY = e.clientY-bbox_rect.top

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

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

因为我没有找到一个解决方案,可以帮助你得到它,如果你把它附加到一个父母元素,你有一个例如选择。

这就是我所做的:

let positions = {
x: event.pageX,
y: event.pageY - event.currentTarget.getBoundingClientRect().top + event.currentTarget.offsetTop
}