feat(scroller): Enhance Scroller class with callback execution and adaptive scroll listener

This commit is contained in:
Philipp Kunz 2025-01-31 23:10:33 +01:00
parent 3093ccd4f6
commit dfd61ce744
3 changed files with 113 additions and 8 deletions

View File

@ -1,5 +1,12 @@
# Changelog
## 2025-01-31 - 2.3.0 - feat(scroller)
Enhance Scroller class with callback execution and adaptive scroll listener
- Added support for executing scroll callbacks in the Scroller class.
- Integrated adaptive scrolling mechanism using Lenis, with native smooth scrolling detection.
- Ensured seamless switching between native scroll listener and Lenis scroll listener.
## 2025-01-31 - 2.2.0 - feat(core)
Enhance scrolling capabilities by integrating Scroller class and adding lenis support

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-domtools',
version: '2.2.0',
version: '2.3.0',
description: 'A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.'
}

View File

@ -4,16 +4,32 @@ import * as plugins from './domtools.plugins.js';
export class Scroller {
public domtoolsInstance: DomTools;
constructor(
domtoolsInstanceArg: DomTools,
) {
// Array to store scroll callback functions.
private scrollCallbacks: Array<() => void> = [];
// Lenis instance (if activated) or null.
private lenisInstance: plugins.Lenis | null = null;
// Bound handlers to allow removal from event listeners.
private handleNativeScroll = (event: Event): void => {
this.executeScrollCallbacks();
};
private handleLenisScroll = (info: any): void => {
this.executeScrollCallbacks();
};
constructor(domtoolsInstanceArg: DomTools) {
this.domtoolsInstance = domtoolsInstanceArg;
// Attach the native scroll listener by default.
this.attachNativeScrollListener();
}
private sweetScroller = new plugins.SweetScroll({
/* some options */
}); // TODO: switch to scroller class
private sweetScroller = new plugins.SweetScroll({});
/**
* Scrolls to a given element with options.
*/
public async scrollToElement(
elementArg: HTMLElement,
optionsArg: Parameters<typeof this.sweetScroller.toElement>[1]
@ -22,6 +38,9 @@ export class Scroller {
await plugins.smartdelay.delayFor(optionsArg.duration);
}
/**
* Detects whether native smooth scrolling is enabled.
*/
public async detectNativeSmoothScroll() {
const done = plugins.smartpromise.defer<boolean>();
const sampleSize = 100;
@ -64,14 +83,93 @@ export class Scroller {
return done.promise;
}
/**
* Enables Lenis scrolling.
* If optionsArg.disableOnNativeSmoothScroll is true and native smooth scrolling is detected,
* Lenis will be destroyed immediately.
*/
public async enableLenisScroll(optionsArg?: { disableOnNativeSmoothScroll?: boolean }) {
const lenis = new plugins.Lenis({
autoRaf: true,
});
if (optionsArg?.disableOnNativeSmoothScroll) {
if (await this.detectNativeSmoothScroll()) {
lenis.destroy();
return;
}
}
// Activate Lenis scrolling.
this.lenisInstance = lenis;
// Switch from native scroll listener to Lenis scroll listener.
this.detachNativeScrollListener();
this.attachLenisScrollListener();
// Monkey-patch the destroy method so that when Lenis is destroyed,
// the native scroll listener is reattached.
const originalDestroy = lenis.destroy.bind(lenis);
lenis.destroy = () => {
originalDestroy();
this.detachLenisScrollListener();
this.attachNativeScrollListener();
this.lenisInstance = null;
};
}
}
/**
* Registers a callback to be executed on scroll.
* @param callback A function to execute on each scroll event.
*/
public onScroll(callback: () => void): void {
this.scrollCallbacks.push(callback);
}
/**
* Executes all registered scroll callbacks concurrently.
*/
private executeScrollCallbacks(): void {
// Execute all callbacks in parallel.
this.scrollCallbacks.forEach((callback) => {
try {
callback();
} catch (error) {
console.error('Error in scroll callback:', error);
}
});
}
/**
* Attaches the native scroll event listener.
*/
private attachNativeScrollListener(): void {
window.addEventListener('scroll', this.handleNativeScroll, { passive: true });
}
/**
* Detaches the native scroll event listener.
*/
private detachNativeScrollListener(): void {
window.removeEventListener('scroll', this.handleNativeScroll);
}
/**
* Attaches the Lenis scroll event listener.
*/
private attachLenisScrollListener(): void {
if (this.lenisInstance) {
// Assuming that Lenis exposes an `on` method to listen to scroll events.
this.lenisInstance.on('scroll', this.handleLenisScroll);
}
}
/**
* Detaches the Lenis scroll event listener.
*/
private detachLenisScrollListener(): void {
if (this.lenisInstance) {
// Assuming that Lenis exposes an `off` method to remove scroll event listeners.
this.lenisInstance.off('scroll', this.handleLenisScroll);
}
}
}