IT story

요소를 기준으로 마우스 위치 찾기

hot-time 2020. 5. 11. 08:07
반응형

요소를 기준으로 마우스 위치 찾기


캔버스를 사용하여 작은 그림 앱을 만들고 싶습니다. 캔버스에서 마우스의 위치를 ​​찾아야합니다.


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각 줄 을 찾을 필요가 없습니다 .


복사 / 붙여 넣을 수있는 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.
}

전체 예제의 JSFiddle


다음은 캔버스 요소에 대한 마우스 위치 관계를 계산합니다.

var example = document.getElementById('example'); 
example.onmousemove = function(e) { 
    var x = e.pageX - this.offsetLeft; 
    var y = e.pageY - this.offsetTop; 
}

이 예에서 this받는 지칭 example요소와 e는 IS onmousemove이벤트.


순수 자바 스크립트에는 참조 요소가 다른 위치에 중첩되어 절대 위치를 지정할 수있는 경우 상대 좌표를 반환하는 대답이 없습니다. 이 시나리오에 대한 해결책은 다음과 같습니다.

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 !== null || reference !== undefined){
    offset.left += reference.offsetLeft;
    offset.top += reference.offsetTop;
    reference = reference.offsetParent;
  }

  return { 
    x: position.x - offset.left,
    y: position.y - offset.top,
  }; 

}

이 문제의 난이도에 대한 자세한 내용은 http://www.quirksmode.org/js/events_properties.html#position에서 확인할 수 있습니다 .

여기에 설명 된 기술을 사용하여 문서에서 마우스 위치를 찾을 수 있습니다. 그런 다음 요소의 경계 상자 안에 있는지 확인하십시오. element.getBoundingClientRect ()를 호출하여 찾을 수 있습니다. {bottom, height, left, right, top, width }. 거기에서 요소 내부 에서조차 발생했는지 여부를 알아내는 것은 사소한 일입니다.


나는이 모든 솔루션을 시도했으며 매트릭스 변환 컨테이너 (panzoom 라이브러리)를 사용한 특수 설정으로 인해 아무것도 작동하지 않았습니다. 확대 / 축소 및 이동 한 경우에도 올바른 값을 반환합니다.

mouseevent(e) {
 const x = e.offsetX
 const y = e.offsetY
}

그러나 아이가없는 경우에만 가능합니다. CSS를 사용하여 해당 어린이를 이벤트에 '보이지 않게'함으로써 우회 할 수 있습니다.

#container.element-dragging *:not(.dragging) {
   pointer-events: none;
}

dragstart / dragstop 함수 .element-dragging에서 컨테이너 및 .dragging요소 자체 에서 클래스 토글하십시오 .


나는 올바른 방향으로 나를 얻었을 때 Mark van Wyk의 대답을 +1했지만 나를 위해 그것을 해결하지 못했습니다. 나는 여전히 다른 요소에 포함 된 요소의 그림에 오프셋을 가졌습니다.

다음은 나를 위해 해결했습니다.

        x = e.pageX - this.offsetLeft - $(elem).offset().left;
        y = e.pageY - this.offsetTop - $(elem).offset().top;

다시 말해서, 나는 단순히 중첩 된 모든 요소의 모든 오프셋을 쌓았습니다.


나는이 질문을 보았지만 내 경우 (DOM 요소 (내 경우에는 캔버스가 아님) 에서 드래그 를 사용 offsetX하여) offsetY에서 작동하도록하기 위해 드래그 마우스 이벤트 에서만 사용해야한다는 것을 알았습니다. .

onDragOver(event){
 var x = event.offsetX;
 var y = event.offsetY;
}

위의 답변 중 어느 것도 만족스러운 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 가지 규칙이 있습니다.

  1. mousemove또는 touchmove행사 중에 가능한 한 적게하십시오 .
  2. mousedown또는 touchstart행사 중에 가능한 한 많이하십시오 .
  3. 전파를 취소하고 터치 이벤트의 기본값을 방지하여 하이브리드 장치에서 마우스 이벤트가 발생하지 않도록합니다.

말할 것도없이, 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;
}

나는 조금 늦었다는 것을 알고 있지만 이것은 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().lefthtml 태그 대해 반환 된 숫자는 음수가됩니다. 그런 다음이 숫자를 사용하여 요소와 컨텐츠 영역의 왼쪽 사이의 거리를 계산합니다. element_offset_x = element.getBoundingClientRect().left......;

컨텐츠 영역에서 요소의 거리를 알고 있습니다. event.clientX뷰포트에서 포인터의 거리를 알려줍니다. 뷰포트와 컨텐츠 영역이 서로 다른 두 엔티티라는 것을 이해하는 것이 중요합니다. 페이지가 스크롤되면 뷰포트가 이동할 수 있습니다. 따라서 clientX는 페이지가 스크롤 되더라도 동일한 번호를 반환합니다.

이를 보완하기 위해 포인터의 x 위치 (뷰포트 기준)를 뷰포트의 x 위치 (컨텐츠 영역 기준)에 추가해야합니다. 뷰포트의 X 위치는 window.pageXOffset.


이 튜토리얼 에서 가져온 주석은 다음과 같이 수정되었습니다.

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 );
} );

@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의 노드.

For block-level elements, offsetTop, offsetLeft, offsetWidth, and offsetHeight describe the border box of an element relative to the offsetParent.

However, for inline-level elements (such as span) that can wrap from one line to the next, offsetTop and offsetLeft describe the positions of the first border box (use Element.getClientRects() to get its width and height), while offsetWidth and offsetHeight describe the dimensions of the bounding border box (use Element.getBoundingClientRect() to get its position). Therefore, a box with the left, top, width and height of offsetLeft, offsetTop, offsetWidth and offsetHeight will not be a bounding box for a span with wrapped text.

HTMLElement.offsetTop

The HTMLElement.offsetTop read-only property returns the distance of the current element relative to the top of the offsetParent node.

MouseEvent.pageX

The pageX read-only property returns the X (horizontal) coordinate in pixels of the event relative to the whole document. This property takes into account any horizontal scrolling of the page.

MouseEvent.pageY

The MouseEvent.pageY read-only property returns the Y (vertical) coordinate in pixels of the event relative to the whole document. This property takes into account any vertical scrolling of the page.

For further explanation, please see the 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


You have to know the structure of your page, because if your canvas is a child of a div which in turn is a child of another div... then the story gets more complicated. Here's my code for a canvas which is inside 2 levels of 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);

});


Original answer said to put it in an iframe. The better solution is to use the events offsetX and offsetY on a canvas that has the padding set to 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>

Here is what I got.

    $(".some-class").click(function(e) {

    var posx = 0;
    var posy = 0;

    posx = e.pageX;
    posy = e.pageY;

    alert(posx);
    alert(posy);
});

I implemented an other solution that I think is very simple so I thought I'd share with you guys.

So, the problem for me was that the dragged div would jump to 0,0 for the mouse cursor. So I needed to capture the mouses position on the div to adjust the divs new position.

I read the divs PageX and PageY and set the top and left of the according to that and then to get the values to adjust the coordinates to keep the cursor in the initial position in the div I use a onDragStart listener and store the e.nativeEvent.layerX and e.nativeEvent.layerY that only in the initial trigger gives you the mouses position within the draggable div.

Example code :

 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,
            });
          }

I hope this will help someone that went through the same problems I went through :)

참고URL : https://stackoverflow.com/questions/3234256/find-mouse-position-relative-to-element

반응형