本文详细解析 JavaScript 拖拽功能的两个进阶应用:限制物体在指定父级 div 内移动,以及实现类似 Photoshop 的自动吸附效果。通过对比基础拖拽代码,说明如何计算边界坐标以限制范围,并利用阈值判断实现接近目标时的自动赋值吸附,附带完整 HTML、CSS 及 JS 代码示例。
限制拖拽范围与自动吸附功能概述
本文将深入探讨 JavaScript 拖拽功能的两个进阶应用场景:一是限制拖拽物体不超出指定父级 div 的范围,二是实现拖拽物体的自动吸附功能。
在前一篇关于限制拖拽可视区范围的文章基础上,我们进一步增加一个父级 div 容器,确保拖拽物体不会超出该父级容器的边界。其核心原理与之前的限制可视区方法一致,实现起来相对简单。
限制在父级 div 内的拖拽实现
以下是实现该功能所需的 HTML 结构代码。
<div id="div2">
<div id="div1">
</div>
</div>
接下来是配套的 CSS 样式代码,用于定义拖拽物体及父级容器的外观和定位。
<style type="text/css">
#div1 {
width: 100px;
height: 100px;
background: red;
position: absolute;
}
#div2 {
width: 400px;
height: 300px;
background: #CCCCCC;
position: relative;
}
</style>
核心逻辑由 JavaScript 代码实现,负责处理鼠标事件及位置计算。
<script type="text/javascript">
// 拖拽空div 低版本的火狐有bug
window.onload = function() {
var oDiv = document.getElementById("div1");
var oDiv2 = document.getElementById("div2");
var disX = 0;
var disY = 0;
oDiv.onmousedown = function(ev) {
var oEvent = ev || event;
disX = oEvent.clientX - oDiv.offsetLeft;
disY = oEvent.clientY - oDiv.offsetTop;
document.onmousemove = function(ev) {
var oEvent = ev || event;
// 存储div当前的位置
var oDivLeft = oEvent.clientX - disX;
var oDivTop = oEvent.clientY - disY;
if (oDivLeft < 0) {
oDivLeft = 0;
} else if (oDivLeft > oDiv2.offsetWidth - oDiv.offsetWidth) {
oDivLeft = oDiv2.offsetWidth - oDiv.offsetWidth;
}
if (oDivTop < 0) {
oDivTop = 0;
} else if (oDivTop > oDiv2.offsetHeight - oDiv.offsetHeight) {
oDivTop = oDiv2.offsetHeight - oDiv.offsetHeight;
}
oDiv.style.left = oDivLeft + 'px';
oDiv.style.top = oDivTop + 'px';
};
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
return false; // 阻止默认事件,解决火狐的bug
};
};
</script>
运行上述代码后的效果如下图所示。

整个过程实现起来并不复杂。
拖拽自动吸附功能原理与代码
接下来,我们将介绍如何实现拖拽物体的自动吸附功能。
这种功能在实际开发中非常常见,例如在 Photoshop 等软件中,当窗口拖动到页面边缘附近时,会自动吸附到边界。
那么,我们的拖拽功能如何具备这种自动吸附的特性呢?
这其实与之前提到的运动缓冲原理类似。就像打车时,司机不可能做到分毫不差地停在指定点,而是会停在靠近目的地的位置。
程序逻辑也是如此:当拖拽物体接近目标位置时,直接将其坐标赋值给目标值即可实现吸附。例如,假设当拖拽物体距离左侧边界 50px 时,我们判定其已到达左侧,则直接将其 left 值赋为 0,从而实现自动吸附效果。
原理虽然简单,但具体代码实现需要稍作修改,请查看以下代码。
<script type="text/javascript">
window.onload = function() {
var oDiv = document.getElementById("div1");
var oDiv2 = document.getElementById("div2");
var disX = 0;
var disY = 0;
oDiv.onmousedown = function(ev) {
var oEvent = ev || event;
disX = oEvent.clientX - oDiv.offsetLeft;
disY = oEvent.clientY - oDiv.offsetTop;
document.onmousemove = function(ev) {
var oEvent = ev || event;
var oDivLeft = oEvent.clientX - disX;
var oDivTop = oEvent.clientY - disY;
// 当left 小于50 就自动归0 这样实现吸附
if (oDivLeft < 50) {
oDivLeft = 0;
} else if (oDivLeft > oDiv2.offsetWidth - oDiv.offsetWidth) {
oDivLeft = oDiv2.offsetWidth - oDiv.offsetWidth;
}
if (oDivTop < 0) {
oDivTop = 0;
} else if (oDivTop > oDiv2.offsetHeight - oDiv.offsetHeight) {
oDivTop = oDiv2.offsetHeight - oDiv.offsetHeight;
}
oDiv.style.left = oDivLeft + 'px';
oDiv.style.top = oDivTop + 'px';
};
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
return false;
};
};
</script>
后续高级应用展望
下一次我们将讲解更高级的应用,这些功能将更加复杂且实用,进一步完善我们的拖拽功能库。
例如图片拖拽和文字选中等场景。目前我们的示例页面中仅包含一个 div,而在实际日常开发中,这种情况较为少见。
那么,在页面存在其他元素的情况下,当前的拖拽实现会出现什么问题呢?
这些问题将在后续文章中解决,敬请期待。