拖拽
在元素的拖拽上,市面上常用的:有H5提供下的drag事件,或者灵活度更大的mouse自定义形式去实现。
drag
触发时机: drag 事件在元素已经被拖动并且其数据(例如拖拽的元素)正在拖动时触发。这通常用于拖动的数据传输(例如拖拽文件到浏览器、拖拽元素到容器等)。
使用场景: drag 事件通常与 dragstart、dragover、dragend 等一系列拖拽事件一起使用,用于实现 HTML5 的拖放 API。它适用于需要拖动数据的场景,且不需要开发者手动控制拖动位置。
element.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', '拖动数据');
});
dropArea.addEventListener('dragover', (e) => {
e.preventDefault(); // 允许拖拽进入
});
dropArea.addEventListener('drop', (e) => {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain');
console.log('Dropped data:', data);
});
简单示例
<div class="dropzone">
<div id="draggable" draggable="true">这个 div 可以拖动</div>
</div>
<div class="dropzone" id="droptarget"></div>
<script>
let dragged;
/* 在可拖动的目标上触发的事件 */
const source = document.getElementById("draggable");
source.addEventListener("dragstart", (event) => {
// 保存被拖动元素的引用
dragged = event.target;
// 设置为半透明
event.target.classList.add("dragging");
});
source.addEventListener("dragend", (event) => {
// 拖动结束,重置透明度
event.target.classList.remove("dragging");
});
/* 在放置目标上触发的事件 */
const target = document.getElementById("droptarget");
target.addEventListener(
"dragover",
(event) => {
// 阻止默认行为以允许放置
event.preventDefault();
},
false,
);
target.addEventListener("dragenter", (event) => {
// 在可拖动元素进入潜在的放置目标时高亮显示该目标
if (event.target.classList.contains("dropzone")) {
event.target.classList.add("dragover");
}
});
target.addEventListener("dragleave", (event) => {
// 在可拖动元素离开潜在放置目标元素时重置该目标的背景
if (event.target.classList.contains("dropzone")) {
event.target.classList.remove("dragover");
}
});
target.addEventListener("drop", (event) => {
// 阻止默认行为(会作为某些元素的链接打开)
event.preventDefault();
// 将被拖动元素移动到选定的目标元素中
if (event.target.classList.contains("dropzone")) {
event.target.classList.remove("dragover");
dragged.parentNode.removeChild(dragged);
event.target.appendChild(dragged);
}
});
</script>
<style>
body {
/* Prevent the user selecting text in the example */
user-select: none;
}
#draggable {
text-align: center;
background: white;
}
.dropzone {
width: 200px;
height: 20px;
background: blueviolet;
margin: 10px;
padding: 10px;
}
.dropzone.dragover {
background-color: purple;
}
.dragging {
opacity: 0.5;
}
</style>
mouse事件
拥有更高的自定义能力,整个拖拽的过程更可控,其中的核心事件:mousedown,mouseover,mouseup事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件形式实现拖拽</title>
<style>
body {
width: 100vw;
height: 100vh;
margin: 0;
overflow: hidden;
}
.source {
background-color: red;
width: 100px;
height: 100px;
position: absolute;
cursor: grab;
user-select: none;
}
.source:active {
cursor: grabbing;
}
</style>
</head>
<body>
<div class="source" id="dragDiv"></div>
<script>
const source = document.getElementById('dragDiv');
let left = 0;
let topPos = 0;
let lastPageX = 0;
let lastPageY = 0;
let isDrag = false;
source.addEventListener('mousedown', (e) => {
if (e.button !== 0) return;
isDrag = true;
left = parseInt(source.style.left) || 0;
topPos = parseInt(source.style.top) || 0;
lastPageX = e.pageX;
lastPageY = e.pageY;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDrag) return;
// 记录上次的移动位置
const deltaX = e.pageX - lastPageX;
const deltaY = e.pageY - lastPageY;
left += deltaX;
topPos += deltaY;
source.style.left = `${left}px`;
source.style.top = `${topPos}px`;
lastPageX = e.pageX;
lastPageY = e.pageY;
e.preventDefault();
});
document.addEventListener('mouseup', () => {
isDrag = false;
});
</script>
</body>
</html>
如果使用vue项目的话,可以对如下部分进行封装下:
const useDrag = (target) => {
const left = ref(0);
const top = ref(0);
const isDragging = ref(false);
let lastPageX = 0;
let lastPageY = 0;
const handleMouseDown = (e) => {
if (e.button !== 0) return; // 只响应左键
isDragging.value = true;
left.value = parseInt(target.style.left) || 0; // 转换为数字
top.value = parseInt(target.style.top) || 0; // 转换为数字
lastPageX = e.pageX;
lastPageY = e.pageY;
e.preventDefault();
};
const handleMouseMove = (e) => {
if (!isDragging.value) return;
const deltaX = e.pageX - lastPageX;
const deltaY = e.pageY - lastPageY;
left.value += deltaX;
top.value += deltaY;
target.style.left = `${left.value}px`;
target.style.top = `${top.value}px`;
lastPageX = e.pageX;
lastPageY = e.pageY;
e.preventDefault();
};
const handleMouseUp = () => {
isDragging.value = false;
};
onMounted(() => {
target.addEventListener('mousedown', handleMouseDown);
document.body.addEventListener('mousemove', handleMouseMove);
document.body.addEventListener('mouseup', handleMouseUp);
});
onUnmounted(() => {
target.removeEventListener('mousedown', handleMouseDown);
document.body.removeEventListener('mousemove', handleMouseMove);
document.body.removeEventListener('mouseup', handleMouseUp);
});
return {
left,
top,
isDragging
};
};
区别和适用场景
| 特性 | mousedown | drag |
|---|---|---|
| 触发时机 | 鼠标按下时触发。 | 当元素开始被拖动时触发。 |
| 使用场景 | 用于实现自定义拖拽逻辑,如监听鼠标位置,计算位移等。 | 用于拖放操作的处理,配合 HTML5 的拖放 API。 |
| 事件流 | 是鼠标事件流的一部分,通常与 mousemove 和 mouseup 配合使用。 | 是 HTML5 拖放 API 的一部分,事件流包含 dragstart、dragover、dragend 等。 |
| 可自定义拖动内容 | 需要手动控制元素的拖动过程。 | 使用浏览器原生的拖放机制,自动处理元素位置变化。 |
| 应用场景 | 自定义的拖拽行为,通常用于实现复杂的拖拽交互。 | 用于简单的拖拽文件、数据、或者元素交换等。 |