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