Customize scrollbar in modern browsers with smooth scrolling experience.
—
Advanced momentum control for natural scrolling physics with customizable easing and momentum manipulation. Provides fine-grained control over scroll momentum for creating smooth, physics-based scrolling experiences.
Methods for directly manipulating scroll momentum during scrolling operations.
/**
* Adds momentum to current scroll velocity
* @param x - Horizontal momentum to add
* @param y - Vertical momentum to add
*/
addMomentum(x: number, y: number): void;
/**
* Sets scroll momentum directly, replacing current momentum
* @param x - Horizontal momentum value
* @param y - Vertical momentum value
*/
setMomentum(x: number, y: number): void;Usage Examples:
// Add momentum for smooth continuation
scrollbar.addMomentum(0, 200); // Add downward momentum
// Stop all momentum immediately
scrollbar.setMomentum(0, 0);
// Set specific momentum for custom scrolling effects
scrollbar.setMomentum(50, -100); // Right and up momentumAdvanced momentum control that allows event-based momentum transformation with callback support.
/**
* Adds momentum that can be transformed by plugins, with callback notification
* @param x - Horizontal momentum to add
* @param y - Vertical momentum to add
* @param fromEvent - Source event that generated the momentum
* @param callback - Optional callback function called with scroll decision
*/
addTransformableMomentum(
x: number,
y: number,
fromEvent: Event,
callback?: AddTransformableMomentumCallback
): void;Usage Examples:
// Add momentum from custom gesture
const customGestureEvent = new CustomEvent("swipe");
scrollbar.addTransformableMomentum(0, 300, customGestureEvent, function(willScroll) {
if (willScroll) {
console.log("Scrolling will occur");
// Add visual feedback
this.containerEl.classList.add("scrolling");
} else {
console.log("Scroll was prevented by plugin");
}
});
// Custom momentum with mouse wheel simulation
const wheelEvent = new WheelEvent("wheel", { deltaY: 120 });
scrollbar.addTransformableMomentum(0, 120, wheelEvent, function(willScroll) {
// Track scroll analytics
analytics.track("custom_scroll", { willScroll, momentum: 120 });
});Configuration options that affect momentum behavior and physics calculations.
interface ScrollbarOptions {
/**
* Momentum reduction damping factor, float between (0, 1)
* Lower values = more smooth scrolling (more frames)
* Higher values = quicker momentum decay
*/
damping: number;
/**
* Render every frame in integer pixel values
* Improves performance by avoiding sub-pixel rendering
*/
renderByPixels: boolean;
/**
* Allow outer scrollbars to continue scrolling when this reaches edge
* Affects momentum transfer to parent scrollable elements
*/
continuousScrolling: boolean;
}Usage Examples:
// Ultra-smooth momentum (more CPU intensive)
const smoothScrollbar = Scrollbar.init(container, {
damping: 0.05, // Very low damping for long momentum
renderByPixels: false // Sub-pixel rendering for smoothness
});
// Performance-optimized momentum
const fastScrollbar = Scrollbar.init(container, {
damping: 0.2, // Higher damping for quicker stops
renderByPixels: true, // Integer pixels for performance
continuousScrolling: false // Contain momentum within this scrollbar
});Creating custom momentum behavior through easing functions and momentum manipulation.
// Custom elastic momentum effect
function addElasticMomentum(scrollbar, targetY, elasticity = 0.8) {
const currentY = scrollbar.scrollTop;
const distance = targetY - currentY;
// Apply elastic easing
const momentum = distance * elasticity;
scrollbar.addTransformableMomentum(0, momentum, new Event("custom"), function(willScroll) {
if (willScroll && Math.abs(distance) > 1) {
// Continue elastic animation
setTimeout(() => {
addElasticMomentum(scrollbar, targetY, elasticity * 0.9);
}, 16);
}
});
}
// Usage
addElasticMomentum(scrollbar, 500);Implementing swipe gestures and momentum-based interactions.
class SwipeGestureHandler {
private startY = 0;
private lastY = 0;
private velocity = 0;
private lastTime = 0;
constructor(private scrollbar: Scrollbar) {
this.setupGestureListeners();
}
private setupGestureListeners() {
const container = this.scrollbar.containerEl;
container.addEventListener("touchstart", (e) => {
this.startY = e.touches[0].clientY;
this.lastY = this.startY;
this.velocity = 0;
this.lastTime = Date.now();
});
container.addEventListener("touchmove", (e) => {
const currentY = e.touches[0].clientY;
const currentTime = Date.now();
const deltaTime = currentTime - this.lastTime;
if (deltaTime > 0) {
this.velocity = (currentY - this.lastY) / deltaTime;
}
this.lastY = currentY;
this.lastTime = currentTime;
});
container.addEventListener("touchend", (e) => {
// Convert velocity to momentum
const momentum = this.velocity * 300; // Scale factor
this.scrollbar.addTransformableMomentum(0, -momentum, e, function(willScroll) {
if (willScroll) {
// Add momentum-based visual feedback
this.containerEl.style.transition = "transform 0.1s ease-out";
this.containerEl.style.transform = `translateY(${momentum > 0 ? -2 : 2}px)`;
setTimeout(() => {
this.containerEl.style.transform = "";
this.containerEl.style.transition = "";
}, 100);
}
});
});
}
}
// Usage
const gestureHandler = new SwipeGestureHandler(scrollbar);Tracking and analyzing momentum for performance optimization and user experience.
class MomentumMonitor {
private momentumHistory: Array<{time: number, momentum: Data2d}> = [];
constructor(private scrollbar: Scrollbar) {
this.setupMonitoring();
}
private setupMonitoring() {
// Override addMomentum to track all momentum changes
const originalAddMomentum = this.scrollbar.addMomentum.bind(this.scrollbar);
this.scrollbar.addMomentum = (x: number, y: number) => {
this.recordMomentum(x, y);
return originalAddMomentum(x, y);
};
}
private recordMomentum(x: number, y: number) {
this.momentumHistory.push({
time: Date.now(),
momentum: { x, y }
});
// Keep only recent history
if (this.momentumHistory.length > 100) {
this.momentumHistory.shift();
}
}
getMomentumStats() {
if (this.momentumHistory.length === 0) return null;
const totalMagnitude = this.momentumHistory.reduce((sum, entry) => {
return sum + Math.sqrt(entry.momentum.x ** 2 + entry.momentum.y ** 2);
}, 0);
return {
averageMagnitude: totalMagnitude / this.momentumHistory.length,
peakMomentum: this.momentumHistory.reduce((max, entry) => {
const magnitude = Math.sqrt(entry.momentum.x ** 2 + entry.momentum.y ** 2);
return Math.max(max, magnitude);
}, 0),
momentumEvents: this.momentumHistory.length
};
}
}
// Usage
const monitor = new MomentumMonitor(scrollbar);
// Later, get performance insights
setInterval(() => {
const stats = monitor.getMomentumStats();
console.log("Momentum stats:", stats);
}, 5000);interface AddTransformableMomentumCallback {
/**
* Callback function called after momentum transformation
* @param willScroll - Whether scrolling will actually occur after transformation
*/
(this: Scrollbar, willScroll: boolean): void;
}
interface Data2d {
/** Horizontal component */
x: number;
/** Vertical component */
y: number;
}Install with Tessl CLI
npx tessl i tessl/npm-smooth-scrollbar