ฉันต้องการสร้างแอพการวาดภาพเล็ก ๆ น้อย ๆ โดยใช้ Canvas ดังนั้นฉันต้องหาตำแหน่งของเมาส์บนผืนผ้าใบ
ฉันต้องการสร้างแอพการวาดภาพเล็ก ๆ น้อย ๆ โดยใช้ Canvas ดังนั้นฉันต้องหาตำแหน่งของเมาส์บนผืนผ้าใบ
คำตอบ:
สำหรับคนที่ใช้ 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แต่ละบรรทัด
มีรุ่นใหม่รุ่น JavaScript เท่านั้นด้านล่างโดย @anytimecoder ดังต่อไปนี้เป็นเบราว์เซอร์ที่สนับสนุน getBoundingClientRect ()
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 มิฉะนั้นองค์ประกอบของเด็กจะส่งผลกระทบต่อมัน
ข้อมูลต่อไปนี้คำนวณตำแหน่งของเมาส์ที่สัมพันธ์กับองค์ประกอบ Canvas:
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ซึ่งจะทำให้คุณได้ตำแหน่งที่สัมพันธ์กับองค์ประกอบที่คุณเพิ่มผู้ฟังเหตุการณ์
ไม่มีคำตอบในจาวาสคริปต์บริสุทธิ์ที่คืนค่าพิกัดสัมพัทธ์เมื่อองค์ประกอบอ้างอิงซ้อนอยู่ในส่วนอื่นซึ่งสามารถอยู่กับตำแหน่งที่แน่นอน นี่คือวิธีแก้ไขสถานการณ์นี้:
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เป็น truthy จะให้แน่ใจว่ามันทำงานร่วมกับทั้งสองและundefined null
offsetParentทรัพย์สินซึ่งกลายเป็นประโยชน์อย่างมากในสถานการณ์อื่น ๆ !
scrollLeftและscrollTopชดเชยลูกหลานที่เลื่อน
สามารถเขียนบทความที่ดีเกี่ยวกับความยากของปัญหานี้ได้ที่: http://www.quirksmode.org/js/events_properties.html#position
ใช้เทคนิคที่อธิบายไว้ที่นั่นคุณสามารถค้นหาตำแหน่ง mouses ในเอกสาร แล้วคุณก็จะตรวจสอบเพื่อดูว่ามันอยู่ภายในขอบเขตกล่องขององค์ประกอบของคุณซึ่งคุณสามารถหาได้โดยการโทรที่จะกลับวัตถุที่มีคุณสมบัติดังต่อไปนี้:element.getBoundingClientRect() { bottom, height, left, right, top, width }จากนั้นมันเป็นเรื่องไม่สำคัญที่จะคิดออกว่าสิ่งที่เกิดขึ้นภายในองค์ประกอบของคุณหรือไม่
ฉันลองวิธีแก้ปัญหาทั้งหมดและเนื่องจากการตั้งค่าพิเศษของฉันด้วยเมทริกซ์ที่แปลงคอนเทนเนอร์ (ไลบรารี panzoom) ไม่มีการทำงาน สิ่งนี้จะคืนค่าที่ถูกต้องแม้ว่าจะซูมและเลื่อน:
mouseevent(e) {
const x = e.offsetX,
y = e.offsetY
}
แต่เฉพาะในกรณีที่ไม่มีองค์ประกอบของเด็กในทาง สิ่งนี้สามารถหลีกเลี่ยงได้โดยทำให้พวกเขา 'มองไม่เห็น' กับเหตุการณ์โดยใช้ CSS:
.child {
pointer-events: none;
}
ฉันเจอคำถามนี้ แต่เพื่อให้มันทำงานได้กับกรณีของฉัน (โดยใช้ dragover บนองค์ประกอบ DOM (ไม่ใช่ผ้าใบในกรณีของฉัน)) ฉันพบว่าคุณต้องใช้offsetXและoffsetYในเหตุการณ์ dragover-mouse เท่านั้น .
onDragOver(event){
var x = event.offsetX;
var y = event.offsetY;
}
ฉัน +1 'คำตอบของมาร์ค van Wyk เพราะมันทำให้ฉันไปในทิศทางที่ถูกต้อง แต่ก็ไม่ได้แก้ปัญหาสำหรับฉัน ฉันยังคงมีการชดเชยในการวาดภาพในองค์ประกอบที่มีอยู่ในองค์ประกอบอื่น
การไหลแก้มันสำหรับฉัน:
x = e.pageX - this.offsetLeft - $(elem).offset().left;
y = e.pageY - this.offsetTop - $(elem).offset().top;
ในคำอื่น ๆ - ฉันเพียงแค่ซ้อน offsets ทั้งหมดจากองค์ประกอบทั้งหมดซ้อนกัน
คำตอบข้างต้นไม่เป็นที่น่าพอใจ 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
สำหรับผู้ที่คุณพัฒนาเว็บไซต์ปกติหรือ PWAs (Progressive Web Apps) สำหรับอุปกรณ์มือถือและ / หรือแล็ปท็อป / จอภาพที่มีหน้าจอสัมผัสคุณจะต้องลงจอดที่นี่เพราะคุณอาจเคยชินกับเหตุการณ์เมาส์และเป็นประสบการณ์ใหม่ของ 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 และมันยังให้พิกัดของตัวชี้ภายในองค์ประกอบถ้าองค์ประกอบนั้นใหญ่กว่าวิวพอร์ตและผู้ใช้เลื่อน
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และอธิบายกล่องชายแดนขององค์ประกอบที่สัมพันธ์กับoffsetHeightoffsetParent
อย่างไรก็ตามสำหรับองค์ประกอบระดับอินไลน์ (เช่น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 Developer Network:
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
pageและoffsetจะเป็นประโยชน์กับฉันมาก ขอบคุณสำหรับการพิจารณาคำขอนี้!
ฉันใช้โซลูชันอื่นที่ฉันคิดว่าง่ายมากดังนั้นฉันคิดว่าฉันจะแบ่งปันกับพวกคุณ
ดังนั้นปัญหาสำหรับฉันก็คือ div div ที่ลากนั้นจะกระโดดไปที่0,0สำหรับเคอร์เซอร์ของเมาส์ ดังนั้นฉันจึงต้องจับตำแหน่ง mouses บน div เพื่อปรับตำแหน่ง divs ใหม่
ฉันอ่าน div ของ PageX และ PageY แล้วตั้งค่าด้านบนและด้านซ้ายตามนั้นและเพื่อให้ได้ค่าสำหรับปรับพิกัดเพื่อให้เคอร์เซอร์อยู่ในตำแหน่งเริ่มต้นใน div ฉันใช้ฟัง onDragStart และเก็บe.nativeEvent .layerXและe.nativeEvent.layerYที่เฉพาะในทริกเกอร์เริ่มต้นเท่านั้นที่จะช่วยให้คุณได้รับตำแหน่ง mouses ภายใน 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 ;
}
ใช้งานได้ดี!
คุณต้องรู้โครงสร้างของหน้าเว็บของคุณเพราะถ้าผืนผ้าใบของคุณเป็นลูกของ div ซึ่งจะเป็นลูกของอีกฝ่าย ... แล้วเรื่องราวก็ซับซ้อนมากขึ้น นี่คือรหัสของฉันสำหรับผืนผ้าใบซึ่งอยู่ภายใน 2 ระดับของ div:
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 ทางออกที่ดีกว่าคือการใช้ events offsetX และ offsetY บนผืนผ้าใบที่มีการเว้นระยะห่างไว้ที่ 0px
<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>
นี่คือสิ่งที่ฉันได้รับ
$(".some-class").click(function(e) {
var posx = 0;
var posy = 0;
posx = e.pageX;
posy = e.pageY;
alert(posx);
alert(posy);
});
คุณสามารถใช้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)
})