UNPKG

vevet

Version:

Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.

133 lines (101 loc) 2.94 kB
import { addEventListener, normalizeWheel } from '@/utils'; import { Snap } from '..'; export class SnapWheel { /** Listeners to destruct */ protected _destructor: () => void; /** Detects if wheel event is started */ protected _hasStarted = false; /** Debounce wheel end event */ protected _debounceEnd?: NodeJS.Timeout; /** Accummulated wheel value for `followWheel=false` */ protected _accum = 0; constructor(protected _snap: Snap) { _snap.on('destroy', () => this._destroy(), { protected: true }); this._destructor = addEventListener(_snap.container, 'wheel', (event) => this._handleWheel(event), ); } /** Snap component */ protected get snap() { return this._snap; } /** * Handles wheel events */ protected _handleWheel(event: WheelEvent) { const { snap } = this; if (!snap.props.wheel) { return; } event.preventDefault(); const { wheelAxis } = snap.props; // Start callback if (!this._hasStarted) { this._hasStarted = true; snap.callbacks.emit('wheelStart', undefined); } // Move callback snap.callbacks.emit('wheel', event); // Normalize wheel data const axis = wheelAxis === 'auto' ? snap.axis : wheelAxis; const wheelData = normalizeWheel(event); const wheelDelta = axis === 'x' ? wheelData.pixelX : wheelData.pixelY; const delta = wheelDelta * snap.props.wheelSpeed; // Update wheel target if (snap.props.followWheel) { this._handleFollow(delta); } else { this._handleNotFollow(delta); } // Debounce End if (this._debounceEnd) { clearTimeout(this._debounceEnd); } // End callback this._debounceEnd = setTimeout(() => this._handleEnd(), 100); } /** Handle `followWheel=true` */ protected _handleFollow(delta: number) { const { snap } = this; // Cancel snap transition snap.cancelTransition(); // Update track target snap.track.iterateTarget(delta); snap.track.clampTarget(); } /** Handle `followWheel=false` */ protected _handleNotFollow(delta: number) { if (this.snap.isTransitioning || Math.abs(delta) < 10) { return; } this._accum += Math.abs(delta) / 2; const direction = Math.sign(delta); if (Math.abs(this._accum) < 100) { return; } if (direction === 1) { this.snap.next(); } else { this.snap.prev(); } this._accum = 0; } /** Handle wheel end */ protected _handleEnd() { const { snap } = this; const { freemode: isFreemode, followWheel: isFollow } = snap.props; this._hasStarted = false; this._accum = 0; if (!isFreemode && isFollow) { snap.stick(); } snap.callbacks.emit('wheelEnd', undefined); } /** Destroy wheel listeners */ protected _destroy() { this._destructor(); if (this._debounceEnd) { clearTimeout(this._debounceEnd); } } }