javascript - 检测 html 5 Canvas 上的点击与拖动

标签 javascript html canvas mouseevent

我正在尝试使用 HTML 5 Canvas 编写一个简单的 map 。拖动鼠标移动 map ,然后单击选择最近的航路点。然而,有一个问题。当我拖动鼠标时,释放鼠标时我仍然会收到click事件。仅当单击时没有移动时我才想得到它。

我尝试在 mousemove 事件处理程序中检查无移动事件,但没有成功:

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

我的问题是:有没有一种简单的方法可以做到这一点,或者我应该检查新闻和发布事件是否在同一个地方?

这是我的代码:

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

最佳答案

这是解决此问题的一种方法:

由于我们需要知道两种状态,因此我们需要两个标志(有些人更喜欢对象 - 这取决于您):

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

(我们对 9 进行平方,这样我们就不必为以后的每个鼠标移动事件计算平方根)。

然后我们可以定义回调处理程序来考虑这些事情:

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

下一个处理程序可以在 window 对象上设置,而不是在 Canvas 元素本身上。这允许我们在按住鼠标按钮时移动到 Canvas 元素之外。我们需要在这里使用 addEventListener() ,以便我们也允许其他代码订阅:

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

最后我们检测点击以及更新鼠标状态,这也是在 window 对象上:

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

最后的问题是点击路径点。进行绝对检查(即 x === 值)很少会成功,因为我们需要将鼠标按钮精确地放置在该点上。允许使用路径点的宽度和高度的范围(假设路径点为对象 wp):

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>

关于javascript - 检测 html 5 Canvas 上的点击与拖动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37239710/

相关文章:

javascript - 如何使 HTML5 Canvas 上的图像垂直和水平重复?

java - 在 Paint Listener 中消除 SWT Canvas 的闪烁

javascript - 是否有 xhr.overrideMimeType() 函数的跨浏览器替代方案?

javascript - 调试 "unsafe javascript attempt to access frame with URL ... "

javascript - 将下拉菜单定位在其父级下

html - 控制和对齐 CSS 中的段落

javascript - 在 HTML5 Canvas 中沿着等式绘制直线

javascript - snapshot.val() 返回未定义,但它正确记录。 Node.js 火力基地

javascript - 单击时查找当前 td 单元格的位置

html - 车身宽度不变