永久缓慢地向下滚动页面,而不会占用大量 CPU 或延迟滚动

Posted

技术标签:

【中文标题】永久缓慢地向下滚动页面,而不会占用大量 CPU 或延迟滚动【英文标题】:Slowly scroll down a page permanently without heavy CPU usage or laggy scrolling 【发布时间】:2017-12-29 10:39:03 【问题描述】:

我想让页面缓慢而流畅地向下滚动。嗯,速度实际上应该是可调的。用户还应该能够在脚本向下滚动时手动向上滚动。首先我尝试了这个:

var autoScrollDelay = 1
var autoScrollSpeed = 1
var autoScrollTimer
function setAutoScroll(newValue) 
    autoScrollSpeed = newValue ? newValue : autoScrollSpeed
    if (autoScrollTimer) 
        clearInterval(autoScrollTimer)
    
    if (autoScrollDelay) 
        autoScrollTimer = setInterval(function()
            window.scrollBy(0,autoScrollSpeed)
      ,autoScrollDelay)
    

setAutoScroll(1) // higher number =  faster scrolling

但它会导致 CPU 负载非常重,并且最慢的速度太快。除此之外,在代码运行时手动向上滚动也无法正常工作。

然后我尝试了:

var autoScrollDelay = 1
var autoScrollSpeed = 1
var autoScrollTimer
function setAutoScroll(newValue) 
    autoScrollDelay = newValue ? newValue : autoScrollDelay //using autoScrollDelay instead of autoScrollSpeed
    if (autoScrollTimer) 
        clearInterval(autoScrollTimer)
    
    if (autoScrollDelay) 
        autoScrollTimer = setInterval(function()
            window.scrollBy(0,autoScrollSpeed)
      ,autoScrollDelay)
    

setAutoScroll(200) // higher number scrolls slower

但设置太慢时滚动不流畅(例如 200)。

然后我尝试了:

$("html, body").animate(
    scrollTop: $('html, body').get(0).scrollHeight, 
, 40000, "linear");

但 CPU 负载再次过高,无法通过这种方式手动向上或向下滚动。

有没有更好的方法来做到这一点?

【问题讨论】:

CSS translateY 可能是您的最佳选择。 你能链接到你的页面吗?页面重量是多少?页面滚动时您是否有很多沉重的图像或动画或发生的任何事情? 我在哪个页面上运行代码并不重要。你可以在 *** 上尝试一下。我的 Firefox 显示 10-12% 的 cpu 负载,这可能与单线程脚本在 8 线程 cpu 上的负载一样多。 我的答案是:不要乱滚动..没有插件/好方法可以创建自定义滚动行为 【参考方案1】:

这是一种可能的实现方式。刷新率是固定的,对应下面代码中的fps。为了确保速度恒定,我在计算新的滚动位置时考虑了自上次滚动以来经过的时间。允许手动滚动(使用滚动条、鼠标滚轮或触摸移动设备)并通过处理scrollwheeltouchmove 事件来考虑。您可以在this codepen 中查看代码。

var fps = 100;
var speedFactor = 0.001;
var minDelta = 0.5;
var autoScrollSpeed = 10;
var autoScrollTimer, restartTimer;
var isScrolling = false;
var prevPos = 0, currentPos = 0;
var currentTime, prevTime, timeDiff;

window.addEventListener("scroll", function (e) 
    // window.pageYOffset is the fallback value for IE
    currentPos = window.scrollY || window.pageYOffset;
);

window.addEventListener("wheel", handleManualScroll);
window.addEventListener("touchmove", handleManualScroll);

function handleManualScroll() 
    // window.pageYOffset is the fallback value for IE
    currentPos = window.scrollY || window.pageYOffset;
    clearInterval(autoScrollTimer);
    if (restartTimer) 
        clearTimeout(restartTimer);
    
    restartTimer = setTimeout(() => 
        prevTime = null;
        setAutoScroll();
    , 50);


function setAutoScroll(newValue) 
    if (newValue) 
        autoScrollSpeed = speedFactor * newValue;
    
    if (autoScrollTimer) 
        clearInterval(autoScrollTimer);
    
    autoScrollTimer = setInterval(function()
        currentTime = Date.now();
        if (prevTime) 
            if (!isScrolling) 
                timeDiff = currentTime - prevTime;
                currentPos += autoScrollSpeed * timeDiff;
                if (Math.abs(currentPos - prevPos) >= minDelta) 
                    isScrolling = true;
                    window.scrollTo(0, currentPos);
                    isScrolling = false;
                    prevPos = currentPos;
                    prevTime = currentTime;
                
            
         else 
            prevTime = currentTime;
        
    , 1000 / fps);


setAutoScroll(20);

【讨论】:

有什么方法可以在运行时允许手动滚动? @Forivin - 使用当前代码,我可以在自动滚动进行时手动滚动(在 Chrome、FF 和 IE 上使用 the codepen 进行了测试)。你不能? 并非如此。感觉非常反应迟钝。即使我尝试尽可能快地手动向上或向下滚动,我也几乎看不到任何反应。就像手动滚动距离设置为 1px 并且仅每 2 秒工作一次。在 chromium 和 firefox 中测试(启用平滑滚动)。如果我在 Firefox 中禁用平滑滚动,它实际上工作正常。但我需要一种适用于两种模式的解决方案。 我正在使用滚动条测试“手动滚动”,没有任何问题。也许您正在使用鼠标滚轮。你可以试试this codepen,里面包含了一些鼠标滚轮事件的代码。 你可以试试this codepen。针对wheeltouchmove 事件处理手动滚动。然而,我在 iPad 上的测试并不成功(自动滚动本身不起作用)。在 ios 上滚动似乎完全不同...【参考方案2】:

