0

How can I make a repeated scrollBy call smoother like when animating with jQuery's animate scrollTop?

Currently it is jumpy, the page jumps to and from the different scroll positions. How can I make it smoother?

Here is the scrollBy code:

window.scrollBy(0, -10*(scrollCount ? scrollCount<0 ? -1 : 1 : 0)) , 600*x); })(i);

And here is the for loop that contains it:

for(var i = 0; i < Math.abs(scrollCount); i++){
    (function(x){
        setTimeout(
        window.scrollBy(0, -10*(scrollCount ? scrollCount<0 ? -1 : 1 : 0))
       , 600*x); })(i);
    }
}
gomangomango
  • 661
  • 1
  • 10
  • 29
  • Use animate, check this: http://css-tricks.com/examples/SmoothPageScroll/ or this: http://stackoverflow.com/questions/8917921/cross-browser-javascript-not-jquery-scroll-to-top-animation – Entropyk Mar 25 '14 at 20:33
  • @CoolArts Yes, I tried that the .animate() function, but it didn't work. – gomangomango Mar 25 '14 at 20:39

2 Answers2

1

Usage

First add this to your page.

scrollByAnimated = function(scrollY, duration){
  var startTime = new Date().getTime();

  var startY = window.scrollY;
  var endY = startY + scrollY;
  var currentY = startY;
  var directionY = scrollY > 0 ? 'down' : 'up';

  var animationComplete;
  var count = 0;

  var animationId;

  if(duration === undefined){
    duration = 250;//ms
  }

  //grab scroll events from the browser
  var mousewheelevt=(/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel" //FF doesn't recognize mousewheel as of FF3.x

  //stop the current animation if its still going on an input from the user
  var cancelAnimation = function () {
    if(animationId!==undefined){
      window.cancelAnimationFrame(animationId)
      animationId=undefined;
    }

  }

  if (document.attachEvent) {
    //if IE (and Opera depending on user setting)
    document.attachEvent("on"+mousewheelevt, cancelAnimation)
  } else if (document.addEventListener) {
    //WC3 browsers
    document.addEventListener(mousewheelevt, cancelAnimation, false)
  }

  var step = function (a,b,c) {
    var now = new Date().getTime();
    var completeness = (now - startTime) / duration;
    window.scrollTo(0, Math.round(startY + completeness * scrollY));
    currentY = window.scrollY;
    if(directionY === 'up') {
      if (currentY === 0){
        animationComplete = true;
      }else{
        animationComplete = currentY<=endY;
      }
    } 
    if(directionY === 'down') {
      /*limitY is cross browser, we want the largest of these values*/ 
      var limitY = Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight );
      if(currentY + window.innerHeight === limitY){
        animationComplete = true;
      }else{
        animationComplete = currentY>=endY;
      }
    } 

    if(animationComplete===true){
      /*Stop animation*/
      return;
    }else{
      /*repeat animation*/
      if(count > 500){
        return;
      }else{
        count++;
        animationId = window.requestAnimationFrame(step);
      }

    }
  };
  /*start animation*/  
  step();
};

Then use it;

scrollByAnimated(100, 250);// change in scroll Y, duration in ms

Explanation

Here's a more robust version than your original code suggested you needed. Additional features include stop scrolling at the top and bottom of the page, uses requestAnimationFrame().

It also only supports scrolling up and down because thats all I need at this time. You'd be a cool, cool person if you added left and right to it.

It has only been tested in Chrome, so your milage may vary.

This code leverages requestAnimationFrame(). First scrollByAnimated() sets variables, then runs step() which loops until the duration has been reached.

At each frame it calculates the animation's completeness as a number from 0 to 1. This is the difference between the startTime and now, divided by duration.

This completeness value is then multiplied by the requested scrollY. This gives us our amount to scroll for each frame. Then we add the startY position to achieve a value that we can use window.scrollTo() to set the frame's position, rather than increment the current position.

Graham P Heath
  • 7,009
  • 3
  • 31
  • 45
0

try to use animate like this

$('html,body').animate({scrollTop: currentoffset},400);

serup
  • 527
  • 5
  • 11