Я хочу зробити невеликий додаток для малювання за допомогою полотна. Тому мені потрібно знайти положення миші на полотні.
Я хочу зробити невеликий додаток для малювання за допомогою полотна. Тому мені потрібно знайти положення миші на полотні.
Відповіді:
Для людей, які використовують 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, а не цільовий ціль. Інакше на це вплинуть дитячі елементи
Далі обчислює відношення положення миші до елемента полотна:
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є truthy, забезпечить, що вона працює з обома 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;
}
Я наштовхнувся на це питання, але для того, щоб він працював у моєму випадку (використовуючи дравер на DOM-елементі (в моєму випадку не є полотном)), я виявив, що вам потрібно використовуватись offsetXі offsetYна події драговер-миша .
onDragOver(event){
var x = event.offsetX;
var y = event.offsetY;
}
Я поставив +1 "Відповідь Марка ван Віка, оскільки він мене направив у правильному напрямку, але не зовсім вирішив це для мене". У мене все ще було зрушення в малюванні елементів, що містяться в іншому елементі.
FOllowing вирішив це для мене:
x = e.pageX - this.offsetLeft - $(elem).offset().left;
y = e.pageY - this.offsetTop - $(elem).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
Для тих, хто розробляє звичайні веб-сайти або PWA (прогресивні веб-програми) для мобільних пристроїв та / або ноутбуків / моніторів з сенсорними екранами, ви приземлилися сюди, тому що вас можуть використовувати для проведення подій миші та є новим для часом болісного досвіду дотику події ... так!
Існує лише 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 у вікні перегляду знайдено з 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Властивість тільки для читання повертає Х (горизонтальна) координата в пікселях подій щодо всього документа. Ця властивість враховує будь-яке горизонтальне прокручування сторінки.
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
pageі offsetмені було б дуже корисно. Дякуємо, що розглянули цей запит!
Я реалізував інше рішення, яке, на мою думку, дуже просте, тому я подумав, що поділюся з вами, хлопці.
Отже, проблема для мене полягала в тому, що перетягнутий div підскочить до 0,0 для курсору миші. Тому мені потрібно було зафіксувати положення миші на діві, щоб скорегувати діву нове положення.
Я читаю сторінку divs PageX та PageY і встановлюю верхню і ліву частини відповідно до цього, а потім отримую значення для коригування координат, щоб утримувати курсор у початковому положенні в div, використовую слухач onDragStart і зберігаю e.nativeEvent .layerX та e.nativeEvent.layerY, що лише в початковому тригері дає вам положення миші в розділювальному елементі, що перетягується.
Приклад коду:
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,
});
}
Я сподіваюся, що це допоможе комусь, хто пройшов через ті самі проблеми, які я переживав :)
Ви повинні знати структуру вашої сторінки, адже якщо ваше полотно - це дитина діва, яка, в свою чергу, є дитиною іншого діва ..., то історія ускладнюється. Ось мій код полотна, який знаходиться у двох рівнях діви:
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);
});
Оригінальна відповідь сказала, що вкладається в рамку. Кращим рішенням є використання подій 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>
Ви можете використовувати 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)
})