我想使用画布制作一个绘画应用程序。因此,我需要找到鼠标在画布上的位置。
我想使用画布制作一个绘画应用程序。因此,我需要找到鼠标在画布上的位置。
Answers:
对于使用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为支持getBoundingClientRect()的浏览器提供了一个更高的纯JavaScript版本。
var y = (evt.pageY - $('#element').offset().left) + $(window).scrollTop();
为var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();
由于找不到可复制/粘贴的无jQuery答案,因此使用了以下解决方案:
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.
}
e.currentTarget
,而不是e.target。否则,儿童元素会影响它
以下计算鼠标与画布元素的位置关系:
var example = document.getElementById('example');
example.onmousemove = function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
}
在此示例中,this
引用example
元素,并且e
是onmousemove
事件。
var example = document.getElementById('example'); example.onmousemove = function(e) { var X = e.pageX - this.offsetLeft; var Y = e.pageY - this.offsetTop; }
this
到e.currentTarget
,这将使您相对于向其添加事件侦听器的元素的位置。
当参考元素嵌套在可以绝对定位的其他元素内时,纯JavaScript没有答案会返回相对坐标。这是此方案的解决方案:
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,
};
}
html
元素偏移量
while(reference !== undefined)
为while(reference)
。至少在Chrome中,offsetParent最终不是undefined
,而是null
。仅检查是否reference
为真将确保它与undefined
和都适用null
。
offsetParent
属性,这在其他情况下也非常有用!
scrollLeft
并scrollTop
补偿滚动后代。
可以在以下位置找到有关此问题的难度的完整文章:http : //www.quirksmode.org/js/events_properties.html#position
使用此处描述的技术,您可以在文档中找到鼠标的位置。然后,您只需检查它是否在元素的边界框中即可,您可以通过调用边界框来找到element.getBoundingClientRect()
它,该方法将返回具有以下属性的对象:{ bottom, height, left, right, top, width }
。从那里很容易判断出偶数是否发生在您的元素内部。
我尝试了所有这些解决方案,并且由于我对矩阵转换的容器(panzoom库)进行了特殊设置而无法使用。即使缩放和平移,这也会返回正确的值:
mouseevent(e) {
const x = e.offsetX,
y = e.offsetY
}
但前提是没有子元素在路上。可以通过使用CSS使事件“不可见”来规避此问题:
.child {
pointer-events: none;
}
我+1了范·范·维克(Mark van Wyk)的答案,因为它可以使我朝正确的方向发展,但并不能完全解决我的问题。在包含在另一个元素中的元素上绘画时,我仍然有偏差。
以下为我解决了这个问题:
x = e.pageX - this.offsetLeft - $(elem).offset().left;
y = e.pageY - this.offsetTop - $(elem).offset().top;
换句话说-我只是堆叠所有嵌套元素的所有偏移量
上面的答案都不是令人满意的IMO,因此,我使用的是:
// 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
对于那些为带触摸屏的移动设备和/或笔记本电脑/显示器开发常规网站或PWA(渐进式Web应用程序)的人来说,您之所以落在这里是因为您可能习惯于鼠标事件,并且不熟悉Touch有时带来的痛苦体验事件...是的!
只有3条规则:
mousemove
或touchmove
事件期间,尽可能少做。mousedown
或touchstart
事件期间尽可能多地做。不用说,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">
你可以得到它
var element = document.getElementById(canvasId);
element.onmousemove = function(e) {
var xCoor = e.clientX;
var yCoor = e.clientY;
}
摘自本教程,并通过顶部注释进行了更正:
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 );
} );
我意识到我来晚了一点,但是这适用于PURE 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元素(内容区域)相对于当前视口的位置。如果页面具有滚动条并被滚动,则返回的数字getBoundingClientRect().left
html标记将为负。然后,我们使用此数字来计算元素与内容区域左侧之间的距离。用element_offset_x = element.getBoundingClientRect().left......;
知道元素与内容区域的距离。event.clientX
给我们指针从视口的距离。重要的是要了解视口和内容区域是两个不同的实体,如果滚动页面,视口可以移动。因此,即使滚动页面,clientX也将返回相同的编号。
为了弥补这一点,我们需要将指针的x位置(相对于视口)添加到视口的x位置(相对于内容区域)。找到视口的X位置 window.pageXOffset.
基于@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;
}
这对滚动和缩放都有效(在这种情况下有时返回浮点数)。
借助event.offsetX和event.offsetY可以获取画布内的鼠标坐标。以下是一些片段来证明我的观点:
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
的节点。
对于块级元素,offsetTop
,offsetLeft
,offsetWidth
,和offsetHeight
描述元件相对于所述一个的边界框offsetParent
。
然而,对于行内的元素(如span
),其可以包从一行到下一,offsetTop
并且offsetLeft
描述第一边界框中的位置(使用Element.getClientRects()
以获得其宽度和高度),而offsetWidth
和offsetHeight
描述的边界的边界框的尺寸(用于Element.getBoundingClientRect()
获取其位置)。因此,与左,顶,宽度和高度的箱offsetLeft
,offsetTop
,offsetWidth
和offsetHeight
将不与换行文本的跨度的边界框。
HTMLElement.offsetTop
的HTMLElement.offsetTop
只读属性返回电流元件相对于所述的顶部的距离offsetParent
的节点。
MouseEvent.pageX
的pageX
只读属性返回事件相对于整个文档的像素的坐标的X(水平)。此属性考虑页面的任何水平滚动。
MouseEvent.pageY
的MouseEvent.pageY
只读属性返回Y(垂直)在事件相对于整个文档的像素的坐标。此属性考虑页面的任何垂直滚动。
有关更多说明,请参见Mozilla开发人员网络:
https://developer.mozilla.org/zh-CN/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/offset 左https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop
page
并offset
就对我很有帮助。感谢您考虑此要求!
我实现了另一个我认为非常简单的解决方案,因此我想与大家分享。
因此,对我来说,问题是鼠标光标的div跳到0,0。因此,我需要捕获div上的鼠标位置以调整divs的新位置。
我阅读了divs PageX和PageY并根据其设置了顶部和左侧,然后获取值以调整坐标以将光标保持在div中的初始位置,我使用了onDragStart侦听器并存储了e.nativeEvent .layerX和e.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,
});
}
我希望这对遇到过同样问题的人有所帮助:)
您必须知道页面的结构,因为如果您的画布是div的子级,而子级又是另一个div的子级,那么故事就变得更加复杂。这是我在div的2个级别内的画布的代码:
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);
});
原始答案说是将其放入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>
您可以使用getBoudingClientRect()
的的relative
父。
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)
})