实现鼠标拖动的问题
Problems implementing dragging by mouse
我想实现一个包含某些元素的可拖动地图。
--> 见 JSFiddle: https://jsfiddle.net/7ndx7s25/7/
通过使用mousedown
、mousemove
和mouseup
我实现了拖动。
但是我遇到了问题:
按下鼠标按钮然后移出 window 时,我没有收到 mouseup
事件。重新进入 window(很久以前就释放了鼠标按钮)我的地图仍然认为按钮已按下并因此出现异常。
当地图上有物体时,我在穿过这些物体时没有收到 mousemove
事件。因此,当我进入和离开这样的物体时,地图会悬挂和跳跃。
虽然在这些对象上,我仍然希望有一个 move
鼠标光标。我可以更改每个对象的 cursor
样式(在 Fiddle 中,我以对象 1 为例),但这似乎不是一个好方法。有没有更优雅的方案?
您需要例如mouseout
在离开 canvas
时捕获,尽管当光标移动到 其他元素 .
上时该事件也会触发
一个简单的解决方法是简单地添加一个 class 到 canvas
,在这些上设置 pointer-events: none
。
这样class你也可以控制光标,避免用脚本设置它。
堆栈片段
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-ish
元素,它可以与上面添加的 class 结合使用,以简化游标处理。
mouseout
和mouseleave
的主要区别是后者在悬停时不会触发children,如下例所示,所以我们不需要切换pointer-events
就像我们在第一个示例中所做的那样。
注意,在第一个示例中简单地使用 mouseleave
,在 canvas
上将有相同的问题 mouseout
,因为 "other element" 不是 canvas
的 children。
堆栈片段
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>
我想实现一个包含某些元素的可拖动地图。
--> 见 JSFiddle: https://jsfiddle.net/7ndx7s25/7/
通过使用mousedown
、mousemove
和mouseup
我实现了拖动。
但是我遇到了问题:
按下鼠标按钮然后移出 window 时,我没有收到
mouseup
事件。重新进入 window(很久以前就释放了鼠标按钮)我的地图仍然认为按钮已按下并因此出现异常。当地图上有物体时,我在穿过这些物体时没有收到
mousemove
事件。因此,当我进入和离开这样的物体时,地图会悬挂和跳跃。虽然在这些对象上,我仍然希望有一个
move
鼠标光标。我可以更改每个对象的cursor
样式(在 Fiddle 中,我以对象 1 为例),但这似乎不是一个好方法。有没有更优雅的方案?
您需要例如mouseout
在离开 canvas
时捕获,尽管当光标移动到 其他元素 .
一个简单的解决方法是简单地添加一个 class 到 canvas
,在这些上设置 pointer-events: none
。
这样class你也可以控制光标,避免用脚本设置它。
堆栈片段
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-ish
元素,它可以与上面添加的 class 结合使用,以简化游标处理。
mouseout
和mouseleave
的主要区别是后者在悬停时不会触发children,如下例所示,所以我们不需要切换pointer-events
就像我们在第一个示例中所做的那样。
注意,在第一个示例中简单地使用 mouseleave
,在 canvas
上将有相同的问题 mouseout
,因为 "other element" 不是 canvas
的 children。
堆栈片段
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>