最终效果
实现思路
简介:
首先我们可以先看一张图
我们可以从函数中拿到浏览器给我们的event对象,而event对象其中有几个值对这种效果的实现至关重要,从图中可以看出用到的这几个变量的意思
要实现这种效果,我们可以用定位来做,通过给draggable盒子定位,计算出他的top值跟left实现位置变动
而拖拽效果是我们点击盒子,按住鼠标才能拖拽,松开鼠标就终止拖拽
(注意:这里还要给拖动的盒子一个类,让里面的文字不能选中,不然在拖拽过程中可能会出现文字给选中的情况)
步骤一:初步实现基本拖拽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body,
html {
padding: 0;
margin: 0;
height: 100vh;
width: 100vw;
}
#draggable {
background-color: #eee;
width: 200px;
height: 200px;
font-style: 2rem;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
}
.no-select {
/* 文字无法选中 */
user-select: none;
cursor: move;
}
</style>
</head>
<body>
<div id="draggable">drag</div>
<script>
// 获取draggable盒子的dom元素(小知识点,直接拿该盒子的id直接当作dom元素用也行,id盒子可以不用做获取dom元素这步操作)
const draggable = document.getElementById('draggable')
// 盒子初始left
let styleLeft = 0
// 盒子初始top
let styleTop = 0
// draggable的盒子高度
let height = draggable.offsetHeight
// draggable的盒子宽度
let width = draggable.offsetWidth
// 盒子位移x距离
let cx
// 盒子位移y距离
let cy
// 鼠标按下事件
draggable.onmousedown = function (e) {
console.log(draggable.classList);
draggable.classList.add("no-select");
// 记录初始拖动鼠标位置
const startX = e.clientX
const startY = e.clientY
document.onmousemove = function (e) {
// if (isMouseDown) {
cx = e.clientX - startX + styleLeft
cy = e.clientY - startY + styleTop
draggable.style.left = cx + 'px';
draggable.style.top = cy + 'px';
// }
}
document.onmouseup = function (e) {
document.onmousemove = null
draggable.classList.remove('no-select');
styleLeft = cx
styleTop = cy
}
}
</script>
</body>
</html>
基本拖拽效果已经完成,但是我们发现,我们竟然可以拖拽出去浏览器可视范围边界?明显是一个bug,所以我们要给它限定范围,不能让它拖出去
这时候我们就要看回到这张图了
最简单的两种情况不能拖出左边和上面,无非就是cx = e.clientX - startX + styleLeft与cy = e.clientY - startY + styleTop的值不能小于0,如果拖到小于0了,那就让他强制等于0,卡在边界里面
还有两种情况,就是不能超出右边和下面,以右边为例子,从图中可以看出,当cx + draggable.offsetWidth = window.innerWidth的时候,盒子刚好在最右边靠边,所以在更右的时候,我们就要把值写死强制让它等于windows.innerWidth-draggable.innerWidth这里因为cx是一直变化的,所以不能直接让他等于cx
步骤二:解决缺陷(增加拖拽边界)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body,
html {
padding: 0;
margin: 0;
height: 100vh;
width: 100vw;
}
#draggable {
background-color: #eee;
width: 200px;
height: 200px;
font-style: 2rem;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
}
.no-select {
/* 文字无法选中 */
user-select: none;
cursor: move;
}
</style>
</head>
<body>
<div id="draggable">drag</div>
<script>
// 获取draggable盒子的dom元素(小知识点,直接拿该盒子的id直接当作dom元素用也行,id盒子可以不用做获取dom元素这步操作)
const draggable = document.getElementById('draggable')
// 盒子初始left
let styleLeft = 0
// 盒子初始top
let styleTop = 0
// draggable的盒子高度
let height = draggable.offsetHeight
// draggable的盒子宽度
let width = draggable.offsetWidth
// 盒子位移x距离
let cx
// 盒子位移y距离
let cy
// 鼠标按下事件
draggable.onmousedown = function (e) {
console.log(draggable.classList);
draggable.classList.add("no-select");
// 记录初始拖动鼠标位置
const startX = e.clientX
const startY = e.clientY
document.onmousemove = function (e) {
// if (isMouseDown) {
cx = e.clientX - startX + styleLeft
cy = e.clientY - startY + styleTop
if (cx < 0) {
cx = 0
}
if (cy < 0) {
cy = 0
}
if (cx + width > window.innerWidth) {
cx = window.innerWidth - width
}
if (cy + height > window.innerHeight) {
cy = window.innerHeight - height
}
draggable.style.left = cx + 'px';
draggable.style.top = cy + 'px';
// }
}
document.onmouseup = function (e) {
document.onmousemove = null
draggable.classList.remove('no-select');
styleLeft = cx
styleTop = cy
}
}
</script>
</body>
</html>
结语
这时候一个基本的拖拽效果就实现了