Customize scrollbar in modern browsers with smooth scrolling experience.
—
Extensible plugin architecture for adding custom behaviors like overscroll effects, custom easing, or specialized scrolling modes. The plugin system provides lifecycle hooks and delta transformation capabilities.
Base class for creating custom scrollbar plugins with lifecycle management and event transformation.
/**
* Base class for scrollbar plugins
*/
class ScrollbarPlugin {
/** Plugin identifier - must be set by subclasses */
static pluginName: string;
/** Default options for the plugin */
static defaultOptions: any;
/** Reference to the scrollbar instance */
readonly scrollbar: Scrollbar;
/** Plugin configuration options */
readonly options: any;
/** Plugin name (same as pluginName) */
readonly name: string;
constructor(scrollbar: Scrollbar, options?: any);
}Methods called during various phases of the scrollbar lifecycle.
/**
* Called when the plugin is initialized
*/
onInit(): void;
/**
* Called when the plugin is being destroyed
*/
onDestroy(): void;
/**
* Called when the scrollbar updates (e.g., after resize)
*/
onUpdate(): void;
/**
* Called during each render frame with remaining momentum
* @param remainMomentum - Remaining scroll momentum after frame
*/
onRender(remainMomentum: Data2d): void;Method for intercepting and modifying scroll events before they're processed.
/**
* Transforms scroll delta values from input events
* @param delta - Original scroll delta from input event
* @param evt - The original input event (wheel, touch, etc.)
* @returns Modified delta to be used for scrolling
*/
transformDelta(delta: Data2d, evt: Event): Data2d;Built-in plugin providing overscroll effects with bounce and glow animations.
class OverscrollPlugin extends ScrollbarPlugin {
static pluginName: "overscroll";
static defaultOptions: {
effect: "bounce";
damping: 0.2;
maxOverscroll: 150;
glowColor: "#87ceeb";
onScroll: null;
};
readonly options: OverscrollOptions;
}
interface OverscrollOptions {
/** Effect type: 'bounce' or 'glow' */
effect?: OverscrollEffect;
/** Callback function called during overscroll */
onScroll?: OnScrollCallback;
/** Overscroll damping factor (0-1) */
damping: number;
/** Maximum overscroll distance in pixels */
maxOverscroll: number;
/** Color for glow effect */
glowColor: string;
}
enum OverscrollEffect {
BOUNCE = "bounce",
GLOW = "glow"
}
interface OnScrollCallback {
(this: OverscrollPlugin, position: Data2d): void;
}
type Position = Data2d;Usage Examples:
import Scrollbar from "smooth-scrollbar";
import OverscrollPlugin from "smooth-scrollbar/plugins/overscroll";
// Register the plugin
Scrollbar.use(OverscrollPlugin);
// Use with bounce effect
const scrollbar = Scrollbar.init(container, {
plugins: {
overscroll: {
effect: "bounce",
damping: 0.2,
maxOverscroll: 150
}
}
});
// Use with glow effect
const scrollbar2 = Scrollbar.init(container2, {
plugins: {
overscroll: {
effect: "glow",
glowColor: "#ff6b6b",
maxOverscroll: 100,
onScroll(position) {
console.log(`Overscroll: x=${position.x}, y=${position.y}`);
}
}
}
});Template for creating custom plugins with proper lifecycle implementation.
import { ScrollbarPlugin } from "smooth-scrollbar";
class CustomPlugin extends ScrollbarPlugin {
static pluginName = "custom-plugin";
static defaultOptions = {
customOption: true,
threshold: 100
};
onInit() {
// Setup plugin resources
console.log("Custom plugin initialized");
}
onDestroy() {
// Clean up plugin resources
console.log("Custom plugin destroyed");
}
onUpdate() {
// Handle scrollbar updates
this.adjustToNewSize();
}
onRender(remainMomentum) {
// Custom rendering logic
if (Math.abs(remainMomentum.y) > this.options.threshold) {
this.triggerCustomEffect();
}
}
transformDelta(delta, fromEvent) {
// Modify scroll behavior
if (fromEvent.type === "wheel") {
return {
x: delta.x * 0.8, // Reduce horizontal scroll speed
y: delta.y * 1.2 // Increase vertical scroll speed
};
}
return delta;
}
private adjustToNewSize() {
// Custom logic for size changes
}
private triggerCustomEffect() {
// Custom effect implementation
}
}
// Register and use the plugin
Scrollbar.use(CustomPlugin);
const scrollbar = Scrollbar.init(container, {
plugins: {
"custom-plugin": {
customOption: false,
threshold: 50
}
}
});Example of a more complex plugin that adds momentum-based effects.
import { ScrollbarPlugin } from "smooth-scrollbar";
class MomentumEffectsPlugin extends ScrollbarPlugin {
static pluginName = "momentum-effects";
static defaultOptions = {
fadeOnScroll: true,
blurOnScroll: false,
scaleOnScroll: false,
maxEffect: 1.0
};
private lastMomentum = { x: 0, y: 0 };
onInit() {
// Initialize effect elements if needed
this.setupEffectElements();
}
onRender(remainMomentum) {
const { fadeOnScroll, blurOnScroll, scaleOnScroll, maxEffect } = this.options;
const intensity = Math.min(
Math.sqrt(remainMomentum.x ** 2 + remainMomentum.y ** 2) / 100,
maxEffect
);
if (fadeOnScroll) {
this.scrollbar.contentEl.style.opacity = (1 - intensity * 0.3).toString();
}
if (blurOnScroll) {
this.scrollbar.contentEl.style.filter = `blur(${intensity * 2}px)`;
}
if (scaleOnScroll) {
const scale = 1 - intensity * 0.05;
this.scrollbar.contentEl.style.transform = `scale(${scale})`;
}
this.lastMomentum = remainMomentum;
}
transformDelta(delta, fromEvent) {
// Add momentum-based resistance
const resistance = this.calculateResistance();
return {
x: delta.x * resistance,
y: delta.y * resistance
};
}
private setupEffectElements() {
// Setup any DOM elements needed for effects
}
private calculateResistance() {
// Calculate scroll resistance based on current momentum
const momentum = Math.abs(this.lastMomentum.x) + Math.abs(this.lastMomentum.y);
return Math.max(0.3, 1 - momentum / 1000);
}
}/**
* Global plugin management functions
*/
/** Register plugins globally for all scrollbar instances */
function addPlugins(...Plugins: (typeof ScrollbarPlugin)[]): void;
/** Initialize plugins for a specific scrollbar instance */
function initPlugins(scrollbar: Scrollbar, options: any): ScrollbarPlugin[];
/** Global plugin registry */
interface PluginMap {
order: Set<string>;
constructors: {
[name: string]: typeof ScrollbarPlugin;
};
}Install with Tessl CLI
npx tessl i tessl/npm-smooth-scrollbar