用户经常使用鼠标在可滚动容器中滚动。除此之外,一些应用程序还允许用户通过拖动元素来滚动。

这篇文章向您展示了一种使用 vanilla JavaScript 实现该功能的简单方法。

假设我们有一个可滚动的容器,如下所示:

<div id="container" class="container">...</div>

元素必须至少有两个 CSS 属性:

.container {
  cursor: grab;
  overflow: auto;
}

cursor: grab 表示该元素可以点击和拖动。

滚动到指定位置

只要元素是可滚动的,我们就可以通过设置 scrollTopscrollLeft 属性将其滚动到给定位置:

const ele = document.getElementById('container')
ele.scrollTop = 100
ele.scrollLeft = 150

拖动滚动

实现非常简单。通过使用 mousedown 用户单击元素时发生的事件:

let pos = {
  top: 0,
  left: 0,
  x: 0,
  y: 0
}

const mouseDownHandler = function (e) {
  pos = {
    // 当前滚动
    left: ele.scrollLeft,
    top: ele.scrollTop,
    // 获取当前鼠标位置
    x: e.clientX,
    y: e.clientY
  }

  document.addEventListener('mousemove', mouseMoveHandler)
  document.addEventListener('mouseup', mouseUpHandler)
}

pos 存储当前的滚动和鼠标位置。当用户移动鼠标时,我们计算它移动了多远,然后滚动到元素到相同的位置:

const mouseMoveHandler = function (e) {
  // 鼠标移动了多远
  const dx = e.clientX - pos.x
  const dy = e.clientY - pos.y

  // 滚动元素
  ele.scrollTop = pos.top - dy
  ele.scrollLeft = pos.left - dx
}

良好做法

正如你看到的上面的 lefttopxy 属性是相互关联的。最好将它们封装在单个变量中,pos 而不是创建四个变量。

最后,我们可以通过在用户开始移动鼠标时设置一些 CSS 属性来改善用户体验:

const mouseDownHandler = function (e) {
  // 改变光标并阻止用户选择文本
  ele.style.cursor = 'grabbing'
  ele.style.userSelect = 'none'
  // ...
}

释放鼠标时,移除事件并重置这些 CSS 属性:

const mouseUpHandler = function () {
  document.removeEventListener('mousemove', mouseMoveHandler)
  document.removeEventListener('mouseup', mouseUpHandler)

  ele.style.cursor = 'grab'
  ele.style.removeProperty('user-select')
}

查看效果