Elastic Overscroll List
Rubber-band overscroll with the real asymptotic curve, engaged only at the boundary.
DOM + CSS0 deps1 file · 5 KB TSX
Props
"use client";
import { useRef } from "react";
/**
* Rubber-band overscroll for a scroll container, for the platforms and
* containers that do not get it for free: Android, Windows, and anywhere you
* have set overscroll-behavior to stop the gesture chaining to the page.
*/
/**
* THE APPLE RUBBER-BAND FUNCTION.
*
* f(x) = (x * d * c) / (d + c * |x|)
*
* where d is the container's size and c is roughly 0.55. It is asymptotic: as
* the drag grows the travel approaches d and never exceeds it, so resistance
* rises continuously and the limit is never actually reached.
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}Details
About this animation
The bounce at the end of an iOS scroll is one curve, and substituting a linear multiplier with a clamp is why most reimplementations feel wrong rather than merely different. The real function divides the drag by a term that grows with the drag, which makes it asymptotic: travel approaches the container's own size and never passes it, resistance rises continuously, and the limit is never actually reached. A clamped linear version hits its maximum almost immediately, so the last two thirds of the gesture produce no response at all — the curve is the effect. Three implementation details decide whether it feels attached to the content. The rubber band engages only when the container is already at a boundary and the drag pushes further, because applying the transform on every move fights the container's own scrolling and the list stutters as the two disagree. The transform is applied to the inner content rather than to the scroller, since transforming a scroll container makes it a containing block for fixed descendants and interferes with its own scrolling. And a new drag reads the current translate off the computed style and reapplies it without a transition, or the content jumps from wherever the previous spring had reached.
Curator’s note
Linear travel with a clamp is the usual stand-in and it feels dead — you reach the clamp instantly and the rest of the gesture does nothing.
