Slowly scroll down a page permanently without heavy CPU usage or laggy scrolling

Forivin picture Forivin · Jul 24, 2017 · Viewed 9.6k times · Source

I want to make a page scroll down slowly and smoothly. Well, the speed should actually adjustable. The user should also be able to scroll up manually while the script is scrolling down. First I tried this:

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

But it was causing a very heavy CPU load and the slowest speed was too fast. And in addition to that manually scrolling up did not work properly while the code was running.

Then I tried:

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

But the scrolling was not smooth when setting it too slow (e.g. 200).

Then I tried:

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

But again the CPU load was unreasonably high and scrolling up or down manually wasn't possible this way.

Is there a better way to do this?

Answer

ConnorsFan picture ConnorsFan · Aug 1, 2017

Here is one possible implementation. The refresh rate is fixed, and corresponds to fps in the code below. To make sure that the speed is constant, I consider the time elapsed since the previous scroll when calculating the new scroll position. Manual scrolling is allowed (with the scroll bar, with the mouse wheel, or with touch on mobile devices) and taken into account by processing scroll, wheel and touchmove events. You can see the code at work in 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);