如何在原生JavaScript中实现鼠标滚轮的平滑垂直滚动?

25

我是 Vanilla JS 的铁杆粉丝,目前正在一个项目中工作,在这个项目中我需要实现鼠标滚轮平滑滚动。我想使用 Vanilla JS 实现这一功能。 通过一些研究,我发现了一段 jQuery 代码片段,如下所示。

$(window).on('mousewheel DOMMouseScroll', function(e) {
   var dir,
   amt = 100;

   e.preventDefault();
   if(e.type === 'mousewheel') {
     dir = e.originalEvent.wheelDelta > 0 ? '-=' : '+=';
   }
   else {
     dir = e.originalEvent.detail < 0 ? '-=' : '+=';
   }      

   $('html, body').stop().animate({
     scrollTop: dir + amt
   },500, 'linear');
});

有没有人可以帮我了解如何在不使用像jQuery或其他库这样的辅助库的情况下实现平滑滚动。

许多人已经使用jQuery进行了许多实现。但是我想要一个最好的实现,可以在任何地方使用纯JS实现。 可以在React、Angular和Vue中的任何地方实现。

3个回答

91

这个怎么样:

function init(){
 new SmoothScroll(document,120,12)
}

function SmoothScroll(target, speed, smooth) {
 if (target === document)
  target = (document.scrollingElement 
              || document.documentElement 
              || document.body.parentNode 
              || document.body) // cross browser support for document scrolling
      
 var moving = false
 var pos = target.scrollTop
  var frame = target === document.body 
              && document.documentElement 
              ? document.documentElement 
              : target // safari is the new IE
  
 target.addEventListener('mousewheel', scrolled, { passive: false })
 target.addEventListener('DOMMouseScroll', scrolled, { passive: false })

 function scrolled(e) {
  e.preventDefault(); // disable default scrolling

  var delta = normalizeWheelDelta(e)

  pos += -delta * speed
  pos = Math.max(0, Math.min(pos, target.scrollHeight - frame.clientHeight)) // limit scrolling

  if (!moving) update()
 }

 function normalizeWheelDelta(e){
  if(e.detail){
   if(e.wheelDelta)
    return e.wheelDelta/e.detail/40 * (e.detail>0 ? 1 : -1) // Opera
   else
    return -e.detail/3 // Firefox
  }else
   return e.wheelDelta/120 // IE,Safari,Chrome
 }

 function update() {
  moving = true
    
  var delta = (pos - target.scrollTop) / smooth
    
  target.scrollTop += delta
    
  if (Math.abs(delta) > 0.5)
   requestFrame(update)
  else
   moving = false
 }

 var requestFrame = function() { // requestAnimationFrame cross browser
  return (
   window.requestAnimationFrame ||
   window.webkitRequestAnimationFrame ||
   window.mozRequestAnimationFrame ||
   window.oRequestAnimationFrame ||
   window.msRequestAnimationFrame ||
   function(func) {
    window.setTimeout(func, 1000 / 50);
   }
  );
 }()
}
p{
  font-size: 16pt;
  margin-bottom: 30%;
}
<body onload="init()">
  <h1>Lorem Ipsum</h1>
  
  <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
  
  <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
  
  <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
</body>

通过调用new SmoothScroll(target,speed,smooth)来使用。

参数:

  1. target:要平滑滚动的元素 - 可以是div或document
  2. speed:每次鼠标滚轮滚动的像素数量
  3. smooth:平滑系数,值越高越平滑。

感谢@Phrogz提供鼠标滚轮归一化方法

编辑:自Chrome 73以来,需要将mousewheel事件的事件侦听器标记为非被动才能调用preventDefault()。感谢@Fred K的提示。


5
这似乎是目前为止最好的实现,非常流畅。感谢您抽出时间来研究此问题。干杯 :) - Adeel Imran
4
这个解决方案真美妙!<3 - Nicholas
2
@JordanBelf 我也很感兴趣。我有一个问题,就是当你第一次使用鼠标然后滚动条或其他东西时,滚动会很跳。 - TheTrueTDF
1
请注意,为了使其正常工作,需要添加 <!DOCTYPE html> - Audi Nugraha
1
有人知道如何处理这个问题吗?当我们尝试使用滚动条或键盘滚动页面,然后移动鼠标时,脚本会回到先前浏览的位置。 - Andrzej Dzirba
显示剩余27条评论

2
你发布的代码几乎是纯js。只需要一些调整。
如果你有时间,看一下滚轮事件这里新的东西将是动画函数

// Code goes here

document.addEventListener('wheel',function (event){
  //only vertical scroll
  if (event.deltaY > 0)
  {
    event.preventDefault();
    smoothScroll(document.documentElement,100,1000)
  }
})
function smoothScroll (domElement,pixel,delay)
{
  const intervalToRepeat = 25;
  const step = (intervalToRepeat * pixel) / delay;
  if ( step < pixel)
  {
    domElement.scrollTop += step;
    setTimeout(function (){
      smoothScroll(domElement,pixel - step,delay)
    },intervalToRepeat);
  }
  
  
}
  
<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Hello Plunker!</h1>
    
    <div style="width:400px;height:200px;" >
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      <br>
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      <br>
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some <br>
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some <br>
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      lorem ipsum some lorem ipsum some lorem ipsum some lorem ipsum some 
      
    </div>
  </body>

</html>


2
不错,但是如果已经向下滚动,则向上滚动会出现问题。此外,繁忙的页面会导致滚动出现问题。与其根据距离设置延迟,不如选择一个小的延迟,比如5毫秒,然后每次函数运行时检查自上次运行以来的时间戳,并将滚动位置更新为计划滚动时间和滚动距离的一部分,而不仅仅是距离。 - N-ate
这个能够在水平滚动时工作吗? - gil hamer

1
一个纯JavaScript的onscroll事件会起作用:
var container = document.getElementById('myScrollingSurface');
var lastY = 0;
container.onscroll = function () {
  doSomethingCool(container.scrollTop - lastY);
  lastY = container.scrollTop;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接