我想实现一个包含某些元素的可拖动地图。

->请参阅JSFiddle:https://jsfiddle.net/7ndx7s25/7/

通过使用mousedownmousemovemouseup,我实现了拖动。
但是我遇到了问题:


当按下鼠标按钮然后移到窗口外时,我没有得到mouseup事件。重新进入窗口(很早以前就释放了鼠标按钮),我的地图仍然认为按钮已按下,因此行为异常。
当地图上有对象时,在这些对象之间移动时不会出现mousemove事件。因此,当我进入和离开此类物体时,地图会悬挂并跳跃。
在这些对象上时,我仍然希望有一个move鼠标光标。我可以更改每个对象的cursor样式(在小提琴中,我对对象1进行了此更改),但这似乎不是一个好方法。有没有更优雅的解决方案?

最佳答案

您需要离开mouseout时可以捕获canvas,但是当光标移到其他元素上时也会触发该事件。

一个简单的解决方法是将一个简单的类添加到canvas,在其上设置pointer-events: none

使用该类,您还可以控制光标,并避免使用脚本进行设置。

堆栈片段



updateInfo = function() {
  document.getElementById('info').innerHTML =
    'Position = ' + JSON.stringify(position) +
    '<br />dragInfo = ' + JSON.stringify(dragInfo);
};

const canvas = document.getElementsByTagName('canvas')[0];

let position = { x: 0, y : 0 };
let dragInfo = null;
updateInfo();

canvas.addEventListener('mousedown', function(e) {
  dragInfo = {
    startEvent: {
      x: e.clientX,
      y: e.clientY,
    },
    startPosition: position
  };
  canvas.classList.add('dragging');
  updateInfo();
});

canvas.addEventListener('mousemove', function(e) {
  if (dragInfo === null) return;

  position = {
    x: dragInfo.startPosition.x - (e.clientX - dragInfo.startEvent.x),
    y: dragInfo.startPosition.y - (e.clientY - dragInfo.startEvent.y)
  };
  updateInfo();
});

canvas.addEventListener('mouseup', function(e) {
  dragInfo = null;
  canvas.classList.remove('dragging');
  updateInfo();
});

canvas.addEventListener('mouseout', function(e) {
  dragInfo = null;
  canvas.classList.remove('dragging');
  updateInfo();
});

* {
  user-select: none;
  font-family: monospace;
}
canvas {
  background: black;
  border: 1px solid red;
}
.dragging {
  cursor: move;
}
.obj {
  position: absolute;
  width: 50px;
  height: 50px;
  background: green;
  color: white;
  text-align: center;
  line-height: 50px;
  font-weight: bold;
}
.dragging ~ .obj {
  pointer-events: none;
}

<div id="myMap-ish">
  <canvas width="500" height="300"></canvas>
  <div class="obj" style="left: 30px; top: 35px">1</div>
  <div class="obj" style="left: 175px; top: 79px">2</div>
  <div class="obj" style="left: 214px; top: 145px">3</div>
  <div class="obj" style="left: 314px; top: 215px">4</div>
</div>
<div id="info"></div>







另一种选择是在外包装上使用mouseleave元素,该元素可以与上面添加的类结合使用,以简单地进行光标处理。

myMap-ishmouseout之间的主要区别在于,如下面的示例所示,mouseleave不会在悬停孩子时触发,因此我们无需像在第一个示例中那样切换pointer-events

注意,在第一个示例中,在mouseleave上简单地使用canvas会出现与mouseout相同的问题,因为“其他元素”不是canvas的子级。

堆栈片段



updateInfo = function() {
  document.getElementById('info').innerHTML =
    'Position = ' + JSON.stringify(position) +
    '<br />dragInfo = ' + JSON.stringify(dragInfo);
};

const canvas = document.getElementById('myMap-ish');

let position = { x: 0, y : 0 };
let dragInfo = null;
updateInfo();

canvas.addEventListener('mousedown', function(e) {
  dragInfo = {
    startEvent: {
      x: e.clientX,
      y: e.clientY,
    },
    startPosition: position
  };
  canvas.style.cursor = 'move';
  document.querySelectorAll('.obj')[0].style.cursor = 'move'; // TODO for all objects
  updateInfo();
});

canvas.addEventListener('mousemove', function(e) {
  if (dragInfo === null) return;

  position = {
    x: dragInfo.startPosition.x - (e.clientX - dragInfo.startEvent.x),
    y: dragInfo.startPosition.y - (e.clientY - dragInfo.startEvent.y)
  };
  updateInfo();
});

canvas.addEventListener('mouseup', function(e) {
  dragInfo = null;
  canvas.style.cursor = 'default';
  document.querySelectorAll('.obj')[0].style.cursor = 'default';  // TODO for all objects
  updateInfo();
});

canvas.addEventListener('mouseleave', function(e) {
  dragInfo = null;
  canvas.style.cursor = 'default';
  document.querySelectorAll('.obj')[0].style.cursor = 'default';  // TODO for all objects
  updateInfo();
});

* {
  user-select: none;
  font-family: monospace;
}
canvas {
  background: black;
  border: 1px solid red;
}
.obj {
  position: absolute;
  width: 50px;
  height: 50px;
  background: green;
  color: white;
  text-align: center;
  line-height: 50px;
  font-weight: bold;
}

<div id="myMap-ish">
  <canvas width="500" height="300"></canvas>
  <div class="obj" style="left: 30px; top: 35px">1</div>
  <div class="obj" style="left: 175px; top: 79px">2</div>
  <div class="obj" style="left: 214px; top: 145px">3</div>
  <div class="obj" style="left: 314px; top: 215px">4</div>
</div>
<div id="info"></div>

10-05 20:54
查看更多