javascript - Detecting clicks versus drags on an html 5 canvas - Stack Overflow

admin2025-04-19  0

I am trying to write a simple map with the HTML 5 canvas. Dragging the mouse moves the map, and clicking selects the closest waypoint. However, there is a problem. When I drag the mouse, I still get the click event when I release it. I would like to get that only if there was no movement while clicking.

I tried to check for a no movement event in the mousemove event handler, but with no success:

function onMove(e) {
    if(e.movementX == 0 && e.movementY == 0) {
        console.log("a"); //never happens...
    }
}

My question is: is there an easy way to do it, or should I check if the press and release event were at the same place?

Here is my code:

function onMove(e) {
    console.log("moving");
}

function onClick(e) {
    console.log("clicked");
}

function init() {
    c = document.getElementById("MapCanvas");
    ctx = c.getContext("2d");
    c.addEventListener("click", onClick, true);
    c.addEventListener("mousemove", onMove, true);
}

I am trying to write a simple map with the HTML 5 canvas. Dragging the mouse moves the map, and clicking selects the closest waypoint. However, there is a problem. When I drag the mouse, I still get the click event when I release it. I would like to get that only if there was no movement while clicking.

I tried to check for a no movement event in the mousemove event handler, but with no success:

function onMove(e) {
    if(e.movementX == 0 && e.movementY == 0) {
        console.log("a"); //never happens...
    }
}

My question is: is there an easy way to do it, or should I check if the press and release event were at the same place?

Here is my code:

function onMove(e) {
    console.log("moving");
}

function onClick(e) {
    console.log("clicked");
}

function init() {
    c = document.getElementById("MapCanvas");
    ctx = c.getContext("2d");
    c.addEventListener("click", onClick, true);
    c.addEventListener("mousemove", onMove, true);
}
Share Improve this question edited May 15, 2016 at 18:34 markE 105k11 gold badges170 silver badges183 bronze badges asked May 15, 2016 at 14:48 marci szmarci sz 1081 silver badge8 bronze badges 2
  • post your event handlers and sample html. Read minimal reproducible example – yezzz Commented May 15, 2016 at 15:06
  • Try the mousedown event for selection instead of click. – ShuberFu Commented May 15, 2016 at 15:21
Add a ment  | 

1 Answer 1

Reset to default 10

Here is one way to solve this:

As we need to know two states we need two flags (some prefer objects - it's up to you):

var isDown = false;    // mouse button is held down
var isMoving = false;  // we're moving (dragging)

Then we need to find a way to distinguish between a drag and a click. A typical approach is to use a radius (length) between first click point to the current point: if outside we consider it to be a drag operation.

So -

var radius = 9 * 9     // radius in pixels, 9 squared
var firstPos;          // keep track of first position

(We're squaring the 9 so we don't have to calculate square-root for every mouse move event later).

Then we can define our callback handlers to consider these things:

canvas.onmousedown = function(e) {
  firstPos = getXY(e); // record click position (see getXY function in demo)
  isDown = true;       // record mouse state
  isMoving = false;    // reset move state
};

The next handlers can be set on window object instead of the canvas element itself. This allows us to move outside the canvas element while the mouse button is held. We need to use addEventListener() here so we allow other code to subscribe as well:

window.addEventListener("mousemove", function(e) {
  if (!isDown) return; // we will only act if mouse button is down
  var pos = getXY(e);  // get current mouse position

  // calculate distance from click point to current point
  var dx = firstPos.x - pos.x,
      dy = firstPos.y - pos.y,
      dist = dx * dx + dy * dy;        // skip square-root (see above)

  if (dist >= radius) isMoving = true; // 10-4 we're on the move

  if (isMoving) {
    // handle move operation here
  }
});

And finally we detect for click as well as updating mouse state, also this on the window object:

window.addEventListener("mouseup", function(e) {
  if (!isDown) return;   // no need for us in this case
  isDown = false;        // record mouse state

  if (!isMoving) {
    // it was a click, handle click operation here
  }
});

Then final problem is then to click the way-point. Doing an absolute check (i.e. x === value) will rarely turn out OK as we need to place the mouse button exactly at that point. Allow a range using the width and height of the way point (assuming an object wp for the way-point):

if (pos.x >= wp.x && pos.x < wp.x + wp.width && 
    pos.y >= wp.y && pos.y < wp.y + wp.height) { ... }

Example

var ctx = canvas.getContext("2d");
var wp = {x: 50, y:50, width:12, height:12};  // demo way-point
ctx.font = "20px sans-serif";
ctx.fillText("Click or click+move on this canvas...", 10, 30);
ctx.strokeRect(wp.x, wp.y, wp.width, wp.height);

var isDown = false;        // mouse button is held down
var isMoving = false;      // we're moving (dragging)
var radius = 9 * 9         // radius in pixels, 9 squared
var firstPos;              // keep track of first position

canvas.onmousedown = function(e) {
  ctx.clearRect(0,0,canvas.width,canvas.height);
  ctx.strokeRect(wp.x, wp.y, wp.width, wp.height);
  
  firstPos = getXY(e);
  isDown = true;           // record mouse state
  isMoving = false;        // reset move state
};

window.addEventListener("mousemove", function(e) {
  if (!isDown) return;     // we will only act if mouse button is down
  var pos = getXY(e);      // get current mouse position

  // calculate distance from click point to current point
  var dx = firstPos.x - pos.x,
      dy = firstPos.y - pos.y,
      dist = dx * dx + dy * dy;        // skip square-root (see above)

  if (dist >= radius) isMoving = true; // 10-4 we're on the move

  if (isMoving) {
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.strokeRect(wp.x, wp.y, wp.width, wp.height);
    ctx.fillText("MOVING", 10, 30);
  }
});

window.addEventListener("mouseup", function(e) {
  if (!isDown) return;     // no need for us in this case
  isDown = false;          // record mouse state

  if (!isMoving) {
    if (firstPos.x >= wp.x && firstPos.x < wp.x + wp.width &&
        firstPos.y >= wp.y && firstPos.y < wp.y + wp.height) {
      ctx.fillText("CLICKED WAYPOINT", 10, 30);
    }
    else {
      ctx.fillText("CLICK", 10, 30);
    }
  }
});

function getXY(e) {
  var rect = canvas.getBoundingClientRect();
  return {x: e.clientX - rect.left, y: e.clientY - rect.top}
}
canvas {background:#ccc}
<canvas id=canvas width=620 height=180></canvas>

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745022057a280439.html

最新回复(0)