如何基于mousemove事件的clientX值控制div的scrollLeft实现平滑拖拽滚动
问题根源
你出现跳变的核心原因是混淆了两个完全独立的坐标体系,并且漏存了鼠标按下时的初始横坐标:
clientX是鼠标相对于整个浏览器视口左上角的横坐标,和div的滚动状态没有任何关联scrollLeft是可滚动容器内部内容向左滚动的偏移量,和视口位置无关
你直接把这两个值相加的逻辑本身就是不成立的,自然会出现大幅跳变。
正确实现逻辑
拖拽滚动的核心是计算「鼠标移动的相对位移」,用初始滚动偏移量减去这个位移即可得到正确的滚动位置:
- 鼠标按下时,同时记录两个初始值:当前容器的
scrollLeft、当前鼠标的clientX - 鼠标移动时,计算当前鼠标位置和初始鼠标位置的差值,用初始
scrollLeft减去这个差值更新容器滚动位置
完整代码示例
const scrollDiv = document.querySelector('#你的可滚动div选择器'); let isDragging = false; let initScrollLeft = 0; let initMouseX = 0; // 鼠标按下触发 scrollDiv.addEventListener('mousedown', (e) => { isDragging = true; // 记录初始状态 initScrollLeft = scrollDiv.scrollLeft; initMouseX = e.clientX; // 优化:防止拖拽时选中文本 scrollDiv.style.userSelect = 'none'; scrollDiv.style.cursor = 'grabbing'; }); // 鼠标移动触发(绑定到document防止鼠标移出div后拖拽中断) document.addEventListener('mousemove', (e) => { if (!isDragging) return; // 计算位移差 const moveX = e.clientX - initMouseX; // 更新滚动位置 scrollDiv.scrollLeft = initScrollLeft - moveX; }); // 鼠标抬起/移出容器时结束拖拽 const endDrag = () => { if (!isDragging) return; isDragging = false; scrollDiv.style.userSelect = 'auto'; scrollDiv.style.cursor = 'grab'; }; scrollDiv.addEventListener('mouseup', endDrag); scrollDiv.addEventListener('mouseleave', endDrag);
可选优化
- 给容器默认加CSS
cursor: grab提示可拖拽 - 用
requestAnimationFrame包裹滚动逻辑,优化高刷新率屏幕下的流畅度 - 如果需要适配移动端,替换对应的
touchstart/touchmove/touchend事件即可
内容的提问来源于stack exchange,提问作者Riri
相关产品推荐
相关产品推荐

