Я хочу сделать небольшое приложение для рисования, используя холст. Поэтому мне нужно найти положение мыши на холсте.
Я хочу сделать небольшое приложение для рисования, используя холст. Поэтому мне нужно найти положение мыши на холсте.
Ответы:
Для людей, использующих 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, чтобы получить положение относительно элемента, к которому вы добавили прослушиватель событий.
В чистом 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;
}
Я наткнулся на этот вопрос, но для того , чтобы заставить его работать на моем случае ( с использованием DragOver на DOM-элемент (не будучи холст в моем случае)), я обнаружил , что у вас есть только использовать offsetXи offsetYна события DragOver-мыши ,
onDragOver(event){
var x = event.offsetX;
var y = event.offsetY;
}
Я +1 'Марк Марк Вик ответил, так как он направил меня в правильном направлении, но не совсем решил для меня. У меня все еще было смещение при рисовании элементов, содержащихся в другом элементе.
Следующие решили это для меня:
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 (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 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только для чтения возвращает координату 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
pageи offsetбыло бы очень полезно для меня. Спасибо за рассмотрение этого запроса!
Я реализовал другое решение, которое мне кажется очень простым, поэтому я решил поделиться с вами, ребята.
Таким образом, проблема для меня заключалась в том, что перетаскиваемый div переместился бы к 0,0 для курсора мыши. Поэтому мне нужно было зафиксировать положение мышей в div, чтобы настроить div в новой позиции.
Я читаю 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 s:
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. Лучшее решение состоит в том, чтобы использовать события 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)
})