From dfd61ce74430597ea11215fb555c5ce78cfbff4a Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Fri, 31 Jan 2025 23:10:33 +0100 Subject: [PATCH] feat(scroller): Enhance Scroller class with callback execution and adaptive scroll listener --- changelog.md | 7 ++ ts/00_commitinfo_data.ts | 2 +- ts/domtools.classes.scroller.ts | 112 ++++++++++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index e9194e5..1ff95ac 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 646883b..0836e12 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -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.' } diff --git a/ts/domtools.classes.scroller.ts b/ts/domtools.classes.scroller.ts index 4d86ab2..3648f2c 100644 --- a/ts/domtools.classes.scroller.ts +++ b/ts/domtools.classes.scroller.ts @@ -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[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(); 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); + } + } +} \ No newline at end of file