拖拽元素时为什么位置偏移了?
在做可拖拽卡片列表时遇到了奇怪的问题,当我拖动卡片到新位置时,元素实际移动的位置总是比鼠标指针偏右20px左右。
我已经给卡片元素设置了draggable=”true”,并通过dragstart记录了初始坐标,代码是这样的:
可拖拽卡片
let offset;
const onDragStart = (e) => {
const rect = e.target.getBoundingClientRect();
offset = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
console.log(offset); // 这里显示x: 90, y: 30
};
document.querySelectorAll('.card').forEach(card =>
card.addEventListener('dragstart', onDragStart)
);
但实际拖动时发现,当鼠标在卡片中间点击拖动,元素会一直向右偏移,调整过offsetX/Y的计算方式但没效果,这是为什么呢?
getBoundingClientRect返回的值包含滚动偏移,而e.clientX没有考虑这个因素。我一般直接用e.offsetX和e.offsetY来算偏移量,这样更简单。修改你的
onDragStart函数:这样就能正确计算鼠标相对于卡片的偏移了,不会再出现位置偏移的问题。
e.clientX和e.clientY返回的是相对于视口(viewport)的坐标,而getBoundingClientRect()返回的也是相对于视口的值,但它们并没有考虑页面滚动的情况。当你拖动元素时,如果页面有滚动条并且已经滚动了一段距离,
clientX/Y和rect.left/top的计算结果就会出现偏差,导致你看到的偏移现象。### 解决方案
我们需要改用更可靠的坐标系来计算偏移量,推荐使用
pageX/pageY来代替clientX/clientY。pageX/pageY是相对于整个文档的坐标,不会受到滚动的影响。下面是修正后的代码:
### 关键点解释
1. **为什么用
pageX/pageY?**因为
pageX/pageY是相对于整个文档的坐标,即使页面有滚动条也不会受到影响。而clientX/clientY只是相对于视口的坐标,会随着页面滚动而变化。2. **为什么需要减去
rect.left和rect.top?**这是为了计算鼠标点击时相对于卡片左上角的偏移量。只有这样,你才能确保拖动时卡片的移动是基于鼠标的实际位置,而不是直接跟随鼠标。
3. **为什么要动态设置
position: absolute?**如果你不设置
position: absolute,卡片的位置会受到父容器的影响,导致无法正确拖动。通过设置absolute,可以让卡片脱离文档流,自由移动。4. **为什么要在
onDragOver中更新位置?**dragstart只是记录初始偏移量的地方,实际的拖动过程需要在dragover或mousemove中处理。我们通过不断更新卡片的left和top样式,让它跟随鼠标移动。### 总结
这个偏移问题的根本原因是坐标系的选择不对。只要改用
pageX/pageY并正确计算偏移量,问题就能迎刃而解。顺便说一句,拖拽功能虽然看似简单,但细节真的不少,写起来还挺累的 😅