Skip to content

Instantly share code, notes, and snippets.

@james2doyle
Last active November 29, 2023 11:41
Show Gist options
  • Save james2doyle/5694700 to your computer and use it in GitHub Desktop.
Save james2doyle/5694700 to your computer and use it in GitHub Desktop.
a native scrollTo function in javascript that uses requestAnimationFrame and easing for animation
// easing functions http://goo.gl/5HLl8
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) {
return c/2*t*t + b
}
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
Math.easeInCubic = function(t, b, c, d) {
var tc = (t/=d)*t*t;
return b+c*(tc);
};
Math.inOutQuintic = function(t, b, c, d) {
var ts = (t/=d)*t,
tc = ts*t;
return b+c*(6*tc*ts + -15*ts*ts + 10*tc);
};
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function(){
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); };
})();
function scrollTo(to, callback, duration) {
// because it's so fucking difficult to detect the scrolling element, just move them all
function move(amount) {
document.documentElement.scrollTop = amount;
document.body.parentNode.scrollTop = amount;
document.body.scrollTop = amount;
}
function position() {
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop;
}
var start = position(),
change = to - start,
currentTime = 0,
increment = 20;
duration = (typeof(duration) === 'undefined') ? 500 : duration;
var animateScroll = function() {
// increment the time
currentTime += increment;
// find the value with the quadratic in-out easing function
var val = Math.easeInOutQuad(currentTime, start, change, duration);
// move the document.body
move(val);
// do the animation unless its over
if (currentTime < duration) {
requestAnimFrame(animateScroll);
} else {
if (callback && typeof(callback) === 'function') {
// the animation is done so lets callback
callback();
}
}
};
animateScroll();
}
@tranghaviet
Copy link

I found an other solution much simpler:

/**
 *  Scroll to element, for now it scroll to Top.
 * @param duration default 1000ms
 */
export const scrollToElement = (duration = 1000) => {
  let cosParameter = window.scrollY / 2
  let scrollCount = 0
  let oldTimestamp = performance.now()

  function step (newTimestamp) {
    scrollCount += Math.PI / (duration / (newTimestamp - oldTimestamp))
    if (scrollCount >= Math.PI) window.scrollTo(0, 0)
    if (window.scrollY === 0) return
    window.scrollTo(0, Math.round(cosParameter + cosParameter * Math.cos(scrollCount)))
    oldTimestamp = newTimestamp
    window.requestAnimationFrame(step)
  }

  window.requestAnimationFrame(step)
}

@dminkovsky
Copy link

dminkovsky commented Mar 14, 2019

shouldn't time be calculated from the timestamp passed to the raf callback? As in the example https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame?

@marinoguerieri
Copy link

Thank you man, you saved my ass after I run out of options for pulling out cross browser compatible animation. I lost like 5 hours in frustration, and your solution works best.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment