拖拽原理思路详解
涉及事件
鼠标按下: mousedown
鼠标移动 :mousemove => 不断的改变元素的 top / left
鼠标抬起 :mouseu
原理图解
如图假如对div进行拖拽:
连接拖拽点
它的横纵坐标变化:就是增加移动的距离
拖拽实现
思路
-
保存鼠标点击的初始位置
startPos -
保存元素的初始位置
boxPos -
保存鼠标移动的位置
nowPos -
计算鼠标移动的差值
dis -
计算元素移动的距离
newBoxPos -
设置元素最终的位置
div.style.top
div.style.left
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 100px;
height: 100px;
background: red;
position: absolute;
}
</style>
</head>
<body>
<div></div>
<script>
{
let div = document.querySelector('div');
var startPos = {} // 1. 鼠标点击的位置
var boxPos={} // 2. 元素的初始位置
div.addEventListener("mousedown", (e)=>{
// 保存
// 初始鼠标位置
startPos.x = e.clientX;
startPos.y = e.clientY;
// 元素的初始位置
boxPos.x = parseFloat(getComputedStyle(div).left);
boxPos.y = parseFloat(getComputedStyle(div).top);
console.log(boxPos.x);
div.addEventListener("mousemove", (e)=>{
var nowPos = {
x : e.clientX,
y : e.clientY
}
var dis = {
x : nowPos.x - startPos.x,
y : nowPos.y - startPos.y
}
var newBoxPos = {
left : boxPos.x + dis.x,
top : boxPos.y + dis.y
}
div.style.top = newBoxPos.top + 'px';
div.style.left = newBoxPos.left + 'px';
});
});
}
</script>
</body>
</html>
运行结果仍然存在问题:
如果鼠标拖拽过快,鼠标甩出div,而事件只能在div上,div就脱离控制了。
解决办法:将鼠标移动事件,添加到document上
{
let div = document.querySelector('div');
var startPos = {} // 1. 鼠标点击的位置
var boxPos={} // 2. 元素的初始位置
div.addEventListener("mousedown", (e)=>{
// 保存
// 初始鼠标位置
startPos.x = e.clientX;
startPos.y = e.clientY;
// 元素的初始位置
boxPos.x = parseFloat(getComputedStyle(div).left);
boxPos.y = parseFloat(getComputedStyle(div).top);
console.log(boxPos.x);
document.addEventListener("mousemove", (e)=>{
var nowPos = {
x : e.clientX,
y : e.clientY
}
var dis = {
x : nowPos.x - startPos.x,
y : nowPos.y - startPos.y
}
var newBoxPos = {
left : boxPos.x + dis.x,
top : boxPos.y + dis.y
}
div.style.top = newBoxPos.top + 'px';
div.style.left = newBoxPos.left + 'px';
});
});
}
但松开鼠标事件还没做,我们把它补充上。
注意:清除事件 - 必须清除的是命名函数!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 100px;
height: 100px;
background: red;
position: absolute;
}
</style>
</head>
<body>
<div></div>
<script>
{
let div = document.querySelector('div');
var startPos = {} // 1. 鼠标点击的位置
var boxPos={} // 2. 元素的初始位置
div.addEventListener("mousedown", (e)=>{
// 保存
// 初始鼠标位置
startPos.x = e.clientX;
startPos.y = e.clientY;
// 元素的初始位置
boxPos.x = parseFloat(getComputedStyle(div).left);
boxPos.y = parseFloat(getComputedStyle(div).top);
document.addEventListener("mousemove", drag);
});
div.addEventListener("mouseup", ()=>{
document.removeEventListener("mousemove", drag);
});
function drag(e){
var nowPos = {
x : e.clientX,
y : e.clientY
}
var dis = {
x : nowPos.x - startPos.x,
y : nowPos.y - startPos.y
}
var newBoxPos = {
left : boxPos.x + dis.x,
top : boxPos.y + dis.y
}
div.style.top = newBoxPos.top + 'px';
div.style.left = newBoxPos.left + 'px';
}
}
</script>
</body>
</html>
代码优化
清除事件应该将其代码加在mousedown中,因为逻辑上应该是鼠标按下的时候,才再加当前document的鼠标移动和抬起的两个事件。
这样避免事件加多个,引起冲突。
我们来看看鼠标清除事件执行多少遍:
{
let div = document.querySelector('div');
var startPos = {} // 1. 鼠标点击的位置
var boxPos={} // 2. 元素的初始位置
div.addEventListener("mousedown", (e)=>{
// 保存
// 初始鼠标位置
startPos.x = e.clientX;
startPos.y = e.clientY;
// 元素的初始位置
boxPos.x = parseFloat(getComputedStyle(div).left);
boxPos.y = parseFloat(getComputedStyle(div).top);
document.addEventListener("mousemove", drag);
var i = 1;
div.addEventListener("mouseup", ()=>{
console.log(i++);
document.removeEventListener("mousemove", drag);
});
});
function drag(e){
var nowPos = {
x : e.clientX,
y : e.clientY
}
var dis = {
x : nowPos.x - startPos.x,
y : nowPos.y - startPos.y
}
var newBoxPos = {
left : boxPos.x + dis.x,
top : boxPos.y + dis.y
}
div.style.top = newBoxPos.top + 'px';
div.style.left = newBoxPos.left + 'px';
}
}
我们会发现移动次数越多,清除事件的执行次数就越多,原因是只要按下一次鼠标,就给div增加一个鼠标抬起事件,因此就会累加很多该事件。
为了提高性能:只需绑定一次清除事件。
{
let div = document.querySelector('div');
var startPos = {} // 1. 鼠标点击的位置
var boxPos={} // 2. 元素的初始位置
div.addEventListener("mousedown", (e)=>{
// 保存
// 初始鼠标位置
startPos.x = e.clientX;
startPos.y = e.clientY;
// 元素的初始位置
boxPos.x = parseFloat(getComputedStyle(div).left);
boxPos.y = parseFloat(getComputedStyle(div).top);
document.addEventListener("mousemove", drag);
var i = 1;
div.addEventListener("mouseup", ()=>{
console.log(i++);
document.removeEventListener("mousemove", drag);
},{
// 只绑定一次事件
once:true
});
});
function drag(e){
var nowPos = {
x : e.clientX,
y : e.clientY
}
var dis = {
x : nowPos.x - startPos.x,
y : nowPos.y - startPos.y
}
var newBoxPos = {
left : boxPos.x + dis.x,
top : boxPos.y + dis.y
}
div.style.top = newBoxPos.top + 'px';
div.style.left = newBoxPos.left + 'px';
}
}
现在接触绑定就只执行一次了。
实现限制范围的拖拽
实际拖拽过程中,放在边缘会出去一部分。
左侧的时候:left只要不是负值,div就不会出去了。
限制左侧:left不能是负值。
限制右侧:浏览器可视区宽度 - div宽度,它的left不能超过这个距离。
上下限制也同理,xiao迪就不再解释了。
{
let div = document.querySelector('div');
var startPos = {} // 1. 鼠标点击的位置
var boxPos={} // 2. 元素的初始位置
div.addEventListener("mousedown", (e)=>{
// 保存
// 初始鼠标位置
startPos.x = e.clientX;
startPos.y = e.clientY;
// 元素的初始位置
boxPos.x = parseFloat(getComputedStyle(div).left);
boxPos.y = parseFloat(getComputedStyle(div).top);
document.addEventListener("mousemove", drag);
var i = 1;
div.addEventListener("mouseup", ()=>{
console.log(i++);
document.removeEventListener("mousemove", drag);
},{
// 只绑定一次事件
once:true
});
});
function drag(e){
var nowPos = {
x : e.clientX,
y : e.clientY
}
var dis = {
x : nowPos.x - startPos.x,
y : nowPos.y - startPos.y
}
var newBoxPos = {
left : boxPos.x + dis.x,
top : boxPos.y + dis.y
}
// 限制左侧
if (newBoxPos.left < 0){
newBoxPos.left = 0;
}
// 限制右侧
var maxLeft = document.documentElement.clientWidth - div.offsetWidth;
if (newBoxPos.left > maxLeft){
newBoxPos.left = maxLeft;
}
// 限制上侧
if (newBoxPos.top < 0){
newBoxPos.top = 0;
}
// 限制下侧
var maxTop = document.documentElement.clientHeight;
if (newBoxPos.top > maxTop) {
newBoxPos.top = maxTop;
}
div.style.top = newBoxPos.top + 'px';
div.style.left = newBoxPos.left + 'px';
}
}
(后续待补充)