Quiero hacer una pequeña aplicación de pintura usando lienzo. Entonces necesito encontrar la posición del mouse en el lienzo.
Quiero hacer una pequeña aplicación de pintura usando lienzo. Entonces necesito encontrar la posición del mouse en el lienzo.
Respuestas:
Para las personas que usan JQuery:
A veces, cuando tiene elementos anidados, uno de ellos con el evento adjunto, puede ser confuso comprender lo que su navegador ve como padre. Aquí, puede especificar qué padre.
Toma la posición del mouse y luego la resta de la posición de desplazamiento del elemento principal.
var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;
Si está intentando obtener la posición del mouse en una página dentro de un panel de desplazamiento:
var x = (evt.pageX - $('#element').offset().left) + self.frame.scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + self.frame.scrollTop();
O la posición relativa a la página:
var x = (evt.pageX - $('#element').offset().left) + $(window).scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();
Tenga en cuenta la siguiente optimización del rendimiento:
var offset = $('#element').offset();
// Then refer to
var x = evt.pageX - offset.left;
De esta manera, JQuery no tiene que buscar #elementcada línea.
A continuación, hay una versión más reciente, solo JavaScript, de @anytimecoder a continuación para los navegadores que admiten getBoundingClientRect () .
var y = (evt.pageY - $('#element').offset().left) + $(window).scrollTop();avar y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();
Como no encontré una respuesta sin jQuery que pudiera copiar / pegar, aquí está la solución que utilicé:
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, no e.target. De lo contrario, elementos secundarios que afectarán
A continuación se calcula la relación de posición del mouse con el elemento del lienzo:
var example = document.getElementById('example');
example.onmousemove = function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
}
En este ejemplo, se thisrefiere al exampleelemento y ees el onmousemoveevento.
var example = document.getElementById('example'); example.onmousemove = function(e) { var X = e.pageX - this.offsetLeft; var Y = e.pageY - this.offsetTop; }
thisa e.currentTarget, eso le dará la posición relativa al elemento al que agregó el detector de eventos.
No hay respuesta en javascript puro que devuelva coordenadas relativas cuando el elemento de referencia está anidado dentro de otros que pueden estar con posicionamiento absoluto. Aquí hay una solución para este escenario:
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,
};
}
htmldesplazamiento del elemento
while(reference !== undefined)con while(reference). Al menos en Chrome, el offsetParent no termina siendo undefined, pero null. Solo verificar si referencees verdadero asegurará que funcione con ambos undefinedy null.
offsetParentpropiedad, ¡que resulta ser muy útil en otras situaciones!
scrollLeft y scrollTopcompensar a los descendientes desplazados.
Una buena reseña de la dificultad de este problema se puede encontrar aquí: http://www.quirksmode.org/js/events_properties.html#position
Usando la técnica que se describe allí, puede encontrar la posición de los ratones en el documento. A continuación, sólo comprobar para ver si está dentro del cuadro delimitador del elemento, que se puede encontrar por llamada element.getBoundingClientRect()que devolverá un objeto con las siguientes propiedades: { bottom, height, left, right, top, width }. A partir de ahí, es trivial averiguar si incluso sucedió dentro de su elemento o no.
Probé todas estas soluciones y, debido a mi configuración especial con un contenedor transformado por matriz (biblioteca panzoom), ninguna funcionó. Esto devuelve el valor correcto, incluso si se amplía y se panoramiza:
mouseevent(e) {
const x = e.offsetX,
y = e.offsetY
}
Pero solo si no hay elementos secundarios en el camino. Esto se puede evitar al hacerlos 'invisibles' para el evento, usando CSS:
.child {
pointer-events: none;
}
Me encontré con esta pregunta, pero para que funcione para mi caso (usando dragover en un elemento DOM (no siendo lienzo en mi caso)), descubrí que solo tienes que usar offsetXy offsetYen el evento dragover-mouse .
onDragOver(event){
var x = event.offsetX;
var y = event.offsetY;
}
I +1 'La respuesta de Mark van Wyk, ya que me llevó en la dirección correcta, pero no lo resolvió por mí. Todavía tenía un desplazamiento en la pintura en elementos contenidos dentro de otro elemento.
Siguiendo me lo resolvió:
x = e.pageX - this.offsetLeft - $(elem).offset().left;
y = e.pageY - this.offsetTop - $(elem).offset().top;
En otras palabras, simplemente apilé todos los desplazamientos de todos los elementos anidados
Ninguna de las respuestas anteriores es IMO satisfactoria, así que esto es lo que uso:
// 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 si alguna vez necesita saber la posición actual de desplazamiento Y de la página:
var yscroll = window.pageYOffset
|| (document.documentElement && document.documentElement.scrollTop)
|| document.body.scrollTop; // scroll Y position in page
Para aquellos de ustedes que desarrollan sitios web regulares o PWAs (Progressive Web Apps) para dispositivos móviles y / o computadoras portátiles / monitores con pantallas táctiles, entonces han aterrizado aquí porque pueden estar acostumbrados a los eventos del mouse y son nuevos en la experiencia a veces dolorosa de Touch eventos ... yay!
Solo hay 3 reglas:
mousemoveo touchmoveeventos.mousedowno touchstarteventos.No hace falta decir que las cosas son más complicadas con los toucheventos porque puede haber más de uno y son más flexibles (complicados) que los eventos del mouse. Solo voy a cubrir un solo toque aquí. Sí, estoy siendo flojo, pero es el tipo de toque más común, así que ahí.
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.
¿Prefieres usar Vue.js ? ¡Hago! Entonces su HTML se vería así:
<div @mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@touchstart.stop.prevent="handleTouchStart"
@touchmove.stop.prevent="handleTouchMove"
@touchend.stop.prevent="handleTouchEnd">
puedes conseguirlo
var element = document.getElementById(canvasId);
element.onmousemove = function(e) {
var xCoor = e.clientX;
var yCoor = e.clientY;
}
Tomado de este tutorial , con correcciones hechas gracias al comentario superior:
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 )
};
}
Usar en un lienzo de la siguiente manera:
var canvas = document.getElementById( 'myCanvas' );
canvas.addEventListener( 'mousemove', function( evt ) {
var mousePos = getMousePos( canvas, evt );
} );
Me doy cuenta de que llego un poco tarde, pero esto funciona con JavaScript PURE, e incluso le da las coordenadas del puntero dentro del elemento si el elemento es más grande que la ventana gráfica y el usuario se ha desplazado.
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 ;
}
Cómo funciona.
Lo primero que hacemos es obtener la ubicación del elemento HTML (el área de contenido) en relación con la ventana gráfica actual. Si la página tiene barras de desplazamiento y se desplaza, el número devuelto porgetBoundingClientRect().left la etiqueta html será negativo. Luego usamos este número para calcular la distancia entre el elemento y la izquierda del área de contenido. Conelement_offset_x = element.getBoundingClientRect().left......;
Conocer la distancia del elemento desde el área de contenido. event.clientXnos da la distancia del puntero desde la ventana gráfica. Es importante comprender que la ventana gráfica y el área de contenido son dos entidades diferentes, la ventana gráfica puede moverse si se desplaza la página. Por lo tanto, clientX devolverá el MISMO número incluso si se desplaza la página.
Para compensar esto, necesitamos agregar la posición x del puntero (en relación con la ventana gráfica), a la posición x de la ventana gráfica (en relación con el área de contenido). La posición X de la ventana gráfica se encuentra con window.pageXOffset.
Basado en la solución de @ Spider, mi versión no JQuery es esta:
// 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;
}
Esto funciona tanto con desplazamiento como con zoom (en cuyo caso a veces devuelve flotantes).
Las coordenadas del mouse dentro de un lienzo se pueden obtener gracias a event.offsetX y event.offsetY. Aquí hay un pequeño fragmento para probar mi punto:
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
La HTMLElement.offsetLeftpropiedad de solo lectura devuelve el número de píxeles que la esquina superior izquierda del elemento actual está desplazada a la izquierda dentro delHTMLElement.offsetParent nodo.
Para los elementos a nivel de bloque, offsetTop, offsetLeft, offsetWidth, y offsetHeightdescribir el cuadro de borde de un elemento con respecto a la offsetParent.
Sin embargo, para elementos de nivel en línea (como span) que pueden ajustarse de una línea a la siguiente, offsetTopyoffsetLeft describir las posiciones del primer cuadro de borde (usar Element.getClientRects()para obtener su ancho y alto), mientras que offsetWidthy offsetHeightdescribir las dimensiones del cuadro de borde delimitador (use Element.getBoundingClientRect()para obtener su posición). Por lo tanto, una caja con la izquierda, superior, anchura y altura de offsetLeft, offsetTop, offsetWidthy offsetHeightno será un cuadro delimitador por una duración con el texto envuelto.
HTMLElement.offsetTop
La HTMLElement.offsetToppropiedad de solo lectura devuelve la distancia del elemento actual en relación con la parte superior del offsetParentnodo.
MouseEvent.pageX
La pageXpropiedad de solo lectura devuelve la coordenada X (horizontal) en píxeles del evento en relación con todo el documento. Esta propiedad tiene en cuenta cualquier desplazamiento horizontal de la página.
MouseEvent.pageY
La MouseEvent.pageYpropiedad de solo lectura devuelve la coordenada Y (vertical) en píxeles del evento en relación con todo el documento. Esta propiedad tiene en cuenta cualquier desplazamiento vertical de la página.
Para obtener más información, consulte la red de desarrolladores de 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
pagey offsetsería muy útil para mí. ¡Gracias por considerar esta solicitud!
Implementé otra solución que creo que es muy simple, así que pensé en compartirla con ustedes.
Entonces, el problema para mí fue que el div arrastrado saltaría a 0,0 para el cursor del mouse. Así que necesitaba capturar la posición de los ratones en el div para ajustar la nueva posición del divs.
Leo los divs PageX y PageY y establezco la parte superior e izquierda de acuerdo con eso y luego para obtener los valores para ajustar las coordenadas para mantener el cursor en la posición inicial en el div, uso un escucha onDragStart y almaceno el e.nativeEvent .layerX y e.nativeEvent.layerY que solo en el disparador inicial le da la posición de los ratones dentro del div arrastrable.
Código de ejemplo:
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,
});
}
Espero que esto ayude a alguien que pasó por los mismos problemas que yo pasé :)
Tienes que conocer la estructura de tu página, porque si tu lienzo es hijo de un div que a su vez es hijo de otro div ... entonces la historia se vuelve más complicada. Aquí está mi código para un lienzo que está dentro de 2 niveles de 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);
});
La respuesta original decía ponerlo en un iframe. La mejor solución es usar los eventos offsetX y offsetY en un lienzo que tenga el relleno establecido en 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>
Puedes usar getBoudingClientRect()el relativepadre.
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)
})