Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

animated-scroll-to

Stanko347.3kMIT2.3.2TypeScript support: included

Simple, plain JavaScript animated window scroll

scroll, scrollTo

readme

animated-scroll-to

npm version npm downloads

Lightweight (1.45kb gzipped) scroll to function with a powerful API. Scrolls window or any other DOM element.

The main difference to other libraries is that it accepts speed of scrolling instead of duration. This way scrolling for 200 pixels will last less than scrolling 10000 pixels. Minimum and maximum duration are configurable and set to reasonable defaults (250 and 3000ms).

All changes are tracked in CHANGELOG.

Demo

Play with the live demo.

Features

  • Accepts speed per 1000px instead of duration
  • Scrolls window or any other DOM element horizontally and vertically
  • Returns a promise with a boolean flag which tells you if desired scroll position was reached (for IE you'll need to include a Promise polyfill)
  • If called multiple times on the same element, it will cancel prior animations
  • Optionally prevent user from scrolling until scrolling animation is finished

Usage

Grab it from npm

npm install animated-scroll-to

and import it in your app

import animateScrollTo from 'animated-scroll-to';

// It returns a promise which will be resolved when scroll animation is finished

animateScrollTo(500).then((hasScrolledToPosition) => {
  // scroll animation is finished

  // "hasScrolledToPosition" indicates if page/element
  // was scrolled to a desired position
  // or if animation got interrupted
  if (hasScrolledToPosition) {
    // page is scrolled to a desired position
  } else {
    // scroll animation was interrupted by user
    // or by another call of "animateScrollTo"
  }
});

Method signatures

Library has three ways to call it:

// "y" is a desired vertical scroll position to scroll to
function animateScrollTo(y, options);

// "coords" are an array "[x, y]"
// Where "x" and "y" are desired horizontal and vertical positions to scroll to
// Both "x" and "y" can be null
// which will result in keeping the current scroll position for that axis
function animateScrollTo(coords, options);

// If you pass a DOM element, page will be scrolled to it
function animateScrollTo(scrollToElement, options);

Example usage of each method:

// Scrolls page vertically to 1000px
animateScrollTo(1000);

// Scrolls page horizontally to 1000px but keeps vertical scroll position
animateScrollTo([1000, null]);

// Scrolls page horizontally too 1000px and vertically to 500px
animateScrollTo([1000, 500]);

// Scrolls page both horizontally and vertically to ".my-element"
animateScrollTo(document.querySelector('.my-element'));

Options

Options with their default values:

const defaultOptions = {
  // Indicated if scroll animation should be canceled on user action (scroll/keypress/touch)
  // if set to "false" user input will be disabled until scroll animation is complete
  cancelOnUserAction: true,

  // Animation easing function, with "easeOutCubic" as default
  easing: (t) => --t * t * t + 1,

  // DOM element that should be scrolled
  // Example: document.querySelector('#element-to-scroll'),
  elementToScroll: window,

  // Horizontal scroll offset
  // Practical when you are scrolling to a DOM element and want to add some padding
  horizontalOffset: 0,

  // Maximum duration of the scroll animation
  maxDuration: 3000,

  // Minimum duration of the scroll animation
  minDuration: 250,

  // Duration of the scroll per 1000px
  speed: 500,

  // Vertical scroll offset
  // Practical when you are scrolling to a DOM element and want to add some padding
  verticalOffset: 0,
};

Easing

By default library is using easeOutCubic easing function. You can pass a custom function only considering the t value for the range [0, 1] => [0, 1].

To make things easier I provided a list of common easing function below:

/*
 * Easing Functions
 * https://gist.github.com/gre/1650294
 */
const EasingFunctions = {
  // no easing, no acceleration
  linear: (t) => {
    return t;
  },
  // accelerating from zero velocity
  easeInQuad: (t) => {
    return t * t;
  },
  // decelerating to zero velocity
  easeOutQuad: (t) => {
    return t * (2 - t);
  },
  // acceleration until halfway, then deceleration
  easeInOutQuad: (t) => {
    return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
  },
  // accelerating from zero velocity
  easeInCubic: (t) => {
    return t * t * t;
  },
  // decelerating to zero velocity
  easeOutCubic: (t) => {
    return --t * t * t + 1;
  },
  // acceleration until halfway, then deceleration
  easeInOutCubic: (t) => {
    return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
  },
  // accelerating from zero velocity
  easeInQuart: (t) => {
    return t * t * t * t;
  },
  // decelerating to zero velocity
  easeOutQuart: (t) => {
    return 1 - --t * t * t * t;
  },
  // acceleration until halfway, then deceleration
  easeInOutQuart: (t) => {
    return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
  },
  // accelerating from zero velocity
  easeInQuint: (t) => {
    return t * t * t * t * t;
  },
  // decelerating to zero velocity
  easeOutQuint: (t) => {
    return 1 + --t * t * t * t * t;
  },
  // acceleration until halfway, then deceleration
  easeInOutQuint: (t) => {
    return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
  },
};

Certain CSS properties might break the animation

As library is using an animation loop to scroll, some CSS properties might clash with the approach and break the animation.

The library will warn you about the ones that are know to break the animation:

  • scroll-behavior: smooth
  • scroll-snap-type: x mandatory (or y mandatory depending on the axis you scroll)

Scrolling an iframe

You can also use the library to scroll iframes from the same domain (check MDN contentWindow documentation).

const iframeWindow =
  document.querySelector('#my-iframe').contentWindow.document.documentElement;

animateScrollTo(500, {
  elementToScroll: iframeWindow,
});

Please note: If the iframe is not on the same domain as the base page, you are going to get a cross origin error.

Why?

I wasn't able to find standalone, simple and working solution.

Browser support

Anything that supports requestAnimationFrame and Promise. For Internet Explorer you'll need to add es6-promise polyfill.

For IE9 and lower, you'll to provide requestAnimationFrame polyfill.

For IE8 and lower, you'll need to polyfill Array.forEach as well. Haven't tested this though.

It is missing <insert a feature here>

I really tried to keep simple and lightweight. If you are missing something, feel free to open a pull request.

Version 1

I advise you to use v2, as v1 is deprecated. But it is still available on v1 branch and on npm.

changelog

Changelog

v2.3.2

17.01.2025.

Added

  • Improved the readme. Added two sections - on scrolling iframes and CSS properties that can break the animation.

v2.3.1

16.01.2025.

Added

  • Added a warning that scroll-snap-type: x mandatory might break the animation #71

v2.3.0

23.06.2022.

Changed

  • Switched to keen as build system.
  • Added ESM and CJS modules.
  • Upgraded dev dependencies.

v2.2.0

18.03.2020.

Fixed

  • Added a warning if the scrolling element has scroll-behavior: smooth #55

v2.1.0

08.03.2020.

Removed

  • Removed support for using the script in browsers directly. It was broken, so probably was not using it anyway. Use version 1 if you really need it.

v2.0.12

27.10.2020.

Fixed

  • Reverted labeled tuple coordinates for TCoords #49 as it breaks on older TS version #50.

v2.0.11

19.10.2020.

Fixed

  • Improved types, labeled tuple coordinates for TCoords #49.
  • Fixed build on windows #49.

v2.0.9, 2.0.10

25.06.2020.

Fixed

  • Added Promise<boolean> as a return type in the types definition file.

v2.0.8

07.05.2020.

Fixed

  • Event options were missing in removeEventListener #44

v2.0.6 and v2.0.7

20.04.2020.

Fixed

  • Calculating element offset inside of element was sometimes a pixel off.
  • Active animations weren't cleared on animation end
  • Now error is thrown in "elementToScroll" is not a parent of "scrollToElement"

v2.0.4 and v2.0.5

09.11.2019.

Fixed

  • Fixed TS types #36

v2.0.3

03.10.2019.

Fixed

  • Fixed library breaking when running on server #34

v2.0.2

26.09.2019.

Changed

  • Switched to commonjs module #33

v2.0.0 and v2.0.1

23.09.2019.

Changed

  • Full TypeScript rewrite
  • New method signatures
  • Method now returns a promise

v1.3.1

14.09.2019.

Fixed

  • Fixed if element was scrolling and cancelOnUserAction was passed the whole page couldn't scroll #28

v1.3.0

02.09.2019.

Changed

  • onComplete callback now has a boolean argument isCanceledByUserAction, and it is called even when scroll is canceled #26

v1.2.2

07.06.2018.

Fixed

  • Fix event listeners not being removed in IE11 #19
  • Updated index.d.ts to add offset #19

v1.2.1

17.05.2018.

Added

  • Added offset option, kudos to @weotch #17

Fixed

  • Chrome would throw Unable to preventDefault inside passive event listener invocation. when cancelOnUserAction was set to false #18

v1.2.0

22.04.2018.

Added

  • Added horizontal option, kudos to @jesseditson #15
  • Changelog

v1.1.11

09.04.2018.

Added

  • Set events to be passive by default, kudos to @cybot1711 #14

For changes prior version 1.1.11 please check the commit list.