6
\$\begingroup\$

I wrote a script to scroll pages when menu buttons are clicked, similar effect like: http://css-tricks.com/examples/SmoothPageScroll

var m1 = document.getElementById('m1'),
    m2 = document.getElementById('m2'),
    m3 = document.getElementById('m3'),
    m4 = document.getElementById('m4'),
    m5 = document.getElementById('m5'),
    m2PositionY = document.getElementById('id2').offsetTop,
    m3PositionY = document.getElementById('id3').offsetTop,
    doneValue = true,
    windowPageYOffset = 0;

function Scroll(ScrollValue1) {
    windowPageYOffset = window.pageYOffset;
    if(windowPageYOffset == ScrollValue1) {
        doneValue = true;
    }
    // scroll up
    else if(windowPageYOffset >= ScrollValue1) {
        window.scrollBy(0, -(1 + ((windowPageYOffset - ScrollValue1) / 10)));
    }
    // scroll down
    else {
        window.scrollBy(0, 1 + ((ScrollValue1 - windowPageYOffset) / 10));
    }
    if(windowPageYOffset != ScrollValue1) {
        var setTimeoutScrollValue1 = setTimeout('Scroll(' + ScrollValue1 + ')', 20);
    }
}

function doneOrNot(doneOrNotValue1) {
    if(doneValue && doneOrNotValue1 != windowPageYOffset) {
        Scroll(doneOrNotValue1);
        doneValue = false;
    }
}
m1.onclick = function () {
    doneOrNot(0); // no need for a id since top is allways 0
}
m2.onclick = function () {
    doneOrNot(m2PositionY);
}
m3.onclick = function () {
    doneOrNot(m3PositionY);
}

I am using a "true or false variable" (doneValue) to determine if previous Scroll is finished before next can start. I wonder if there is any better method to do this in JavaScript?

Please give other feedback if you have any :)

\$\endgroup\$
7
\$\begingroup\$

There are a few things you can do differently

  1. Animations are most often accomplished by getting the difference between start and end values, and animating a factor from zero to 1. Multiply that with the difference, and you get an offset.
  2. Instead of relying on an interval (which may be slow, since JS is single-threaded), most animation libraries compare the start time to the current time, to see how far along the animation should be.
  3. Instead of waiting for the animation to stop and a boolean to flip, you can clear the animation's timer, and forcibly stop the animation, before starting a new one. That way the user's clicks aren't ignored, but override previous clicks instead.

Here's an alternative implementation using that approach and other good stuff:

var smoothScrollTo = (function () {
  var timer, start, factor;

  return function (target, duration) {
    var offset = window.pageYOffset,
        delta  = target - window.pageYOffset; // Y-offset difference
    duration = duration || 1000;              // default 1 sec animation
    start = Date.now();                       // get start time
    factor = 0;

    if( timer ) {
      clearInterval(timer); // stop any running animation
    }

    function step() {
      var y;
      factor = (Date.now() - start) / duration; // get interpolation factor
      if( factor >= 1 ) {
        clearInterval(timer); // stop animation
        factor = 1;           // clip to max 1.0
      } 
      y = factor * delta + offset;
      window.scrollBy(0, y - window.pageYOffset);
    }

    timer = setInterval(step, 10);
    return timer; // return the interval timer, so you can clear it elsewhere
  };
}());

Here's a demo

Another advantage of always animating a factor between zero and 1 is that you can run it through other expressions, and (for instance) animated the scroll with a sine wave.

\$\endgroup\$
  • \$\begingroup\$ I refactored out the general animation logic from the specific act of scrolling and added support for easing functions. \$\endgroup\$ – Bill Barry Jun 27 '12 at 14:47
  • \$\begingroup\$ And added back in the canceling animation functionality: jsfiddle.net/DruwJ/3 \$\endgroup\$ – Bill Barry Jun 27 '12 at 14:56
  • \$\begingroup\$ Very nice, but it seems you're assigning change = to - from before you've checked the from and to arguments. I think you're going to get some NaN-nonsense there if you omit the from and to args but include an easing function. \$\endgroup\$ – Flambino Jun 27 '12 at 15:19
  • \$\begingroup\$ Good catch \$\endgroup\$ – Bill Barry Jun 27 '12 at 16:52
  • \$\begingroup\$ Thank you both so much! It looks really good! I have much to learn... Some questions: What does this "window.smoothScrollTo = function (...) {}" differ from "function smoothScrollTo(target, duration)"? Bill, can you show me a working example with easing out function? \$\endgroup\$ – user1087110 Jun 28 '12 at 7:49

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.