Skip to content

Instantly share code, notes, and snippets.

@drwpow
Last active November 2, 2024 03:53
Show Gist options
  • Save drwpow/17f34dc5043a31017f6bbc8485f0da3c to your computer and use it in GitHub Desktop.
Save drwpow/17f34dc5043a31017f6bbc8485f0da3c to your computer and use it in GitHub Desktop.
Performant, 60FPS smooth scrolling in Vanilla JavaScript using requestAnimationFrame
/**
* @param {number} yPos Pixels from the top of the screen to scroll to
* @param {number} duration Time of animation in milliseconds
*/
const scrollTo = (yPos, duration = 600) => {
const startY = window.scrollY;
const difference = yPos - startY;
const startTime = performance.now();
const step = () => {
const progress = (performance.now() - startTime) / duration;
const amount = easeOutCubic(progress);
window.scrollTo({ top: startY + amount * difference });
if (progress < 0.99) {
window.requestAnimationFrame(step);
}
};
step();
}
// Easing function from https://gist.github.com/gre/1650294
const easeOutCubic = t => --t * t * t + 1;
@drwpow
Copy link
Author

drwpow commented Oct 22, 2018

Usage:

<button onClick={() => scrollTo(700)}>Scroll to 700px</button>

Snippet made to be used with react-scroll-agent (which provides window.scrollY values)

@simonsmith
Copy link

Adapted this slightly, mainly to be promise based. Have found it to be a reliable way to know when the scroll has finished:

export const smoothScrollTo = (
  y: number,
  {duration = 400, offset = 0} = {}
) => {
  const easeOutCubic = (t: number) => --t * t * t + 1;
  const startY = window.scrollY;
  const difference = y - startY;
  const startTime = performance.now();

  if (y === startY + offset) {
    return Promise.resolve(undefined);
  }

  return new Promise((resolve) => {
    const step = () => {
      const progress = (performance.now() - startTime) / duration;
      const amount = easeOutCubic(progress);
      window.scrollTo({top: startY + amount * difference - offset});
      if (progress < 0.99) {
        window.requestAnimationFrame(step);
      } else {
        resolve(undefined);
      }
    };
    step();
  });
};

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