拖拽原理思路詳解
涉及事件
鼠標按下: 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';
}
}
(後續待補充)