Skip to content

faustienf/easing-scroll

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

easing-scroll

npm version npm bundle size license TypeScript

Programmatic smooth scrolling with custom easing, abort support, and promise-based completion tracking.

Demo

Highlights

  • Zero dependencies — ~450 bytes min+gzip
  • TypeScript-first — written in TypeScript, ships type declarations
  • Dual package — ESM and CJS builds
  • Customizable — bring your own easing function
  • Cancellable — abort with AbortSignal
  • Promise-basedawait completion or track partial progress
  • Universal — works with any scrollable Element

Install

npm install easing-scroll
pnpm add easing-scroll

Quick Start

import { easingScroll } from "easing-scroll";

const container = document.querySelector(".container");

await easingScroll(container, {
  top: 300,
  duration: 400,
  easing: (x) => 1 - Math.pow(1 - x, 3), // easeOutCubic
});

API

easingScroll(target, options): Promise<number>

Smoothly scrolls target to the given position.

target

Type: Element

Any scrollable DOM element.

options

Option Type Default Description
top number Target vertical scroll position in pixels
left number Target horizontal scroll position in pixels
duration number 0 Animation duration in milliseconds
easing (t: number) => number (t) => t Easing function mapping progress (0–1) to eased value
signal AbortSignal Signal to cancel the animation

Return value

Resolves with a number between 0 and 1 representing animation progress:

Value Meaning
1 Animation completed fully
0 < x < 1 Animation was aborted at x progress
0 Animation never started (signal was already aborted)

Behavior

  • Instant scroll — when duration is 0 or negative, the element scrolls instantly and resolves 1.
  • No-op — when both top and left are omitted, resolves 1 immediately.
  • Clamping — scroll values are clamped to the element's scrollable range. No visual flash occurs.
  • Already-aborted signal — resolves 0 without scrolling.

Examples

Custom Easing

The default easing is linear (t) => t. Pass any function from easings.net:

await easingScroll(element, {
  top: 500,
  duration: 600,
  // https://easings.net/#easeOutCubic
  easing: (x) => 1 - Math.pow(1 - x, 3),
});

Abort Scrolling

Use an AbortController to cancel an in-flight animation:

const controller = new AbortController();

setTimeout(() => controller.abort(), 100);

const progress = await easingScroll(element, {
  top: 1000,
  duration: 400,
  signal: controller.signal,
});

if (progress < 1) {
  console.log(`Aborted at ${Math.round(progress * 100)}%`);
}

React Hook

A reusable hook that cancels the previous scroll when dependencies change or the component unmounts:

import { useEffect, RefObject } from "react";
import { easingScroll } from "easing-scroll";

function useEasingScroll(ref: RefObject<HTMLElement | null>, top: number) {
  useEffect(() => {
    const target = ref.current;
    if (!target) return;

    const controller = new AbortController();

    easingScroll(target, {
      top,
      duration: 400,
      signal: controller.signal,
      easing: (x) => 1 - Math.pow(1 - x, 3),
    });

    return () => controller.abort();
  }, [top]);
}

License

MIT