UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

299 lines (297 loc) 10.6 kB
/** * Slide initialization state */ var SlideState; (function (SlideState) { /** Slide is not initialized at all */ SlideState["UNINITIALIZED"] = "uninitialized"; /** Slide has a lightweight placeholder */ SlideState["PLACEHOLDER"] = "placeholder"; /** Slide is fully loaded and ready for display */ SlideState["LOADED"] = "loaded"; /** Slide is currently visible */ SlideState["ACTIVE"] = "active"; /** Slide failed to load */ SlideState["ERROR"] = "error"; })(SlideState || (SlideState = {})); /** * Manages a sliding window of initialized slides to optimize memory usage * and improve performance by only fully loading slides that are visible * or likely to become visible soon. */ class SlidingWindowManager { /** Current active slide index */ activeIndex; /** Size of the window on each side of the active slide */ windowSize; /** Total number of slides */ totalSlides; /** Information about each slide */ slides; /** Whether debug logging is enabled */ debug; /** Resource manager for tracking resources */ resourceManager; /** Direction of the last navigation (-1 for prev, 1 for next, 0 for initial) */ lastDirection = 0; /** * Creates a new SlidingWindowManager * * @param options - Configuration options * @param resourceManager - Optional ResourceManager for resource tracking */ constructor(options, resourceManager) { this.windowSize = options.windowSize ?? 2; this.totalSlides = options.totalSlides; this.activeIndex = options.initialIndex ?? 0; this.debug = options.debug ?? false; this.resourceManager = resourceManager; // Initialize slide info array this.slides = Array(this.totalSlides).fill(null).map((_, index) => ({ index, state: SlideState.UNINITIALIZED, inWindow: this.isInWindow(index, this.activeIndex) })); this.log(`Initialized with ${this.totalSlides} slides, window size ±${this.windowSize}, active index ${this.activeIndex}`); this.log(`Initial window: ${this.getWindowIndices().join(', ')}`); } /** * Log a message if debug is enabled */ log(message, level = 'info') { if (!this.debug) return; const prefix = `[SlidingWindowManager]`; switch (level) { case 'info': console.log(`${prefix} ${message}`); break; case 'warn': console.warn(`${prefix} ${message}`); break; case 'error': console.error(`${prefix} ${message}`); break; } } /** * Check if a slide index is within the current window * * @param index - Slide index to check * @param centerIndex - Center of the window (usually activeIndex) * @returns Whether the index is within the window */ isInWindow(index, centerIndex) { const distance = Math.abs(index - centerIndex); return distance <= this.windowSize; } /** * Get all slide indices that should be in the current window * * @returns Array of slide indices in the window */ getWindowIndices() { const indices = []; // Include the active index and windowSize slides on each side for (let i = Math.max(0, this.activeIndex - this.windowSize); i <= Math.min(this.totalSlides - 1, this.activeIndex + this.windowSize); i++) { indices.push(i); } return indices; } /** * Get all slide indices that should be in the extended window * (includes prediction based on navigation direction) * * @returns Array of slide indices in the extended window */ getExtendedWindowIndices() { const indices = this.getWindowIndices(); // Add extra slides in the direction of navigation if (this.lastDirection !== 0) { const extraIndex = this.lastDirection > 0 ? this.activeIndex + this.windowSize + 1 : this.activeIndex - this.windowSize - 1; if (extraIndex >= 0 && extraIndex < this.totalSlides) { indices.push(extraIndex); } } return indices; } /** * Update the active slide index and recalculate the window * * @param newIndex - New active slide index * @returns Object containing arrays of indices that entered and left the window */ updateActiveIndex(newIndex) { if (newIndex < 0 || newIndex >= this.totalSlides) { this.log(`Invalid index: ${newIndex}`, 'error'); return { entered: [], left: [] }; } // Calculate direction of navigation this.lastDirection = newIndex > this.activeIndex ? 1 : -1; const oldWindowIndices = this.getWindowIndices(); const oldActiveIndex = this.activeIndex; // Update active index this.activeIndex = newIndex; // Update active slide info if (this.slides[newIndex]) { this.slides[newIndex].state = SlideState.ACTIVE; this.slides[newIndex].lastActiveTime = Date.now(); } // Update previous active slide if (this.slides[oldActiveIndex] && oldActiveIndex !== newIndex) { if (this.slides[oldActiveIndex].state === SlideState.ACTIVE) { this.slides[oldActiveIndex].state = SlideState.LOADED; } } // Get new window indices const newWindowIndices = this.getWindowIndices(); // Calculate which indices entered and left the window const entered = newWindowIndices.filter(index => !oldWindowIndices.includes(index)); const left = oldWindowIndices.filter(index => !newWindowIndices.includes(index)); // Update inWindow flag for all slides this.slides.forEach(slide => { slide.inWindow = this.isInWindow(slide.index, this.activeIndex); }); this.log(`Updated active index to ${newIndex}, direction: ${this.lastDirection}`); this.log(`Window changed: +[${entered.join(', ')}] -[${left.join(', ')}]`); return { entered, left }; } /** * Register a sprite for a slide * * @param index - Slide index * @param sprite - Sprite instance * @param state - Current state of the slide */ registerSlide(index, sprite, state = SlideState.LOADED) { if (index < 0 || index >= this.totalSlides) { this.log(`Invalid index for registerSlide: ${index}`, 'error'); return; } this.slides[index] = { ...this.slides[index], sprite, state: index === this.activeIndex ? SlideState.ACTIVE : state, inWindow: this.isInWindow(index, this.activeIndex) }; this.log(`Registered slide ${index} with state ${state}`); } /** * Update the state of a slide * * @param index - Slide index * @param state - New state */ updateSlideState(index, state) { if (index < 0 || index >= this.totalSlides) { this.log(`Invalid index for updateSlideState: ${index}`, 'error'); return; } this.slides[index].state = state; this.log(`Updated slide ${index} state to ${state}`); } /** * Get information about a slide * * @param index - Slide index * @returns Slide information or null if index is invalid */ getSlideInfo(index) { if (index < 0 || index >= this.totalSlides) { this.log(`Invalid index for getSlideInfo: ${index}`, 'error'); return null; } return this.slides[index]; } /** * Get the current active index * * @returns Active slide index */ getActiveIndex() { return this.activeIndex; } /** * Get all slide information * * @returns Array of slide information */ getAllSlides() { return [...this.slides]; } /** * Get slides that need to be initialized (converted from UNINITIALIZED to at least PLACEHOLDER) * * @returns Array of slide indices that need initialization */ getSlidesToInitialize() { const windowIndices = this.getExtendedWindowIndices(); return windowIndices.filter(index => this.slides[index].state === SlideState.UNINITIALIZED); } /** * Get slides that need to be fully loaded (converted from PLACEHOLDER to LOADED) * * @returns Array of slide indices that need to be fully loaded */ getSlidesToLoad() { const windowIndices = this.getWindowIndices(); return windowIndices.filter(index => this.slides[index].state === SlideState.PLACEHOLDER); } /** * Get slides that can be unloaded (converted from LOADED to PLACEHOLDER) * * @returns Array of slide indices that can be unloaded */ getSlidesToUnload() { return this.slides .filter(slide => !slide.inWindow && (slide.state === SlideState.LOADED || slide.state === SlideState.ACTIVE)) .map(slide => slide.index); } /** * Set the window size * * @param size - New window size */ setWindowSize(size) { if (size < 1) { this.log(`Invalid window size: ${size}, must be at least 1`, 'error'); return; } this.windowSize = size; // Recalculate which slides are in the window this.slides.forEach(slide => { slide.inWindow = this.isInWindow(slide.index, this.activeIndex); }); this.log(`Window size updated to ±${size}`); } /** * Get the current window size * * @returns Current window size */ getWindowSize() { return this.windowSize; } /** * Get the last navigation direction * * @returns Last direction (-1 for prev, 1 for next, 0 for initial) */ getLastDirection() { return this.lastDirection; } /** * Alias for updateActiveIndex to maintain naming consistency * * @param newIndex - New current slide index * @returns Object containing arrays of indices that entered and left the window */ updateCurrentIndex(newIndex) { return this.updateActiveIndex(newIndex); } } export { SlideState, SlidingWindowManager as default }; //# sourceMappingURL=SlidingWindowManager.js.map