Skip to content

Instantly share code, notes, and snippets.

@felipenmoura
Forked from joshbeckman/animatedScrollTo.js
Last active June 24, 2024 15:28
Show Gist options
  • Save felipenmoura/650e7e1292c1e7638bcf6c9f9aeb9dd5 to your computer and use it in GitHub Desktop.
Save felipenmoura/650e7e1292c1e7638bcf6c9f9aeb9dd5 to your computer and use it in GitHub Desktop.
ScrollTo animation using pure javascript and no jquery
/**
* Will gracefuly scroll the page
* This function will scroll the page using
* an `ease-in-out` effect.
*
* You can use it to scroll to a given element, as well.
* To do so, pass the element instead of a number as the position.
* Optionally, you can pass a `queryString` for an element selector.
*
* The default duration is half a second (500ms)
*
* This function returns a Promise that resolves as soon
* as it has finished scrolling. If a selector is passed and
* the element is not present in the page, it will reject.
*
* EXAMPLES:
*
* ```js
* window.scrollPageTo('#some-section', 2000);
* window.scrollPageTo(document.getElementById('some-section'), 1000);
* window.scrollPageTo(500); // will scroll to 500px in 500ms
* ```
*
* @returns {Promise}
* @param {HTMLElement|Number|Selector} Target
* @param {Number} Duration [default=500]
*
* Inspired by @andjosh's work
*
*/
function scrollPageTo (to, duration=500) {
//t = current time
//b = start value
//c = change in value
//d = duration
const 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;
};
return new Promise((resolve, reject) => {
const element = document.scrollingElement;
if (typeof to === 'string') {
to = document.querySelector(to) || reject();
}
if (typeof to !== 'number') {
to = to.getBoundingClientRect().top + element.scrollTop;
}
let start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
const animateScroll = function() {
currentTime += increment;
let val = easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
} else {
resolve();
}
};
animateScroll();
});
}
@9mm
Copy link

9mm commented Aug 27, 2021

An updated version that satisfies much more strict ESLint parameters, plus new ES syntax:

export const animateScrollTo = (to, duration) => {
  const element = document.scrollingElement || document.documentElement;
  const start = element.scrollTop;
  const change = to - start;
  const startDate = +new Date();
  // t = current time
  // b = start value
  // c = change in value
  // d = duration
  const easeInOutQuad = (t, b, c, d) => {
    let t2 = t;
    t2 /= d / 2;
    if (t2 < 1) return (c / 2) * t2 * t2 + b;
    t2 -= 1;
    return (-c / 2) * (t2 * (t2 - 2) - 1) + b;
  };
  const animateScroll = () => {
    const currentDate = +new Date();
    const currentTime = currentDate - startDate;
    element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration), 10);
    if (currentTime < duration) {
      requestAnimationFrame(animateScroll);
    } else {
      element.scrollTop = to;
    }
  };
  animateScroll();
};

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