UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

1 lines 17.4 kB
{"version":3,"file":"RenderScheduler.cjs","sources":["../../../src/managers/RenderScheduler.ts"],"sourcesContent":["/**\n * @file RenderScheduler.ts\n * @description Provides centralized render scheduling and batching for optimized UI updates.\n * Coordinates the timing of visual effects to minimize rendering overhead and improve performance.\n */\n\nimport { UpdateType, getPriorityForUpdateType, createUpdateId } from './UpdateTypes';\nimport { FrameThrottler, ThrottleStrategy, type ThrottlerConfig } from './FrameThrottler';\n\n/**\n * Priority levels for rendering updates.\n * Higher numbers indicate higher priority.\n * @enum {number}\n */\nexport enum UpdatePriority {\n /** Background, non-visual updates */\n LOW = 0,\n /** Standard visual updates */\n NORMAL = 1,\n /** Important visual feedback */\n HIGH = 2,\n /** Must-run-immediately updates */\n CRITICAL = 3\n}\n\n/**\n * Interface for update tasks managed by the scheduler.\n * @interface\n */\nexport interface UpdateTask {\n /** Unique identifier for the task */\n id: string;\n /** Function to execute when the task runs */\n callback: () => void;\n /** Priority level determining execution order */\n priority: UpdatePriority;\n /** Timestamp when the task was added to the queue */\n timestamp: number;\n}\n\n/**\n * Coordinates and batches render updates for KineticSlider.\n * This helps reduce unnecessary render cycles by grouping related updates\n * and executing them at the optimal time.\n *\n * @example\n * ```typescript\n * // Get the scheduler instance\n * const scheduler = RenderScheduler.getInstance();\n *\n * // Schedule a normal priority update\n * scheduler.scheduleUpdate('my-component-update', () => {\n * // Update logic here\n * });\n *\n * // Schedule a high priority update with a specific update type\n * scheduler.scheduleTypedUpdate('my-component', UpdateType.MOUSE_RESPONSE, () => {\n * // Mouse response update logic\n * });\n * ```\n */\nexport class RenderScheduler {\n /** Singleton instance of the scheduler */\n private static instance: RenderScheduler;\n\n /** Map of task IDs to pending update tasks */\n private updateQueue: Map<string, UpdateTask> = new Map();\n\n /** Whether the processing loop is active */\n private isProcessing: boolean = false;\n\n /** Current requestAnimationFrame ID or null if not active */\n private rafId: number | null = null;\n\n /** Timestamp of the last frame execution */\n private lastFrameTime: number = 0;\n\n /** Minimum time between frames in milliseconds (~60fps default) */\n private frameThrottle: number = 16;\n\n /** Frame throttler for advanced timing control */\n private frameThrottler: FrameThrottler;\n\n /**\n * Get the singleton instance of the RenderScheduler.\n * @returns {RenderScheduler} The singleton instance\n */\n public static getInstance(): RenderScheduler {\n if (!RenderScheduler.instance) {\n RenderScheduler.instance = new RenderScheduler();\n }\n return RenderScheduler.instance;\n }\n\n /**\n * Private constructor for singleton pattern.\n * @private\n */\n private constructor() {\n // Initialize\n this.lastFrameTime = performance.now();\n // Initialize frame throttler with default settings\n this.frameThrottler = new FrameThrottler({\n targetFps: 60,\n strategy: ThrottleStrategy.PRIORITY,\n enableMonitoring: true\n });\n }\n\n /**\n * Schedule a task for execution.\n * If a task with the same ID already exists, it will be replaced.\n *\n * @param {string} id - Unique identifier for the task\n * @param {() => void} callback - Function to execute\n * @param {UpdatePriority} [priority=UpdatePriority.NORMAL] - Priority level\n * @returns {RenderScheduler} The scheduler instance for chaining\n *\n * @example\n * ```typescript\n * scheduler.scheduleUpdate('update-text', () => {\n * element.textContent = 'Updated text';\n * }, UpdatePriority.NORMAL);\n * ```\n */\n public scheduleUpdate(\n id: string,\n callback: () => void,\n priority: UpdatePriority = UpdatePriority.NORMAL\n ): RenderScheduler {\n this.updateQueue.set(id, {\n id,\n callback,\n priority,\n timestamp: performance.now()\n });\n\n // Start processing if not already running\n this.startProcessing();\n\n return this;\n }\n\n /**\n * Schedule an update using a standard update type.\n * This provides a more semantic API for scheduling updates.\n *\n * @param {string} componentId - ID of the component requesting the update\n * @param {UpdateType} updateType - Type of update\n * @param {() => void} callback - Function to execute\n * @param {string} [suffix] - Optional suffix for the update ID\n * @returns {RenderScheduler} The scheduler instance for chaining\n *\n * @example\n * ```typescript\n * scheduler.scheduleTypedUpdate(\n * 'slider',\n * UpdateType.SLIDE_TRANSFORM,\n * () => updateSlidePosition()\n * );\n * ```\n */\n public scheduleTypedUpdate(\n componentId: string,\n updateType: UpdateType,\n callback: () => void,\n suffix?: string\n ): RenderScheduler {\n const id = createUpdateId(componentId, updateType, suffix);\n const priority = getPriorityForUpdateType(updateType);\n\n return this.scheduleUpdate(id, callback, priority);\n }\n\n /**\n * Cancel a scheduled update.\n *\n * @param {string} id - ID of the task to cancel\n * @returns {boolean} True if a task was found and removed\n *\n * @example\n * ```typescript\n * const wasRemoved = scheduler.cancelUpdate('update-text');\n * console.log(`Update was ${wasRemoved ? 'successfully' : 'not'} canceled`);\n * ```\n */\n public cancelUpdate(id: string): boolean {\n return this.updateQueue.delete(id);\n }\n\n /**\n * Cancel an update that was scheduled with a standard update type.\n *\n * @param {string} componentId - ID of the component that scheduled the update\n * @param {UpdateType} updateType - Type of update to cancel\n * @param {string} [suffix] - Optional suffix from the update ID\n * @returns {boolean} True if a task was found and removed\n *\n * @example\n * ```typescript\n * scheduler.cancelTypedUpdate('slider', UpdateType.SLIDE_TRANSFORM);\n * ```\n */\n public cancelTypedUpdate(\n componentId: string,\n updateType: UpdateType,\n suffix?: string\n ): boolean {\n const id = createUpdateId(componentId, updateType, suffix);\n return this.cancelUpdate(id);\n }\n\n /**\n * Immediately execute a task with CRITICAL priority.\n * Bypasses the queue but still respects frame throttling.\n *\n * @param {() => void} callback - Function to execute immediately\n *\n * @example\n * ```typescript\n * scheduler.executeImmediate(() => {\n * // Handle urgent user input\n * processCriticalUserAction();\n * });\n * ```\n */\n public executeImmediate(callback: () => void): void {\n // For truly immediate execution, add to queue with CRITICAL priority\n const id = `immediate-${performance.now()}`;\n this.scheduleUpdate(id, callback, UpdatePriority.CRITICAL);\n\n // Force processing on next available frame\n this.processQueue();\n }\n\n /**\n * Start the processing loop if not already running.\n * @private\n */\n private startProcessing(): void {\n if (this.isProcessing || this.updateQueue.size === 0) return;\n\n this.isProcessing = true;\n this.processQueue();\n }\n\n /**\n * Process the queue using requestAnimationFrame for timing.\n * @private\n */\n private processQueue(): void {\n // Cancel any existing frame request\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Schedule next frame\n this.rafId = requestAnimationFrame(() => {\n const now = performance.now();\n\n // Get highest priority in the queue\n let highestPriority = 0;\n this.updateQueue.forEach(task => {\n if (task.priority > highestPriority) {\n highestPriority = task.priority;\n }\n });\n\n // Check if we should process this frame based on throttling settings\n if (this.frameThrottler.shouldProcessFrame(highestPriority)) {\n this.executeQueuedTasks();\n this.frameThrottler.frameProcessed();\n this.lastFrameTime = now;\n }\n\n // Continue processing if queue is not empty\n if (this.updateQueue.size > 0) {\n this.processQueue();\n } else {\n this.isProcessing = false;\n this.rafId = null;\n }\n });\n }\n\n /**\n * Execute all queued tasks in priority order.\n * @private\n */\n private executeQueuedTasks(): void {\n if (this.updateQueue.size === 0) return;\n\n // Get all tasks and sort by priority (highest first) and then by timestamp\n const tasks = Array.from(this.updateQueue.values()).sort((a, b) => {\n if (a.priority !== b.priority) {\n return b.priority - a.priority; // Higher priority first\n }\n return a.timestamp - b.timestamp; // Older tasks first within same priority\n });\n\n // Clear the queue before executing to avoid potential infinite loops\n this.updateQueue.clear();\n\n // Execute all tasks\n tasks.forEach(task => {\n try {\n task.callback();\n } catch (error) {\n console.error(`Error executing task ${task.id}:`, error);\n }\n });\n }\n\n /**\n * Set the frame throttle rate.\n *\n * @param {number} milliseconds - Minimum time between frames\n *\n * @example\n * ```typescript\n * // Set to 30fps (33.33ms between frames)\n * scheduler.setFrameThrottle(33.33);\n * ```\n */\n public setFrameThrottle(milliseconds: number): void {\n this.frameThrottle = Math.max(0, milliseconds);\n }\n\n /**\n * Get current queue size for debugging.\n *\n * @returns {number} Number of tasks currently in the queue\n *\n * @example\n * ```typescript\n * const queueSize = scheduler.getQueueSize();\n * console.log(`Current queue size: ${queueSize}`);\n * ```\n */\n public getQueueSize(): number {\n return this.updateQueue.size;\n }\n\n /**\n * Clear all pending updates.\n *\n * @example\n * ```typescript\n * // Cancel all pending updates (e.g., when component unmounts)\n * scheduler.clearQueue();\n * ```\n */\n public clearQueue(): void {\n this.updateQueue.clear();\n }\n\n /**\n * Configure frame throttling behavior.\n *\n * @param {ThrottlerConfig} config - Throttling configuration options\n *\n * @example\n * ```typescript\n * scheduler.configureThrottling({\n * targetFps: 30,\n * strategy: ThrottleStrategy.ADAPTIVE\n * });\n * ```\n */\n public configureThrottling(config: Partial<ThrottlerConfig>): void {\n if (config.strategy !== undefined) {\n this.frameThrottler.setStrategy(config.strategy);\n }\n\n if (config.targetFps !== undefined) {\n this.frameThrottler.setTargetFps(config.targetFps);\n }\n }\n\n /**\n * Get current performance metrics.\n *\n * @returns {Object} Performance metrics including queue size and frame rate\n *\n * @example\n * ```typescript\n * const metrics = scheduler.getPerformanceMetrics();\n * console.log(`Current FPS: ${metrics.currentFps}`);\n * ```\n */\n public getPerformanceMetrics(): object {\n return {\n queueSize: this.getQueueSize(),\n ...this.frameThrottler.getPerformanceMetrics()\n };\n }\n}\n\nexport default RenderScheduler;"],"names":["FrameThrottler","ThrottleStrategy","createUpdateId","getPriorityForUpdateType"],"mappings":"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"}