How to know whether the mousedown event is occured on where we want in canvas?

285 views Asked by At

Here I have a code sample related to moving an element on canvas. To move a circle to an other position on canvas, the below code is checking whether the mousedown event got triggered on the circle element itself or not so that further dragging can be initiated using mousemove. But I dont understand the logic that's been used to know whether or not the mouse is doubleclicked on the correct circle to drag it.

// start dragging
function DragStart(e) {

    //coordinates of mouse from mousedown event e{x,y}
    e = MousePos(e);
    var dx, dy;

    //initialCirclePosition is the centre (x,y) of the circle
    dx = initialCiclePosition.x - e.x;
    dy = initialCiclePosition.y - e.y;

    if ((dx * dx) + (dy * dy) < circleRadius * circleRadius)   { 

        //Yes, user is trying to move the circle only
        ........
    }
}

When user holds the mouse control on any element(by clicking on it), mousedown event occurs, Then, when he tries to drag the element, mousemove event occurs. But, before letting mousemove gets triggered, we should find whether or not the user is trying to drag the right element (circle here). If you see the code above, the logic in if() statement was used to check that. I am unable to understand that logic and that's the question is about. Thanks.

1

There are 1 answers

0
markE On BEST ANSWER

An explanation of what your code is testing.

This part of your code...

((dx * dx) + (dy * dy) < circleRadius * circleRadius)

...uses the Pythagorean Theorem to mathematically test if the mouse is inside the circumference of a circle.

(dx * dx) + (dy * dy) measures the distance between the circle centerpoint and the mouse. It actually measures the centerpoint-to-mouse distance squared, but since Math.sqrt is an expensive operation, we just compare the mouse distance squared to the circle radius squared. We get the same result but avoid the expensive Math.sqrt.

Here's a tutorial on determining distances using the Pythagorean Theorem:

https://www.youtube.com/watch?v=We3LG8pK-LU


We can't tell why the test is being done without seeing more of your code.

But, but presumably if the mouse is inside the circle you are deciding that the user intends to drag the circle.

And conversely if the mouse is outside the circle you are deciding that the user intends to execute a click.


An alternative click vs drag test:

This alternative test is nice because you can let the use either click in the circle or drag the circle. This alternative tries to "read the intent" of the user.

  1. In mousedown, save the starting mouseX & mouseY position.

    var isDown,startX,startY,itIsADrag;
    
    function handleMouseDown(e){                
    
      // save the mouse position at mousedown
      startX=parseInt(e.clientX-offsetX);
      startY=parseInt(e.clientY-offsetY);
    
      // initially set the "itIsADrag" flag to false
      itIsADrag=false;
    
      // set the "isDown" flag to true
      isDown=true;
    
      ... any other code ...
    }
    
  2. In mousemove, test if the mouse has moved less than about 5 total pixels (or 10px or whatever).

    • If moved <5 pixels do nothing in anticipation of this being a click.
    • If moved >=5 pixels start a drag operation.

      function handleMouseMove(e){
      
          // return if the mouse is not down
          if(!isDown){return;}
      
          // get the current mouse position
          mouseX=parseInt(e.clientX-offsetX);
          mouseY=parseInt(e.clientY-offsetY);
      
          // do nothing if the mouse has moved less than 5 total pixels since mousedown
          if(!itIsADrag && Math.abs(mouseX-startX)+Math.abs(mouseY-startY)<5){return;}
      
          // Set the dragging flag to true
          // This flag prevents the Math.abs test above if we know we're dragging
          itIsADrag=true;
      
          // start a drag operation
          ... do drag stuff ...
      }
      
  3. By the time mouseup occurs, we can just read our itIsADrag flag to determine if the user clicked or dragged.

    function handleMouseUp(e){
    
      if(itIsADrag){
          console.log("You have been dragging");
      }else{
          console.log("You've did a click");
      }
    
      // clean up by clearing the isDown flag
      isDown=false;
    }
    

Example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }

var isDown=false;
var isDown,startX,startY,itIsADrag;

$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});

function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // save the mouse position at mousedown
  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);

  // initially set the "itIsADrag" flag to false
  itIsADrag=false;

  // set the "isDown" flag to true
  isDown=true;
}

function handleMouseUp(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // report if this was a click or a drag
  if(itIsADrag){
    alert("You have been dragging");
  }else{
    alert("You've did a click");
  }

  // clean up by clearing the isDown flag
  isDown=false;
}

function handleMouseOut(e){
  // clean up by clearing the isDown flag
  isDown=false;
}

function handleMouseMove(e){
  // return if the mouse is not down
  if(!isDown){return;}

  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // get the current mouse position
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // do nothing if the mouse has moved less than 5 total pixels since mousedown
  if(!itIsADrag && Math.abs(mouseX-startX)+Math.abs(mouseY-startY)<5){return;}

  // Set the dragging flag to true
  // This flag prevents the Math.abs test above if we know we're dragging
  itIsADrag=true;
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click or drag in the canvas.</h4>
<canvas id="canvas" width=300 height=300></canvas>