this article 的函数使用 vanilla JS 来实现各种速度下的平滑滚动。这是一个演示:

document.getElementById("scrollBottomButton").onclick = function() 
  var duration = document.getElementById("bottomScrollDuration").value * 1000;
  scrollIt(document.querySelector("#bottom-row"), duration, "easeOutQuad");
;

document.getElementById("scrollTopButton").onclick = function() 
  var duration = document.getElementById("topScrollDuration").value * 1000;
  scrollIt(document.getElementById("top-row"), duration, "easeOutQuad");
;

// thanks to https://pawelgrzybek.com/page-scroll-in-vanilla-javascript/
function scrollIt(destination, duration = 200, easing = "linear", callback) 
  const easings = 
    linear(t) 
      return t;
    ,
    easeOutQuad(t) 
      return t * (2 - t);
    
  ;

  const start = window.pageYOffset;
  const startTime = "now" in window.performance
  ? performance.now()
  : new Date().getTime();

  const documentHeight = Math.max(
    document.body.scrollHeight,
    document.body.offsetHeight,
    document.documentElement.clientHeight,
    document.documentElement.scrollHeight,
    document.documentElement.offsetHeight
  );
  const windowHeight =
        window.innerHeight ||
        document.documentElement.clientHeight ||
        document.getElementsByTagName("body")[0].clientHeight;
  const destinationOffset = typeof destination === "number"
  ? destination
  : destination.offsetTop;
  const destinationOffsetToScroll = Math.round(
    documentHeight - destinationOffset < windowHeight
    ? documentHeight - windowHeight
    : destinationOffset
  );

  if ("requestAnimationFrame" in window === false) 
    window.scroll(0, destinationOffsetToScroll);
    if (callback) 
      callback();
    
    return;
  

  function scroll() 
    const now = "now" in window.performance
    ? performance.now()
    : new Date().getTime();
    const time = Math.min(1, (now - startTime) / duration);
    const timeFunction = easings[easing](time);
    window.scroll(
      0,
      Math.ceil(timeFunction * (destinationOffsetToScroll - start) + start)
    );

    if (window.pageYOffset === destinationOffsetToScroll) 
      if (callback) 
        callback();
      
      return;
    

    requestAnimationFrame(scroll);
  

  scroll();



// scroll testing    
var middleHtml = [];

const schiller = "Nur Beharrung führt zum Ziel, Nur die Fülle führt zur Klarheit, Und im Abgrund wohnt die Wahrheit.".split(' ')

for(var i=0; i<schiller.length;i+=1)
  middleHtml.push("<div class=' container row' id='scrolling'><h1 style='margin: 30rem 10rem 30rem 0;font-size: 3.5em;font-family: Helvetica, sans-serif;color: #fff;'>"+schiller[i]+"</h1></div>");



document.getElementById('middle').innerHTML = middleHtml.join('');
.container-fluid 
background: #e52d27;
background: -webkit-linear-gradient(to top, #b31217, #e52d27);
background: linear-gradient(to top, #b31217, #e52d27);


.container-fluid input, .container-fluid .btn 
  border-radius: 0;


.btn 
  background: rgba(210,200,200,0.95);
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>

<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>

<div class='container-fluid'>
  <div class='row' id='top-row'>
    <div class='col-sm-8'>
      <input class='form-control' id='bottomScrollDuration' placeholder='Enter duration in seconds (4, 25, 40, etc...)' />
    </div>
    <div class='col-sm-4'>
      <button class='btn' id='scrollBottomButton'>Scroll to bottom</button>
    </div>    
  </div>
  <div id='middle'>    
  </div>

  <div class='row' id='bottom-row'>
    <div class='col-sm-8'>
      <input class='form-control' id='topScrollDuration' placeholder='Enter duration in seconds (4, 25, 40, etc...)' />
    </div>
    <div class='col-sm-4'>
      <button class='btn' id='scrollTopButton'>Scroll to top</button>
    </div>
  </div>
</div>

CodePen Demo

更新

如果您只是想调整速度并保持恒定的滚动行为,您可以试试这个:

function pageScroll(speed) 
    window.scrollBy(0,1);
    scrolldelay = setTimeout(pageScroll,speed);

然后以您选择的速度调用该函数,即:

pageScroll(1);

我在 Chrome 中运行它,它并没有占用我的 CPU 资源。在 Firefox 中运行时,CPU 确实会出现更多峰值。

【讨论】:

这段代码在 CPU 上似乎更轻,速度取决于页面的高度,因为您设置了滚动持续时间而不是滚动速度。哦,到达底部时滚动不应该停止。 您可以在此功能中选择要滚动到的位置,但我想这不是您想要的。如果页面底部没有停止滚动,滚动是如何停止的?用户是否停止滚动?超时了吗? @Forivin 我已经用一个较小的函数更新了我的答案,该函数不会使 Chrome 中的 CPU 使用率飙升。但是,当它在 Firefox 中运行时,CPU 使用率确实会飙升。 该函数的问题与我的第一个代码 sn-p 的问题完全相同。即使以 1 的速度滚动也太快了,它会导致相同的 CPU 负载(至少在最新的 Firefox 上)并且手动向上滚动无法正常工作。 如果用户想在某个点停止滚动,您还应该添加 .stop 来中断滚动。看看这个作为参考:***.com/a/18445654/4465062

以上是关于永久缓慢地向下滚动页面,而不会占用大量 CPU 或延迟滚动的主要内容,如果未能解决你的问题,请参考以下文章

java性能调优——技客时间

如何自动向下滚动html页面?

Vue页面在加载时不会向下滚动

性能调优 -- 哪些计算机资源有可能成为系统的性能瓶颈?

在 Python 中使用 PhantomJS 向下滚动到无限页面的底部

CSS 性能问题