@motion.page/sdk 1.0.7 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -0
- package/dist/core/AnimationBuilder.d.ts +9 -1
- package/dist/core/Engine.d.ts +15 -1
- package/dist/core/Motion.d.ts +2 -0
- package/dist/core/MotionContext.d.ts +77 -0
- package/dist/core/PropTween.d.ts +8 -1
- package/dist/core/Timeline.d.ts +5 -13
- package/dist/index.cjs +18 -16
- package/dist/index.cjs.map +22 -19
- package/dist/index.d.ts +4 -1
- package/dist/index.js +18 -16
- package/dist/index.js.map +22 -19
- package/dist/registries/SDKRegistry.d.ts +17 -1
- package/dist/render/CSSRenderer.d.ts +10 -0
- package/dist/triggers/EventTrigger.d.ts +1 -0
- package/dist/triggers/MarkerManager.d.ts +2 -2
- package/dist/triggers/TriggerManager.d.ts +28 -0
- package/dist/types/index.d.ts +127 -0
- package/dist/types/public.d.ts +1 -1
- package/dist/utils/ClipPathParser.d.ts +74 -0
- package/dist/utils/PropertyParser.d.ts +33 -1
- package/dist/utils/TextFlapper.d.ts +105 -0
- package/dist/utils/TextSplitter.d.ts +8 -0
- package/llms.txt +2 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1,52 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/utils/executeTimelineAction.ts", "../../src/registries/SDKRegistry.ts", "../../src/triggers/TriggerManager.ts", "../../src/utils/TargetResolver.ts", "../../src/triggers/BaseTrigger.ts", "../../src/utils/getLayoutRect.ts", "../../src/triggers/EventTrigger.ts", "../../src/render/TransformCache.ts", "../../src/render/RenderBatch.ts", "../../src/core/Ticker.ts", "../../src/utils/PathParser.ts", "../../src/utils/PropertyParser.ts", "../../src/utils/StyleReset.ts", "../../src/triggers/PinManager.ts", "../../src/utils/ScrollPositionParser.ts", "../../src/triggers/MarkerManager.ts", "../../src/triggers/ScrollTrigger.ts", "../../src/triggers/PageLoadTrigger.ts", "../../src/triggers/MouseMoveTrigger.ts", "../../src/triggers/GestureTrigger.ts", "../../src/render/CSSRenderer.ts", "../../src/render/PathRenderer.ts", "../../src/core/PropTween.ts", "../../src/memory/ObjectPool.ts", "../../src/memory/PropTweenPool.ts", "../../src/core/AnimationBuilder.ts", "../../src/utils/PositionParser.ts", "../../src/core/Timeline.ts", "../../src/utils/QuickSetter.ts", "../../src/triggers/CursorTrigger.ts", "../../src/triggers/PageExitTrigger.ts", "../../src/utils/ColorParser.ts", "../../src/utils/FilterParser.ts", "../../src/utils/DrawSVGParser.ts", "../../src/utils/TextSplitter.ts", "../../src/stagger/StaggerResolver.ts", "../../src/fit/FitResolver.ts", "../../src/easing/index.ts", "../../src/core/Animation.ts", "../../src/memory/AnimationPool.ts", "../../src/core/Engine.ts", "../../src/utils/MotionUtils.ts", "../../src/core/Motion.ts"],
|
|
3
|
+
"sources": ["../../src/utils/executeTimelineAction.ts", "../../src/registries/SDKRegistry.ts", "../../src/triggers/TriggerManager.ts", "../../src/utils/TargetResolver.ts", "../../src/triggers/BaseTrigger.ts", "../../src/utils/getLayoutRect.ts", "../../src/triggers/EventTrigger.ts", "../../src/render/TransformCache.ts", "../../src/render/RenderBatch.ts", "../../src/core/Ticker.ts", "../../src/utils/PathParser.ts", "../../src/utils/PropertyParser.ts", "../../src/utils/StyleReset.ts", "../../src/triggers/PinManager.ts", "../../src/utils/ScrollPositionParser.ts", "../../src/triggers/MarkerManager.ts", "../../src/triggers/ScrollTrigger.ts", "../../src/triggers/PageLoadTrigger.ts", "../../src/triggers/MouseMoveTrigger.ts", "../../src/triggers/GestureTrigger.ts", "../../src/render/CSSRenderer.ts", "../../src/render/PathRenderer.ts", "../../src/core/PropTween.ts", "../../src/memory/ObjectPool.ts", "../../src/memory/PropTweenPool.ts", "../../src/core/AnimationBuilder.ts", "../../src/utils/PositionParser.ts", "../../src/core/Timeline.ts", "../../src/utils/QuickSetter.ts", "../../src/triggers/CursorTrigger.ts", "../../src/triggers/PageExitTrigger.ts", "../../src/utils/ColorParser.ts", "../../src/utils/FilterParser.ts", "../../src/utils/DrawSVGParser.ts", "../../src/utils/ClipPathParser.ts", "../../src/utils/TextSplitter.ts", "../../src/utils/TextFlapper.ts", "../../src/stagger/StaggerResolver.ts", "../../src/fit/FitResolver.ts", "../../src/easing/index.ts", "../../src/core/Animation.ts", "../../src/memory/AnimationPool.ts", "../../src/core/Engine.ts", "../../src/core/MotionContext.ts", "../../src/utils/MotionUtils.ts", "../../src/core/Motion.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * Minimal interface for timeline playback control.\n * Using an interface instead of importing Timeline directly to avoid circular dependencies\n * (utils/ never imports from core/).\n */\nexport interface PlaybackTarget {\n play(): unknown;\n pause(): unknown;\n reverse(): unknown;\n restart(): unknown;\n progress(value: number): unknown;\n}\n\nexport type TimelineAction = 'play' | 'pause' | 'resume' | 'reverse' | 'restart' | 'reset' | 'complete' | 'none';\n\n/**\n * Execute a GSAP-compatible timeline action. Maps action strings to method calls.\n * Covers all 8 GSAP toggleActions keywords.\n */\nexport function executeTimelineAction(action: TimelineAction, timeline: PlaybackTarget): void {\n switch (action) {\n case 'play':\n case 'resume': // GSAP compat — resume is semantically identical to play()\n timeline.play();\n break;\n case 'pause':\n timeline.pause();\n break;\n case 'reverse':\n timeline.reverse();\n break;\n case 'restart':\n timeline.restart();\n break;\n case 'reset':\n timeline.pause();\n timeline.progress(0);\n break;\n case 'complete':\n timeline.progress(1);\n timeline.pause();\n break;\n case 'none':\n // Intentional no-op\n break;\n default: {\n const unknownAction = action as unknown as string;\n console.warn(\n `[Motion] Unknown timeline action: \"${unknownAction}\". ` +\n `Valid actions: play, pause, resume, reverse, restart, reset, complete, none`\n );\n }\n }\n}\n",
|
|
6
|
-
"/**\n * SDKRegistry - Central registry for all optional SDK modules\n *\n * Optional modules register themselves here when loaded.\n * Core modules access features through this registry with optional chaining.\n * This enables tree-shaking: unused modules aren't bundled.\n */\n\nimport type { ParsedFilterFunction } from '../utils/FilterParser';\nimport type { ParsedDrawSVG } from '../utils/DrawSVGParser';\nimport type { AnimationTarget, StaggerVars, SplitType } from '../types';\nimport type { TriggerManager } from '../triggers/TriggerManager';\n\n// --- Parser interfaces ---\n\ninterface ColorParserFunctions {\n parseColor: (color: string, element?: Element) => Float32Array | null;\n getCurrentColor: (element: Element, property: string) => Float32Array;\n}\n\ninterface FilterParserFunctions {\n parseFilter: (filter: string) => ParsedFilterFunction[] | null;\n getCurrentFilter: (element: Element) => ParsedFilterFunction[];\n mergeFilterArrays: (start: ParsedFilterFunction[], end: ParsedFilterFunction[]) => { start: ParsedFilterFunction[]; end: ParsedFilterFunction[] };\n interpolateFilters: (start: ParsedFilterFunction[], end: ParsedFilterFunction[], progress: number) => ParsedFilterFunction[];\n filterToString: (filters: ParsedFilterFunction[]) => string;\n}\n\ninterface DrawSVGParserFunctions {\n parseDrawSVG: (value: string | { start?: number; end?: number }, element?: Element) => ParsedDrawSVG | null;\n getCurrentDrawSVG: (element: Element) => ParsedDrawSVG;\n getPathLength: (element: Element) => number;\n applyDrawSVG: (element: Element, start: number, end: number, length: number) => void;\n}\n\n// --- Utility interfaces ---\n\ninterface TextSplitterFunctions {\n split: (element: Element, type: SplitType, options?: { mask?: boolean; consumer?: object }) => HTMLElement[];\n revert: (element: Element, consumer?: object) => boolean;\n isSplit: (element: Element) => boolean;\n}\n\ninterface StyleResetFunctions {\n registerAnimatedProps: (element: Element, props: string[]) => void;\n clearAnimationStylesForProps: (element: Element, props: string[]) => void;\n clearAnimationStylesAndUnregister: (element: Element) => void;\n clearPinStylesAndUnregister: (element: Element) => void;\n}\n\ninterface FitResolverFunctions {\n registerPendingSetup(\n animation: import('../core/Animation').Animation,\n element: Element,\n fitConfig: import('../types').FitConfig,\n propTweenPool: typeof import('../memory/PropTweenPool').PropTweenPool\n ): void;\n}\n\n/**\n * Unified registry for all optional SDK features.\n * Each slot is null until the corresponding module loads and self-registers.\n */\nexport const SDKRegistry = {\n // Parsers\n color: null as ColorParserFunctions | null,\n filter: null as FilterParserFunctions | null,\n drawSVG: null as DrawSVGParserFunctions | null,\n\n // Utilities\n stagger: null as { resolve: (targets: AnimationTarget[], stagger: number | StaggerVars) => number[] } | null,\n textSplitter: null as TextSplitterFunctions | null,\n triggerManager: null as { getInstance: () => TriggerManager } | null,\n styleReset: null as StyleResetFunctions | null,\n fit: null as FitResolverFunctions | null,\n};\n",
|
|
7
|
-
"/**\n * TriggerManager\n *\n * Coordinates all trigger types (pageLoad, pageExit, scroll, hover, click, mouseMove, gesture)\n * and activates timelines based on trigger conditions.\n *\n * Uses a registry pattern for trigger classes to avoid importing them directly.\n * Each trigger module registers itself via TriggerManager.registerTrigger() when loaded.\n * This enables tree-shaking: only trigger types included in the bundle are available.\n */\n\nimport type { ScrollConfig, MouseMoveConfig, GestureConfig, HoverConfig, ClickConfig, CursorConfig, PageExitConfig } from '../types';\nimport type { Timeline } from '../core/Timeline';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\n/** Config for page load trigger */\nexport interface PageLoadTriggerConfig {\n timing?: 'before' | 'during' | 'after';\n /** When true, the timeline is built but not played — use Motion('name').play() to start it manually */\n paused?: boolean;\n}\n\n/** Interface that trigger instances must satisfy for cleanup */\ninterface TriggerInstance {\n enable(): void;\n disable(): void;\n}\n\n/** Interface for PageLoadTrigger specifically */\ninterface PageLoadTriggerInstance {\n register(timeline: Timeline, config: PageLoadTriggerConfig): void;\n unregister(timeline: Timeline): void;\n clear(): void;\n _reset(): void;\n}\n\n/** Trigger class constructors stored in registry */\ntype TriggerFactory = new (...args: any[]) => TriggerInstance;\n\nexport class TriggerManager {\n private static _instance: TriggerManager;\n\n /** Registry for trigger class constructors - populated by each trigger module on load */\n private static _triggerRegistry: Map<string, TriggerFactory> = new Map();\n /** PageLoadTrigger factory - separate because it has a different interface */\n private static _pageLoadTriggerFactory: (new () => PageLoadTriggerInstance) | null = null;\n\n /** Debounce delay (ms) for the window resize → refreshScrollTriggers handler */\n private static readonly _RESIZE_DEBOUNCE_MS = 200;\n\n private _pageLoadTrigger: PageLoadTriggerInstance | null = null;\n private _scrollTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _eventTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _mouseMoveTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _gestureTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _cursorTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _pageExitTriggers: Map<Timeline, TriggerInstance> = new Map();\n\n /** Bound resize handler (created once, reused for add/removeEventListener) */\n private _resizeHandler: (() => void) | null = null;\n /** Timer id for debounced resize */\n private _resizeTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** All trigger maps — used by unregister() and killAll() for bulk iteration */\n private get _allTriggerMaps(): Map<Timeline, TriggerInstance>[] {\n return [\n this._scrollTriggers,\n this._eventTriggers,\n this._mouseMoveTriggers,\n this._gestureTriggers,\n this._cursorTriggers,\n this._pageExitTriggers,\n ];\n }\n\n private constructor() {\n // PageLoadTrigger is created lazily on first use, not eagerly in constructor\n }\n\n static getInstance(): TriggerManager {\n if (!TriggerManager._instance) {\n TriggerManager._instance = new TriggerManager();\n }\n return TriggerManager._instance;\n }\n\n /**\n * Register a trigger class. Called by each trigger module when it loads.\n * @internal\n */\n static registerTrigger(type: string, factory: TriggerFactory): void {\n TriggerManager._triggerRegistry.set(type, factory);\n }\n\n /**\n * Register the PageLoadTrigger class. Called by PageLoadTrigger module when it loads.\n * @internal\n */\n static registerPageLoadTrigger(factory: new () => PageLoadTriggerInstance): void {\n TriggerManager._pageLoadTriggerFactory = factory;\n }\n\n /** Get or create the PageLoadTrigger instance */\n private _getPageLoadTrigger(): PageLoadTriggerInstance | null {\n if (!this._pageLoadTrigger && TriggerManager._pageLoadTriggerFactory) {\n this._pageLoadTrigger = new TriggerManager._pageLoadTriggerFactory();\n }\n return this._pageLoadTrigger;\n }\n\n /** Create a trigger instance from the registry */\n private _createTrigger(type: string, ...args: any[]): TriggerInstance | null {\n const Factory = TriggerManager._triggerRegistry.get(type);\n if (!Factory) {\n const available = Array.from(TriggerManager._triggerRegistry.keys());\n console.warn(\n `[Motion SDK] Trigger type '${type}' is not available in this bundle. ` +\n `Available triggers: ${available.join(', ') || 'none'}. ` +\n `Animations using .on${type.charAt(0).toUpperCase() + type.slice(1)}() will be skipped.`\n );\n return null;\n }\n return new Factory(...args);\n }\n\n /**\n * Register a pageLoad trigger\n */\n registerPageLoad(timeline: Timeline, config: PageLoadTriggerConfig): void {\n this.unregister(timeline);\n const pageLoad = this._getPageLoadTrigger();\n if (pageLoad) {\n pageLoad.register(timeline, config);\n }\n }\n\n /**\n * Attach a debounced window resize listener that refreshes all scroll triggers.\n * Called lazily when the first scroll trigger is registered.\n * @internal\n */\n private _attachResizeListener(): void {\n if (this._resizeHandler || typeof window === 'undefined') return;\n\n this._resizeHandler = () => {\n if (this._resizeTimer !== null) clearTimeout(this._resizeTimer);\n this._resizeTimer = setTimeout(() => {\n this._resizeTimer = null;\n this.refreshScrollTriggers();\n }, TriggerManager._RESIZE_DEBOUNCE_MS);\n };\n\n window.addEventListener('resize', this._resizeHandler, { passive: true });\n }\n\n /**\n * Detach the window resize listener.\n * Called when the last scroll trigger is removed or killAll() is invoked.\n * @internal\n */\n private _detachResizeListener(): void {\n if (!this._resizeHandler || typeof window === 'undefined') return;\n\n window.removeEventListener('resize', this._resizeHandler);\n if (this._resizeTimer !== null) {\n clearTimeout(this._resizeTimer);\n this._resizeTimer = null;\n }\n this._resizeHandler = null;\n }\n\n /**\n * Register a scroll trigger\n */\n registerScroll(timeline: Timeline, config: ScrollConfig): void {\n this.unregister(timeline);\n const scrollTrigger = this._createTrigger('scroll', timeline, config);\n if (!scrollTrigger) return; // Gracefully skip if trigger module not loaded\n this._scrollTriggers.set(timeline, scrollTrigger);\n timeline._trigger = scrollTrigger;\n scrollTrigger.enable();\n\n // Lazily attach resize listener when first scroll trigger is added\n this._attachResizeListener();\n }\n\n /**\n * Register a hover trigger\n */\n registerHover(timeline: Timeline, config: HoverConfig): void {\n this.unregister(timeline);\n const eventTrigger = this._createTrigger('event', timeline, {\n type: 'hover',\n target: config.target,\n onLeave: config.onLeave,\n leaveDelay: config.leaveDelay,\n });\n if (!eventTrigger) return; // Gracefully skip if trigger module not loaded\n this._eventTriggers.set(timeline, eventTrigger);\n timeline._trigger = eventTrigger;\n eventTrigger.enable();\n }\n\n /**\n * Register a click trigger\n */\n registerClick(timeline: Timeline, config: ClickConfig): void {\n this.unregister(timeline);\n const eventTrigger = this._createTrigger('event', timeline, {\n type: 'click',\n target: config.target,\n secondTarget: config.secondTarget,\n toggle: config.toggle,\n preventDefault: config.preventDefault,\n });\n if (!eventTrigger) return; // Gracefully skip if trigger module not loaded\n this._eventTriggers.set(timeline, eventTrigger);\n timeline._trigger = eventTrigger;\n eventTrigger.enable();\n }\n\n /**\n * Register a mouseMove trigger\n */\n registerMouseMove(timeline: Timeline, config: MouseMoveConfig): void {\n this.unregister(timeline);\n const mouseMoveTrigger = this._createTrigger('mouseMove', timeline, config);\n if (!mouseMoveTrigger) return; // Gracefully skip if trigger module not loaded\n this._mouseMoveTriggers.set(timeline, mouseMoveTrigger);\n timeline._trigger = mouseMoveTrigger;\n mouseMoveTrigger.enable();\n }\n\n /**\n * Register a gesture trigger\n */\n registerGesture(timeline: Timeline, config: GestureConfig): void {\n this.unregister(timeline);\n const gestureTrigger = this._createTrigger('gesture', timeline, config);\n if (!gestureTrigger) return; // Gracefully skip if trigger module not loaded\n this._gestureTriggers.set(timeline, gestureTrigger);\n timeline._trigger = gestureTrigger;\n gestureTrigger.enable();\n }\n\n /**\n * Register a cursor trigger\n */\n registerCursor(timeline: Timeline, config: CursorConfig): void {\n this.unregister(timeline);\n const cursorTrigger = this._createTrigger('cursor', timeline, config);\n if (!cursorTrigger) return; // Gracefully skip if trigger module not loaded\n this._cursorTriggers.set(timeline, cursorTrigger);\n timeline._trigger = cursorTrigger;\n cursorTrigger.enable();\n }\n\n /**\n * Register a page exit trigger\n */\n registerPageExit(timeline: Timeline, config: PageExitConfig): void {\n this.unregister(timeline);\n const pageExitTrigger = this._createTrigger('pageExit', timeline, config);\n if (!pageExitTrigger) return; // Gracefully skip if trigger module not loaded\n this._pageExitTriggers.set(timeline, pageExitTrigger);\n timeline._trigger = pageExitTrigger;\n pageExitTrigger.enable();\n }\n\n /**\n * Unregister a timeline\n */\n unregister(timeline: Timeline): void {\n // PageLoad uses a separate API (unregister per-timeline, not disable())\n const pageLoad = this._getPageLoadTrigger();\n if (pageLoad) {\n pageLoad.unregister(timeline);\n }\n\n // Disable and remove from all trigger maps\n for (const map of this._allTriggerMaps) {\n const trigger = map.get(timeline);\n if (trigger) {\n trigger.disable();\n map.delete(timeline);\n }\n }\n\n timeline._trigger = undefined;\n\n // Detach resize listener when no scroll triggers remain\n if (this._scrollTriggers.size === 0) {\n this._detachResizeListener();\n }\n }\n\n /**\n * Refresh all position-based triggers after layout changes (e.g. window resize).\n * This includes scroll triggers, hover event triggers, gesture triggers, and\n * mouseMove triggers — all of which cache element bounds at setup time.\n *\n * Scroll triggers are refreshed in DOM document order (top to bottom).\n * This ensures that when a pinned trigger's spacer size changes during refresh,\n * triggers lower in the page measure the already-updated spacer positions rather\n * than stale pre-refresh values. Non-pinned triggers (no pin manager / layout\n * rect) are always sorted after pinned ones so they benefit from all spacer\n * updates before recalculating their own positions.\n */\n refreshScrollTriggers(): void {\n const scrollTriggersArray = Array.from(this._scrollTriggers.values());\n const scrollY = typeof window !== 'undefined' ? window.scrollY : 0;\n scrollTriggersArray.sort((a, b) => {\n const layoutA = (a as any)._pinManager?.getLayoutRect?.();\n const layoutB = (b as any)._pinManager?.getLayoutRect?.();\n // Triggers without a pin manager (non-pinned) sort after pinned triggers\n // so that spacer changes propagate before their positions are recalculated.\n if (!layoutA && !layoutB) return 0;\n if (!layoutA) return 1;\n if (!layoutB) return -1;\n return (layoutA.top + scrollY) - (layoutB.top + scrollY);\n });\n scrollTriggersArray.forEach(trigger => (trigger as any).refresh?.());\n this._eventTriggers.forEach(trigger => (trigger as any).refresh?.());\n this._gestureTriggers.forEach(trigger => (trigger as any).refresh?.());\n this._mouseMoveTriggers.forEach(trigger => (trigger as any).refresh?.());\n }\n\n /**\n * Kill all triggers\n */\n killAll(): void {\n // PageLoad uses .clear() (kills all at once, not per-timeline)\n const pageLoad = this._getPageLoadTrigger();\n if (pageLoad) {\n pageLoad.clear();\n }\n\n for (const map of this._allTriggerMaps) {\n map.forEach(trigger => trigger.disable());\n map.clear();\n }\n\n // No scroll triggers left — detach resize listener\n this._detachResizeListener();\n }\n\n /**\n * Reset for testing - DO NOT USE IN PRODUCTION\n * Resets the singleton instance\n * @internal\n */\n static _reset(): void {\n if (TriggerManager._instance) {\n if (TriggerManager._instance._pageLoadTrigger) {\n TriggerManager._instance._pageLoadTrigger._reset();\n }\n TriggerManager._instance.killAll();\n TriggerManager._instance = undefined!;\n }\n }\n}\n\n// Self-register with the registry\nSDKRegistry.triggerManager = { getInstance: TriggerManager.getInstance };\n",
|
|
6
|
+
"/**\n * SDKRegistry - Central registry for all optional SDK modules\n *\n * Optional modules register themselves here when loaded.\n * Core modules access features through this registry with optional chaining.\n * This enables tree-shaking: unused modules aren't bundled.\n */\n\nimport type { ParsedFilterFunction } from '../utils/FilterParser';\nimport type { ParsedDrawSVG } from '../utils/DrawSVGParser';\nimport type { ParsedClipPath } from '../utils/ClipPathParser';\nimport type { AnimationTarget, StaggerVars, SplitType, FlapConfig } from '../types';\nimport type { TriggerManager } from '../triggers/TriggerManager';\nimport type { FlapController, FlapCharDriver, UserPropEntry } from '../utils/TextFlapper';\n\n// --- Parser interfaces ---\n\ninterface ColorParserFunctions {\n parseColor: (color: string, element?: Element) => Float32Array | null;\n getCurrentColor: (element: Element, property: string) => Float32Array;\n}\n\ninterface FilterParserFunctions {\n parseFilter: (filter: string) => ParsedFilterFunction[] | null;\n getCurrentFilter: (element: Element) => ParsedFilterFunction[];\n mergeFilterArrays: (start: ParsedFilterFunction[], end: ParsedFilterFunction[]) => { start: ParsedFilterFunction[]; end: ParsedFilterFunction[] };\n interpolateFilters: (start: ParsedFilterFunction[], end: ParsedFilterFunction[], progress: number) => ParsedFilterFunction[];\n filterToString: (filters: ParsedFilterFunction[]) => string;\n}\n\ninterface DrawSVGParserFunctions {\n parseDrawSVG: (value: string | { start?: number; end?: number }, element?: Element) => ParsedDrawSVG | null;\n getCurrentDrawSVG: (element: Element) => ParsedDrawSVG;\n getPathLength: (element: Element) => number;\n applyDrawSVG: (element: Element, start: number, end: number, length: number) => void;\n}\n\ninterface ClipPathParserFunctions {\n parseClipPath: (value: string) => ParsedClipPath | null;\n getCurrentClipPath: (element: Element) => ParsedClipPath | null;\n interpolateClipPaths: (start: ParsedClipPath, end: ParsedClipPath, progress: number) => ParsedClipPath;\n clipPathToString: (parsed: ParsedClipPath) => string;\n canInterpolate: (start: ParsedClipPath, end: ParsedClipPath) => boolean;\n}\n\n// --- Utility interfaces ---\n\ninterface TextSplitterFunctions {\n split: (element: Element, type: SplitType, options?: { mask?: boolean; consumer?: object }) => HTMLElement[];\n revert: (element: Element, consumer?: object) => boolean;\n isSplit: (element: Element) => boolean;\n}\n\ninterface StyleResetFunctions {\n registerAnimatedProps: (element: Element, props: string[]) => void;\n clearAnimationStylesForProps: (element: Element, props: string[]) => void;\n clearAnimationStylesAndUnregister: (element: Element) => void;\n clearPinStylesAndUnregister: (element: Element) => void;\n}\n\ninterface FitResolverFunctions {\n registerPendingSetup(\n animation: import('../core/Animation').Animation,\n element: Element,\n fitConfig: import('../types').FitConfig,\n propTweenPool: typeof import('../memory/PropTweenPool').PropTweenPool\n ): void;\n}\n\ninterface TextFlapperFunctions {\n prepare: (charElements: HTMLElement[], config: FlapConfig, continuous?: boolean, userProps?: UserPropEntry[]) => FlapCharDriver[];\n flap: (charElements: HTMLElement[], config: FlapConfig, staggerDelays?: number[], continuous?: boolean) => FlapController;\n revert: (charElements: HTMLElement[]) => void;\n}\n\n/**\n * Unified registry for all optional SDK features.\n * Each slot is null until the corresponding module loads and self-registers.\n */\nexport const SDKRegistry = {\n // Parsers\n color: null as ColorParserFunctions | null,\n filter: null as FilterParserFunctions | null,\n drawSVG: null as DrawSVGParserFunctions | null,\n clipPath: null as ClipPathParserFunctions | null,\n\n // Utilities\n stagger: null as { resolve: (targets: AnimationTarget[], stagger: number | StaggerVars) => number[] } | null,\n textSplitter: null as TextSplitterFunctions | null,\n textFlapper: null as TextFlapperFunctions | null,\n triggerManager: null as { getInstance: () => TriggerManager } | null,\n styleReset: null as StyleResetFunctions | null,\n fit: null as FitResolverFunctions | null,\n};\n",
|
|
7
|
+
"/**\n * TriggerManager\n *\n * Coordinates all trigger types (pageLoad, pageExit, scroll, hover, click, mouseMove, gesture)\n * and activates timelines based on trigger conditions.\n *\n * Uses a registry pattern for trigger classes to avoid importing them directly.\n * Each trigger module registers itself via TriggerManager.registerTrigger() when loaded.\n * This enables tree-shaking: only trigger types included in the bundle are available.\n */\n\nimport type { ScrollConfig, MouseMoveConfig, GestureConfig, HoverConfig, ClickConfig, CursorConfig, PageExitConfig } from '../types';\nimport type { Timeline } from '../core/Timeline';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\n/** Config for page load trigger */\nexport interface PageLoadTriggerConfig {\n timing?: 'before' | 'during' | 'after';\n /** When true, the timeline is built but not played — use Motion('name').play() to start it manually */\n paused?: boolean;\n}\n\n/** Interface that trigger instances must satisfy for cleanup */\ninterface TriggerInstance {\n enable(): void;\n disable(): void;\n}\n\n/** Interface for PageLoadTrigger specifically */\ninterface PageLoadTriggerInstance {\n register(timeline: Timeline, config: PageLoadTriggerConfig): void;\n unregister(timeline: Timeline): void;\n clear(): void;\n _reset(): void;\n}\n\n/** Trigger class constructors stored in registry */\ntype TriggerFactory = new (...args: any[]) => TriggerInstance;\n\nexport class TriggerManager {\n private static _instance: TriggerManager;\n\n /** Registry for trigger class constructors - populated by each trigger module on load */\n private static _triggerRegistry: Map<string, TriggerFactory> = new Map();\n /** PageLoadTrigger factory - separate because it has a different interface */\n private static _pageLoadTriggerFactory: (new () => PageLoadTriggerInstance) | null = null;\n\n /** Debounce delay (ms) for the window resize → refreshScrollTriggers handler */\n private static readonly _RESIZE_DEBOUNCE_MS = 200;\n\n /** Debounce delay (ms) for the body ResizeObserver → refreshScrollTriggers handler */\n private static readonly _BODY_RESIZE_DEBOUNCE_MS = 250;\n\n private _pageLoadTrigger: PageLoadTriggerInstance | null = null;\n private _scrollTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _eventTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _mouseMoveTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _gestureTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _cursorTriggers: Map<Timeline, TriggerInstance> = new Map();\n private _pageExitTriggers: Map<Timeline, TriggerInstance> = new Map();\n\n /** Bound resize handler (created once, reused for add/removeEventListener) */\n private _resizeHandler: (() => void) | null = null;\n /** Timer id for debounced resize */\n private _resizeTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** ResizeObserver watching document.body for height changes (layout shifts) */\n private _bodyResizeObserver: ResizeObserver | null = null;\n /** Last known body height — only refresh when height actually changes */\n private _lastBodyHeight: number = 0;\n /** Timer id for debounced body resize */\n private _bodyResizeTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** All trigger maps — used by unregister() and killAll() for bulk iteration */\n private get _allTriggerMaps(): Map<Timeline, TriggerInstance>[] {\n return [\n this._scrollTriggers,\n this._eventTriggers,\n this._mouseMoveTriggers,\n this._gestureTriggers,\n this._cursorTriggers,\n this._pageExitTriggers,\n ];\n }\n\n private constructor() {\n // PageLoadTrigger is created lazily on first use, not eagerly in constructor\n }\n\n static getInstance(): TriggerManager {\n if (!TriggerManager._instance) {\n TriggerManager._instance = new TriggerManager();\n }\n return TriggerManager._instance;\n }\n\n /**\n * Register a trigger class. Called by each trigger module when it loads.\n * @internal\n */\n static registerTrigger(type: string, factory: TriggerFactory): void {\n TriggerManager._triggerRegistry.set(type, factory);\n }\n\n /**\n * Register the PageLoadTrigger class. Called by PageLoadTrigger module when it loads.\n * @internal\n */\n static registerPageLoadTrigger(factory: new () => PageLoadTriggerInstance): void {\n TriggerManager._pageLoadTriggerFactory = factory;\n }\n\n /** Get or create the PageLoadTrigger instance */\n private _getPageLoadTrigger(): PageLoadTriggerInstance | null {\n if (!this._pageLoadTrigger && TriggerManager._pageLoadTriggerFactory) {\n this._pageLoadTrigger = new TriggerManager._pageLoadTriggerFactory();\n }\n return this._pageLoadTrigger;\n }\n\n /** Create a trigger instance from the registry */\n private _createTrigger(type: string, ...args: any[]): TriggerInstance | null {\n const Factory = TriggerManager._triggerRegistry.get(type);\n if (!Factory) {\n const available = Array.from(TriggerManager._triggerRegistry.keys());\n console.warn(\n `[Motion SDK] Trigger type '${type}' is not available in this bundle. ` +\n `Available triggers: ${available.join(', ') || 'none'}. ` +\n `Animations using .on${type.charAt(0).toUpperCase() + type.slice(1)}() will be skipped.`\n );\n return null;\n }\n return new Factory(...args);\n }\n\n /**\n * Register a pageLoad trigger\n */\n registerPageLoad(timeline: Timeline, config: PageLoadTriggerConfig): void {\n this.unregister(timeline);\n const pageLoad = this._getPageLoadTrigger();\n if (pageLoad) {\n pageLoad.register(timeline, config);\n }\n }\n\n /**\n * Attach a debounced window resize listener that refreshes all scroll triggers.\n * Called lazily when the first scroll trigger is registered.\n * @internal\n */\n private _attachResizeListener(): void {\n if (this._resizeHandler || typeof window === 'undefined') return;\n\n this._resizeHandler = () => {\n if (this._resizeTimer !== null) clearTimeout(this._resizeTimer);\n this._resizeTimer = setTimeout(() => {\n this._resizeTimer = null;\n this.refreshScrollTriggers();\n }, TriggerManager._RESIZE_DEBOUNCE_MS);\n };\n\n window.addEventListener('resize', this._resizeHandler, { passive: true });\n }\n\n /**\n * Detach the window resize listener.\n * Called when the last scroll trigger is removed or killAll() is invoked.\n * @internal\n */\n private _detachResizeListener(): void {\n if (!this._resizeHandler || typeof window === 'undefined') return;\n\n window.removeEventListener('resize', this._resizeHandler);\n if (this._resizeTimer !== null) {\n clearTimeout(this._resizeTimer);\n this._resizeTimer = null;\n }\n this._resizeHandler = null;\n }\n\n /**\n * Attach a ResizeObserver on document.body to detect document height changes.\n *\n * Layout shifts caused by content-visibility:auto, lazy-loaded images,\n * dynamic content insertion, or font loading can change the document height\n * without triggering a window resize event. This observer detects those\n * shifts and refreshes all scroll triggers so pin positions stay correct.\n *\n * Only reacts to height changes (width changes are already covered by the\n * window resize listener). Debounced to avoid excessive refreshes during\n * rapid layout shifts (e.g. multiple images loading in sequence).\n * @internal\n */\n private _attachBodyResizeObserver(): void {\n if (this._bodyResizeObserver) return;\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n if (typeof ResizeObserver === 'undefined') return;\n\n // Snapshot current body height so the first observation doesn't trigger\n // a spurious refresh.\n this._lastBodyHeight = document.body.offsetHeight;\n\n this._bodyResizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n // Use borderBoxSize when available (more accurate), fall back to contentRect\n const newHeight = entry.borderBoxSize?.[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n\n if (Math.abs(newHeight - this._lastBodyHeight) < 1) continue;\n\n this._lastBodyHeight = newHeight;\n\n // Debounce: multiple layout shifts can happen in quick succession\n // (e.g. several lazy images loading, content-visibility sections\n // rendering). Wait for shifts to settle before refreshing.\n if (this._bodyResizeTimer !== null) clearTimeout(this._bodyResizeTimer);\n this._bodyResizeTimer = setTimeout(() => {\n this._bodyResizeTimer = null;\n this.refreshScrollTriggers();\n }, TriggerManager._BODY_RESIZE_DEBOUNCE_MS);\n }\n });\n\n this._bodyResizeObserver.observe(document.body);\n }\n\n /**\n * Detach the body ResizeObserver.\n * Called when the last scroll trigger is removed or killAll() is invoked.\n * @internal\n */\n private _detachBodyResizeObserver(): void {\n if (!this._bodyResizeObserver) return;\n\n this._bodyResizeObserver.disconnect();\n this._bodyResizeObserver = null;\n\n if (this._bodyResizeTimer !== null) {\n clearTimeout(this._bodyResizeTimer);\n this._bodyResizeTimer = null;\n }\n\n this._lastBodyHeight = 0;\n }\n\n /**\n * Register a scroll trigger\n */\n registerScroll(timeline: Timeline, config: ScrollConfig): void {\n this.unregister(timeline);\n const scrollTrigger = this._createTrigger('scroll', timeline, config);\n if (!scrollTrigger) return; // Gracefully skip if trigger module not loaded\n this._scrollTriggers.set(timeline, scrollTrigger);\n timeline._trigger = scrollTrigger;\n scrollTrigger.enable();\n\n // Lazily attach resize listener and body observer when first scroll trigger is added\n this._attachResizeListener();\n this._attachBodyResizeObserver();\n }\n\n /**\n * Register a hover trigger\n */\n registerHover(timeline: Timeline, config: HoverConfig): void {\n this.unregister(timeline);\n const eventTrigger = this._createTrigger('event', timeline, {\n type: 'hover',\n target: config.target,\n onEnter: config.onEnter,\n onLeave: config.onLeave,\n leaveDelay: config.leaveDelay,\n });\n if (!eventTrigger) return; // Gracefully skip if trigger module not loaded\n this._eventTriggers.set(timeline, eventTrigger);\n timeline._trigger = eventTrigger;\n eventTrigger.enable();\n }\n\n /**\n * Register a click trigger\n */\n registerClick(timeline: Timeline, config: ClickConfig): void {\n this.unregister(timeline);\n const eventTrigger = this._createTrigger('event', timeline, {\n type: 'click',\n target: config.target,\n secondTarget: config.secondTarget,\n toggle: config.toggle,\n preventDefault: config.preventDefault,\n });\n if (!eventTrigger) return; // Gracefully skip if trigger module not loaded\n this._eventTriggers.set(timeline, eventTrigger);\n timeline._trigger = eventTrigger;\n eventTrigger.enable();\n }\n\n /**\n * Register a mouseMove trigger\n */\n registerMouseMove(timeline: Timeline, config: MouseMoveConfig): void {\n this.unregister(timeline);\n const mouseMoveTrigger = this._createTrigger('mouseMove', timeline, config);\n if (!mouseMoveTrigger) return; // Gracefully skip if trigger module not loaded\n this._mouseMoveTriggers.set(timeline, mouseMoveTrigger);\n timeline._trigger = mouseMoveTrigger;\n mouseMoveTrigger.enable();\n }\n\n /**\n * Register a gesture trigger\n */\n registerGesture(timeline: Timeline, config: GestureConfig): void {\n this.unregister(timeline);\n const gestureTrigger = this._createTrigger('gesture', timeline, config);\n if (!gestureTrigger) return; // Gracefully skip if trigger module not loaded\n this._gestureTriggers.set(timeline, gestureTrigger);\n timeline._trigger = gestureTrigger;\n gestureTrigger.enable();\n }\n\n /**\n * Register a cursor trigger\n */\n registerCursor(timeline: Timeline, config: CursorConfig): void {\n this.unregister(timeline);\n const cursorTrigger = this._createTrigger('cursor', timeline, config);\n if (!cursorTrigger) return; // Gracefully skip if trigger module not loaded\n this._cursorTriggers.set(timeline, cursorTrigger);\n timeline._trigger = cursorTrigger;\n cursorTrigger.enable();\n }\n\n /**\n * Register a page exit trigger\n */\n registerPageExit(timeline: Timeline, config: PageExitConfig): void {\n this.unregister(timeline);\n const pageExitTrigger = this._createTrigger('pageExit', timeline, config);\n if (!pageExitTrigger) return; // Gracefully skip if trigger module not loaded\n this._pageExitTriggers.set(timeline, pageExitTrigger);\n timeline._trigger = pageExitTrigger;\n pageExitTrigger.enable();\n }\n\n /**\n * Unregister a timeline\n */\n unregister(timeline: Timeline): void {\n // PageLoad uses a separate API (unregister per-timeline, not disable())\n const pageLoad = this._getPageLoadTrigger();\n if (pageLoad) {\n pageLoad.unregister(timeline);\n }\n\n // Disable and remove from all trigger maps\n for (const map of this._allTriggerMaps) {\n const trigger = map.get(timeline);\n if (trigger) {\n trigger.disable();\n map.delete(timeline);\n }\n }\n\n timeline._trigger = undefined;\n\n // Detach resize listener and body observer when no scroll triggers remain\n if (this._scrollTriggers.size === 0) {\n this._detachResizeListener();\n this._detachBodyResizeObserver();\n }\n }\n\n /**\n * Refresh all position-based triggers after layout changes (e.g. window resize).\n * This includes scroll triggers, hover event triggers, gesture triggers, and\n * mouseMove triggers — all of which cache element bounds at setup time.\n *\n * Scroll triggers are refreshed in DOM document order (top to bottom).\n * This ensures that when a pinned trigger's spacer size changes during refresh,\n * triggers lower in the page measure the already-updated spacer positions rather\n * than stale pre-refresh values. Non-pinned triggers (no pin manager / layout\n * rect) are always sorted after pinned ones so they benefit from all spacer\n * updates before recalculating their own positions.\n */\n refreshScrollTriggers(): void {\n const scrollTriggersArray = Array.from(this._scrollTriggers.values());\n const scrollY = typeof window !== 'undefined' ? window.scrollY : 0;\n scrollTriggersArray.sort((a, b) => {\n const layoutA = (a as any)._pinManager?.getLayoutRect?.();\n const layoutB = (b as any)._pinManager?.getLayoutRect?.();\n // Triggers without a pin manager (non-pinned) sort after pinned triggers\n // so that spacer changes propagate before their positions are recalculated.\n if (!layoutA && !layoutB) return 0;\n if (!layoutA) return 1;\n if (!layoutB) return -1;\n return (layoutA.top + scrollY) - (layoutB.top + scrollY);\n });\n scrollTriggersArray.forEach(trigger => (trigger as any).refresh?.());\n this._eventTriggers.forEach(trigger => (trigger as any).refresh?.());\n this._gestureTriggers.forEach(trigger => (trigger as any).refresh?.());\n this._mouseMoveTriggers.forEach(trigger => (trigger as any).refresh?.());\n }\n\n /**\n * Kill all triggers\n */\n killAll(): void {\n // PageLoad uses .clear() (kills all at once, not per-timeline)\n const pageLoad = this._getPageLoadTrigger();\n if (pageLoad) {\n pageLoad.clear();\n }\n\n for (const map of this._allTriggerMaps) {\n map.forEach(trigger => trigger.disable());\n map.clear();\n }\n\n // No scroll triggers left — detach resize listener and body observer\n this._detachResizeListener();\n this._detachBodyResizeObserver();\n }\n\n /**\n * Reset for testing - DO NOT USE IN PRODUCTION\n * Resets the singleton instance\n * @internal\n */\n static _reset(): void {\n if (TriggerManager._instance) {\n if (TriggerManager._instance._pageLoadTrigger) {\n TriggerManager._instance._pageLoadTrigger._reset();\n }\n TriggerManager._instance.killAll();\n TriggerManager._instance = undefined!;\n }\n }\n}\n\n// Self-register with the registry\nSDKRegistry.triggerManager = { getInstance: TriggerManager.getInstance };\n",
|
|
8
8
|
"/**\n * TargetResolver - Convert various target formats to AnimationTarget array\n *\n * Supports both DOM elements and plain JS objects for universal animation.\n */\n\nimport type { AnimationTarget, ObjectTarget, TargetInput } from '../types';\n\n/**\n * Execute callback when DOM is ready\n * If DOM is already ready, executes immediately\n */\nexport function whenDOMReady(callback: () => void): void {\n if (typeof document === 'undefined') {\n callback();\n return;\n }\n \n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', callback, { once: true });\n } else {\n callback();\n }\n}\n\n/**\n * Check if value is a plain JS object (not DOM, not array-like)\n * Used to detect objects like { x: 0, y: 0 } or mesh.position\n */\nfunction isPlainObject(value: unknown): value is ObjectTarget {\n if (value === null || typeof value !== 'object') return false;\n if (value instanceof Element) return false;\n if ('nodeType' in value) return false; // DOM node\n if ('style' in value) return false; // DOM element or mock element\n\n // Check for ThreeJS-style objects (Vector3, Euler, etc.)\n // These have 'length' but also have named numeric properties like x, y, z\n const obj = value as Record<string, unknown>;\n if ('x' in obj && typeof obj.x === 'number') {\n return true; // ThreeJS Vector3, Euler, or similar\n }\n\n // Not array-like (NodeList, Array, HTMLCollection)\n if ('length' in value) return false;\n\n return true;\n}\n\n/**\n * Check if a resolved target is a DOM Element\n * Used for caching _isElement flag in PropTween\n * Also recognizes mock elements (objects with 'style' property) in test environments\n */\nexport function isElement(target: AnimationTarget): target is Element {\n return target instanceof Element ||\n (typeof target === 'object' && target !== null &&\n ('nodeType' in target || 'style' in target));\n}\n\n/**\n * Resolve animation targets to an array of AnimationTargets\n * @param targets - CSS selector, string[], Element, NodeList, Element[], plain object, or object array\n * @returns Array of resolved AnimationTargets (Element or ObjectTarget)\n */\nexport function resolveTargets(targets: TargetInput): AnimationTarget[] {\n // Plain object target (e.g., { x: 0, y: 0 } or mesh.position)\n if (isPlainObject(targets)) {\n return [targets];\n }\n\n // String selector\n if (typeof targets === 'string') {\n if (typeof document !== 'undefined') {\n try {\n return Array.from(document.querySelectorAll(targets));\n } catch (e) {\n console.warn(`[Motion] Invalid selector \"${targets}\":`, e);\n return [];\n }\n }\n return [];\n }\n\n // Single Element\n if (targets instanceof Element) {\n return [targets];\n }\n\n // Check if it's a mock element (has a style property) - for test environments\n if (targets && typeof targets === 'object' && 'style' in targets && !Array.isArray(targets)) {\n return [targets as unknown as Element];\n }\n\n // NodeList\n if (targets instanceof NodeList) {\n return Array.from(targets) as Element[];\n }\n\n // Array - could be string[], Element[] or ObjectTarget[]\n if (Array.isArray(targets)) {\n if (targets.length === 0) return [];\n\n // Array of string selectors - resolve each and flatten\n if (typeof targets[0] === 'string') {\n if (typeof document === 'undefined') return [];\n const elements: Element[] = [];\n for (const selector of targets as string[]) {\n try {\n elements.push(...Array.from(document.querySelectorAll(selector)));\n } catch (e) {\n console.warn(`[Motion] Invalid selector \"${selector}\":`, e);\n }\n }\n return elements;\n }\n\n // Check first element to determine type\n if (isPlainObject(targets[0])) {\n return targets as ObjectTarget[];\n }\n return targets as Element[];\n }\n\n // Handle array-like objects (HTMLCollection)\n if (targets && typeof targets === 'object' && 'length' in targets) {\n return Array.from(targets as ArrayLike<Element>);\n }\n\n return [];\n}\n",
|
|
9
9
|
"/**\n * BaseTrigger - Abstract base class for all standard trigger types\n *\n * Extracts the common enable/disable lifecycle pattern shared by\n * ScrollTrigger, EventTrigger, MouseMoveTrigger, GestureTrigger, and CursorTrigger.\n *\n * Provides:\n * - enable/disable guards with _enabled flag\n * - DOM-ready gating via whenDOMReady\n * - Shared helper methods for selector resolution and listener cleanup\n *\n * NOT used by PageLoadTrigger (which has a different lifecycle pattern).\n */\n\nimport type { Timeline } from '../core/Timeline';\nimport { whenDOMReady } from '../utils/TargetResolver';\n\nexport abstract class BaseTrigger<TConfig> {\n protected _timeline: Timeline;\n protected _config: TConfig;\n protected _enabled: boolean = false;\n\n constructor(timeline: Timeline, config: TConfig) {\n this._timeline = timeline;\n this._config = config;\n }\n\n /**\n * Enable the trigger. Guards against double-enable and waits for DOM ready.\n * Calls _onEnable() once the DOM is ready (or immediately if already ready).\n */\n public enable(): void {\n if (this._enabled) return;\n this._enabled = true;\n\n whenDOMReady(() => {\n // May have been disabled while waiting for DOM to be ready\n if (!this._enabled) return;\n this._onEnable();\n });\n }\n\n /**\n * Disable the trigger. Guards against double-disable.\n * Calls _onDisable() immediately (no DOM-ready gating needed for teardown).\n */\n public disable(): void {\n if (!this._enabled) return;\n this._enabled = false;\n this._onDisable();\n }\n\n /**\n * Subclass: set up event listeners, resolve targets, start ticker loops, etc.\n * Called once per enable cycle after DOM is ready.\n */\n protected abstract _onEnable(): void;\n\n /**\n * Subclass: remove event listeners, clear state, stop ticker loops, etc.\n * Called once per disable cycle.\n */\n protected abstract _onDisable(): void;\n\n /**\n * Resolve a single element from a string selector or Element reference.\n * Returns the fallback (defaults to null) if selector is undefined, not found, or invalid.\n *\n * Note: non-string values are treated as direct element references without an instanceof\n * check, so this works with both real DOM elements and mock objects in tests.\n */\n protected _resolveElement(\n selector: string | Element | undefined,\n fallback?: EventTarget | null\n ): HTMLElement | EventTarget | null {\n if (selector === undefined || selector === null) {\n return fallback ?? null;\n }\n // Non-string: treat as direct element reference (works with mock elements in tests)\n if (typeof selector !== 'string') {\n return selector as HTMLElement;\n }\n // String selector: query the DOM\n if (typeof document !== 'undefined') {\n try {\n const el = document.querySelector(selector);\n if (el !== null) return el as unknown as HTMLElement;\n } catch (e) {\n console.warn(`[Motion] Invalid selector \"${selector}\":`, e);\n }\n }\n return fallback ?? null;\n }\n\n /**\n * Resolve multiple elements from a string selector or single Element reference.\n * Returns an empty array if selector is undefined, not found, or invalid.\n *\n * Note: non-string values are treated as direct element references without an instanceof\n * check, so this works with both real DOM elements and mock objects in tests.\n */\n protected _resolveElements(\n selector: string | Element | undefined\n ): Element[] {\n if (selector === undefined || selector === null) return [];\n // Non-string: treat as direct element reference (works with mock elements in tests)\n if (typeof selector !== 'string') {\n return [selector as Element];\n }\n // String selector: query the DOM\n if (typeof document !== 'undefined') {\n try {\n const elements = document.querySelectorAll(selector);\n return Array.from(elements);\n } catch (e) {\n console.warn(`[Motion] Invalid selector \"${selector}\":`, e);\n return [];\n }\n }\n return [];\n }\n\n /**\n * Remove all event listeners tracked in a nested listener map and clear the map.\n * Accepts Map<EventTarget, Map<eventName, listener>>.\n */\n protected _cleanupListenerMap(\n listeners: Map<EventTarget, Map<string, EventListener>>\n ): void {\n listeners.forEach((eventMap, target) => {\n eventMap.forEach((listener, eventName) => {\n target.removeEventListener(eventName, listener);\n });\n });\n listeners.clear();\n }\n}\n",
|
|
10
10
|
"/**\n * Returns the element's bounding rect as if no CSS transform or position:fixed were applied.\n * Temporarily strips inline transforms and any fixed positioning, measures, then restores.\n * This gives us the \"layout\" position in the document flow.\n *\n * Used by ScrollTrigger and MarkerManager to compute trigger positions and\n * marker placements relative to the element's original layout position,\n * regardless of any animation transforms or pin states currently applied.\n *\n * Handles position:fixed elements: a fixed element returns viewport-relative\n * coordinates from getBoundingClientRect(), which would produce incorrect\n * document-relative trigger positions. Temporarily clearing position/top/left/width\n * restores document-flow measurement before reading the rect.\n *\n * Performance notes:\n * - The getComputedStyle() call to detect position:fixed is skipped when we\n * can tell from the inline style alone (fast path for the common case).\n * - No forced reflow after restoring styles — the browser batches style\n * writes and applies them before the next paint automatically.\n */\nexport function getLayoutRect(element: Element): DOMRect {\n const el = element as HTMLElement;\n const savedTransform = el.style.transform;\n const savedTransition = el.style.transition;\n\n // Disable transitions to prevent visual flicker during measurement\n el.style.transition = 'none';\n el.style.transform = 'none';\n\n // Detect position:fixed. Fast path: check inline style first (avoids\n // getComputedStyle in the common case where position is set inline by PinManager).\n // Slow path: only call getComputedStyle if inline style doesn't conclusively tell us.\n let isFixed = false;\n const savedPosition = el.style.position;\n const savedTop = el.style.top;\n const savedLeft = el.style.left;\n const savedWidth = el.style.width;\n\n if (savedPosition === 'fixed') {\n // Inline style is explicitly fixed — no need for getComputedStyle\n isFixed = true;\n } else if (savedPosition === '' || savedPosition === undefined) {\n // No inline position — could be fixed via stylesheet, must check computed\n if (\n typeof window !== 'undefined' &&\n typeof window.getComputedStyle === 'function' &&\n window.getComputedStyle(el).position === 'fixed'\n ) {\n isFixed = true;\n }\n }\n // else: inline position is set to something other than 'fixed' (static, relative, absolute)\n // — no need to check computed, it's definitely not fixed\n\n if (isFixed) {\n el.style.position = '';\n el.style.top = '';\n el.style.left = '';\n el.style.width = '';\n }\n\n const rect = el.getBoundingClientRect();\n\n // Restore all saved styles. The browser will batch these writes and apply\n // them before the next paint — no forced reflow needed.\n el.style.transform = savedTransform;\n el.style.transition = savedTransition;\n if (isFixed) {\n el.style.position = savedPosition;\n el.style.top = savedTop;\n el.style.left = savedLeft;\n el.style.width = savedWidth;\n }\n\n return rect;\n}\n",
|
|
11
|
-
"/**\n * EventTrigger\n *\n * Triggers timelines based on DOM events:\n * - hover: plays on mouseenter, reverses on mouseleave\n * - click: plays on click with optional toggle behavior (reverse/restart on 2nd click)\n */\n\nimport type { Timeline } from '../core/Timeline';\nimport { executeTimelineAction } from '../utils/executeTimelineAction';\nimport type { TimelineAction } from '../utils/executeTimelineAction';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\nimport { getLayoutRect } from '../utils/getLayoutRect';\n\n/** Internal config for EventTrigger */\nexport interface EventTriggerConfig {\n type: 'hover' | 'click';\n target?: string | Element;\n // Click-specific options\n secondTarget?: string | Element; // Element for 2nd click action\n toggle?: 'reverse' | 'restart' | 'play'; // 2nd click behavior\n preventDefault?: boolean;\n // Hover-specific options\n onLeave?: 'reverse' | 'pause' | 'stop' | 'restart' | 'none'; // Mouse leave behavior\n leaveDelay?: number; // Delay before leave action (seconds)\n}\n\nexport class EventTrigger extends BaseTrigger<EventTriggerConfig> {\n private _targets: Element[] = [];\n private _secondTargets: Element[] = []; // Targets for 2nd click\n private _listeners: Map<EventTarget, Map<string, EventListener>> = new Map();\n private _isForward: boolean = true; // Toggle state for click\n\n // Hover frozen-rect state — bounds are snapshotted at rest so that\n // translateY animations don't cause infinite mouseenter/mouseleave jitter.\n private _frozenRects: Map<Element, DOMRect> = new Map();\n private _snapshotScrollX: number = 0; // scroll offset when rects were captured\n private _snapshotScrollY: number = 0;\n private _wasHovering: boolean = false;\n private _hoverLeaveTimeout: number | null = null;\n private _boundMouseMoveHandler: ((e: MouseEvent) => void) | null = null;\n\n constructor(timeline: Timeline, config: EventTriggerConfig) {\n super(timeline, config);\n }\n\n /**\n * Set up event listeners once DOM is ready.\n */\n protected _onEnable(): void {\n // Resolve target elements\n this._resolveTargets();\n\n // Add event listeners based on trigger type\n if (this._config.type === 'hover') {\n this._addHoverListeners();\n } else if (this._config.type === 'click') {\n this._addClickListeners();\n }\n }\n\n /**\n * Remove all event listeners and cancel pending timers.\n */\n protected _onDisable(): void {\n // Remove global mousemove handler used by hover frozen-rect detection\n if (this._boundMouseMoveHandler) {\n window.removeEventListener('mousemove', this._boundMouseMoveHandler);\n this._boundMouseMoveHandler = null;\n }\n\n // Clear pending hover leave timeout\n if (this._hoverLeaveTimeout !== null) {\n clearTimeout(this._hoverLeaveTimeout);\n this._hoverLeaveTimeout = null;\n }\n\n // Clear hover state\n this._frozenRects.clear();\n this._wasHovering = false;\n\n // Remove all click listeners\n this._cleanupListenerMap(this._listeners);\n }\n\n /**\n * Re-snapshot frozen rects after layout changes (e.g. window resize).\n * Called by TriggerManager.refreshScrollTriggers() on resize.\n */\n refresh(): void {\n if (this._config.type !== 'hover') return;\n this._snapshotRects();\n }\n\n /** Snapshot frozen rects and the current scroll offset. */\n private _snapshotRects(): void {\n this._snapshotScrollX = window.scrollX || 0;\n this._snapshotScrollY = window.scrollY || 0;\n for (const element of this._targets) {\n this._frozenRects.set(element, getLayoutRect(element));\n }\n }\n\n private _resolveTargets(): void {\n // Resolve primary targets\n this._targets = this._resolveElements(this._config.target);\n\n // Resolve secondary targets (for 2nd click) - defaults to primary targets\n if (this._config.secondTarget) {\n this._secondTargets = this._resolveElements(this._config.secondTarget);\n } else {\n this._secondTargets = this._targets;\n }\n }\n\n private _addHoverListeners(): void {\n const onLeave = this._config.onLeave || 'reverse';\n const leaveDelay = this._config.leaveDelay || 0;\n\n // Snapshot element bounds at their rest (layout) position using getLayoutRect.\n // This strips any inline transform currently applied by an animation, measures\n // the element as if no transform exists, then restores the transform.\n // Using frozen rects prevents the jitter loop that occurs when a translateY\n // animation moves the element away from the cursor: with live bounds the browser\n // would fire mouseleave → reverse → element returns → mouseenter → play → repeat.\n this._snapshotRects();\n\n // A single window mousemove listener replaces per-element mouseenter/mouseleave.\n // Hit-testing against frozen rects keeps detection stable during animation.\n // Frozen rects are viewport-relative at snapshot time, so we compensate for\n // any scroll that happened since the snapshot by adjusting cursor coordinates.\n this._boundMouseMoveHandler = (e: MouseEvent) => {\n const scrollDX = (window.scrollX || 0) - this._snapshotScrollX;\n const scrollDY = (window.scrollY || 0) - this._snapshotScrollY;\n const cx = e.clientX + scrollDX;\n const cy = e.clientY + scrollDY;\n\n // Check if cursor is inside any frozen target rect\n let isOverTarget = false;\n for (const rect of this._frozenRects.values()) {\n if (cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom) {\n isOverTarget = true;\n break;\n }\n }\n\n if (isOverTarget && !this._wasHovering) {\n // Cursor entered — cancel any pending leave and play timeline\n this._wasHovering = true;\n if (this._hoverLeaveTimeout !== null) {\n clearTimeout(this._hoverLeaveTimeout);\n this._hoverLeaveTimeout = null;\n }\n this._timeline.play();\n } else if (!isOverTarget && this._wasHovering) {\n // Cursor left — execute leave action (with optional delay)\n this._wasHovering = false;\n if (onLeave === 'none') return;\n\n const executeLeaveAction = () => {\n this._handleLeaveAction(onLeave);\n this._hoverLeaveTimeout = null;\n };\n\n if (leaveDelay > 0) {\n this._hoverLeaveTimeout = window.setTimeout(executeLeaveAction, leaveDelay * 1000);\n } else {\n executeLeaveAction();\n }\n }\n };\n\n window.addEventListener('mousemove', this._boundMouseMoveHandler);\n }\n\n private _handleLeaveAction(action: 'reverse' | 'pause' | 'stop' | 'restart' | 'none'): void {\n // Map EventTrigger's 'stop' to the shared utility's 'reset' (same behavior: pause + progress(0))\n const mapped: TimelineAction = action === 'stop' ? 'reset' : action;\n executeTimelineAction(mapped, this._timeline);\n }\n\n private _addClickListeners(): void {\n const toggle = this._config.toggle || 'play';\n const preventDefault = this._config.preventDefault || false;\n const hasSeparateSecondTarget = this._config.secondTarget && this._config.secondTarget !== this._config.target;\n\n // Add listeners to primary targets (1st click / play)\n this._targets.forEach(element => {\n const elementListeners = this._listeners.get(element) || new Map<string, EventListener>();\n\n const clickListener = (e: Event) => {\n if (preventDefault) {\n e.preventDefault();\n }\n\n // If same element handles both clicks, use toggle state\n if (!hasSeparateSecondTarget) {\n if (this._isForward) {\n // First click - play\n this._timeline.restart();\n if (toggle !== 'play') {\n this._isForward = false;\n }\n } else {\n // Second click - reverse/restart/play based on toggle setting\n this._handleSecondClick(toggle);\n this._isForward = true;\n }\n } else {\n // Separate targets: primary always plays\n this._timeline.restart();\n }\n };\n\n element.addEventListener('click', clickListener);\n elementListeners.set('click', clickListener);\n this._listeners.set(element, elementListeners);\n });\n\n // Add listeners to secondary targets (2nd click) if different from primary\n if (hasSeparateSecondTarget) {\n this._secondTargets.forEach(element => {\n // Skip if already has listeners (overlapping selectors)\n if (this._listeners.has(element)) return;\n\n const elementListeners = new Map<string, EventListener>();\n\n const clickListener = (e: Event) => {\n if (preventDefault) {\n e.preventDefault();\n }\n this._handleSecondClick(toggle);\n };\n\n element.addEventListener('click', clickListener);\n elementListeners.set('click', clickListener);\n this._listeners.set(element, elementListeners);\n });\n }\n }\n\n private _handleSecondClick(toggle: 'reverse' | 'restart' | 'play'): void {\n executeTimelineAction(toggle, this._timeline);\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('event', EventTrigger);\n",
|
|
11
|
+
"/**\n * EventTrigger\n *\n * Triggers timelines based on DOM events:\n * - hover: plays on mouseenter, reverses on mouseleave\n * - click: plays on click with optional toggle behavior (reverse/restart on 2nd click)\n */\n\nimport type { Timeline } from '../core/Timeline';\nimport { executeTimelineAction } from '../utils/executeTimelineAction';\nimport type { TimelineAction } from '../utils/executeTimelineAction';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\nimport { getLayoutRect } from '../utils/getLayoutRect';\n\n/** Internal config for EventTrigger */\nexport interface EventTriggerConfig {\n type: 'hover' | 'click';\n target?: string | Element;\n // Click-specific options\n secondTarget?: string | Element; // Element for 2nd click action\n toggle?: 'reverse' | 'restart' | 'play'; // 2nd click behavior\n preventDefault?: boolean;\n // Hover-specific options\n onEnter?: 'play' | 'restart'; // Mouse enter behavior (default: 'play')\n onLeave?: 'reverse' | 'pause' | 'stop' | 'restart' | 'none'; // Mouse leave behavior\n leaveDelay?: number; // Delay before leave action (seconds)\n}\n\nexport class EventTrigger extends BaseTrigger<EventTriggerConfig> {\n private _targets: Element[] = [];\n private _secondTargets: Element[] = []; // Targets for 2nd click\n private _listeners: Map<EventTarget, Map<string, EventListener>> = new Map();\n private _isForward: boolean = true; // Toggle state for click\n\n // Hover frozen-rect state — bounds are snapshotted at rest so that\n // translateY animations don't cause infinite mouseenter/mouseleave jitter.\n private _frozenRects: Map<Element, DOMRect> = new Map();\n private _snapshotScrollX: number = 0; // scroll offset when rects were captured\n private _snapshotScrollY: number = 0;\n private _wasHovering: boolean = false;\n private _hoverLeaveTimeout: number | null = null;\n private _boundMouseMoveHandler: ((e: MouseEvent) => void) | null = null;\n\n constructor(timeline: Timeline, config: EventTriggerConfig) {\n super(timeline, config);\n }\n\n /**\n * Set up event listeners once DOM is ready.\n */\n protected _onEnable(): void {\n // Resolve target elements\n this._resolveTargets();\n\n // Add event listeners based on trigger type\n if (this._config.type === 'hover') {\n this._addHoverListeners();\n } else if (this._config.type === 'click') {\n this._addClickListeners();\n }\n }\n\n /**\n * Remove all event listeners and cancel pending timers.\n */\n protected _onDisable(): void {\n // Remove global mousemove handler used by hover frozen-rect detection\n if (this._boundMouseMoveHandler) {\n window.removeEventListener('mousemove', this._boundMouseMoveHandler);\n this._boundMouseMoveHandler = null;\n }\n\n // Clear pending hover leave timeout\n if (this._hoverLeaveTimeout !== null) {\n clearTimeout(this._hoverLeaveTimeout);\n this._hoverLeaveTimeout = null;\n }\n\n // Clear hover state\n this._frozenRects.clear();\n this._wasHovering = false;\n\n // Remove all click listeners\n this._cleanupListenerMap(this._listeners);\n }\n\n /**\n * Re-snapshot frozen rects after layout changes (e.g. window resize).\n * Called by TriggerManager.refreshScrollTriggers() on resize.\n */\n refresh(): void {\n if (this._config.type !== 'hover') return;\n this._snapshotRects();\n }\n\n /** Snapshot frozen rects and the current scroll offset. */\n private _snapshotRects(): void {\n this._snapshotScrollX = window.scrollX || 0;\n this._snapshotScrollY = window.scrollY || 0;\n for (const element of this._targets) {\n this._frozenRects.set(element, getLayoutRect(element));\n }\n }\n\n private _resolveTargets(): void {\n // Resolve primary targets\n this._targets = this._resolveElements(this._config.target);\n\n // Resolve secondary targets (for 2nd click) - defaults to primary targets\n if (this._config.secondTarget) {\n this._secondTargets = this._resolveElements(this._config.secondTarget);\n } else {\n this._secondTargets = this._targets;\n }\n }\n\n private _addHoverListeners(): void {\n const onEnter = this._config.onEnter || 'play';\n const onLeave = this._config.onLeave || 'reverse';\n const leaveDelay = this._config.leaveDelay || 0;\n\n // Initial rect snapshot. Seeds the scroll-offset correction and acts as a\n // fallback for the very first mousemove. While NOT hovering, the handler\n // below re-snapshots rects on every mousemove so that an unrelated\n // animation moving the target (or a parent container) between setup time\n // and first hover can't leave the hit-test box offset from where the\n // element is actually drawn.\n //\n // Once hovering, rects stay frozen to prevent a jitter loop from a\n // hover-driven translateY animation: with live bounds the browser would\n // fire mouseleave → reverse → element returns → mouseenter → play → repeat.\n this._snapshotRects();\n\n // A single window mousemove listener replaces per-element mouseenter/mouseleave.\n // Hit-testing against frozen rects keeps detection stable during animation.\n // Frozen rects are viewport-relative at snapshot time, so we compensate for\n // any scroll that happened since the snapshot by adjusting cursor coordinates.\n this._boundMouseMoveHandler = (e: MouseEvent) => {\n // Refresh the frozen rects while NOT currently hovering. This ensures\n // external layout changes that happened AFTER the initial snapshot\n // (e.g. a page-entrance timeline sliding a container from y:8 → y:0)\n // don't leave our hit-test boxes offset from the visible target.\n //\n // Once hovering, rects stay locked — the original jitter-prevention\n // guarantee. Cost is negligible: small N per trigger instance and\n // mousemove stops firing when the cursor is idle.\n if (!this._wasHovering) {\n this._snapshotRects();\n }\n\n const scrollDX = (window.scrollX || 0) - this._snapshotScrollX;\n const scrollDY = (window.scrollY || 0) - this._snapshotScrollY;\n const cx = e.clientX + scrollDX;\n const cy = e.clientY + scrollDY;\n\n // Check if cursor is inside any frozen target rect\n let isOverTarget = false;\n for (const rect of this._frozenRects.values()) {\n if (cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom) {\n isOverTarget = true;\n break;\n }\n }\n\n if (isOverTarget && !this._wasHovering) {\n // Cursor entered — cancel any pending leave and run enter action\n this._wasHovering = true;\n if (this._hoverLeaveTimeout !== null) {\n clearTimeout(this._hoverLeaveTimeout);\n this._hoverLeaveTimeout = null;\n }\n if (onEnter === 'restart') {\n // One-shot semantics: only restart when the previous run has\n // finished (or hasn't started yet). If the timeline is still\n // mid-play, let it finish naturally — don't interrupt.\n //\n // This prevents jittery cursors at the element edge from\n // stuttering the animation with repeated enter events, and it\n // stops mouseleave+re-enter from resetting the flap that is\n // already running. Pairs naturally with `onLeave: 'none'`.\n const progress = this._timeline.progress();\n if (progress >= 1 || progress === 0) {\n this._timeline.restart();\n }\n // else: still mid-run, leave it alone.\n } else {\n // Default: resume from current position (or no-op when at end).\n // Pairs with onLeave: 'reverse' for classic reversible hover.\n this._timeline.play();\n }\n } else if (!isOverTarget && this._wasHovering) {\n // Cursor left — execute leave action (with optional delay)\n this._wasHovering = false;\n if (onLeave === 'none') return;\n\n const executeLeaveAction = () => {\n this._handleLeaveAction(onLeave);\n this._hoverLeaveTimeout = null;\n };\n\n if (leaveDelay > 0) {\n this._hoverLeaveTimeout = window.setTimeout(executeLeaveAction, leaveDelay * 1000);\n } else {\n executeLeaveAction();\n }\n }\n };\n\n window.addEventListener('mousemove', this._boundMouseMoveHandler);\n }\n\n private _handleLeaveAction(action: 'reverse' | 'pause' | 'stop' | 'restart' | 'none'): void {\n // Map EventTrigger's 'stop' to the shared utility's 'reset' (same behavior: pause + progress(0))\n const mapped: TimelineAction = action === 'stop' ? 'reset' : action;\n executeTimelineAction(mapped, this._timeline);\n }\n\n private _addClickListeners(): void {\n const toggle = this._config.toggle || 'play';\n const preventDefault = this._config.preventDefault || false;\n const hasSeparateSecondTarget = this._config.secondTarget && this._config.secondTarget !== this._config.target;\n\n // Add listeners to primary targets (1st click / play)\n this._targets.forEach(element => {\n const elementListeners = this._listeners.get(element) || new Map<string, EventListener>();\n\n const clickListener = (e: Event) => {\n if (preventDefault) {\n e.preventDefault();\n }\n\n // If same element handles both clicks, use toggle state\n if (!hasSeparateSecondTarget) {\n if (this._isForward) {\n // First click - play\n this._timeline.restart();\n if (toggle !== 'play') {\n this._isForward = false;\n }\n } else {\n // Second click - reverse/restart/play based on toggle setting\n this._handleSecondClick(toggle);\n this._isForward = true;\n }\n } else {\n // Separate targets: primary always plays\n this._timeline.restart();\n }\n };\n\n element.addEventListener('click', clickListener);\n elementListeners.set('click', clickListener);\n this._listeners.set(element, elementListeners);\n });\n\n // Add listeners to secondary targets (2nd click) if different from primary\n if (hasSeparateSecondTarget) {\n this._secondTargets.forEach(element => {\n // Skip if already has listeners (overlapping selectors)\n if (this._listeners.has(element)) return;\n\n const elementListeners = new Map<string, EventListener>();\n\n const clickListener = (e: Event) => {\n if (preventDefault) {\n e.preventDefault();\n }\n this._handleSecondClick(toggle);\n };\n\n element.addEventListener('click', clickListener);\n elementListeners.set('click', clickListener);\n this._listeners.set(element, elementListeners);\n });\n }\n }\n\n private _handleSecondClick(toggle: 'reverse' | 'restart' | 'play'): void {\n executeTimelineAction(toggle, this._timeline);\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('event', EventTrigger);\n",
|
|
12
12
|
"/**\n * TransformCache - Optimized transform value storage\n *\n * Uses Float32Array for fast transform value storage per element.\n * Separate array for units (string per property).\n * Dirty flag bitmask to skip unchanged properties.\n * Cached transform string to avoid rebuilding.\n */\n\n// Transform property indices in Float32Array\n// PERSPECTIVE must be first for correct CSS transform order\nconst enum TransformIndex {\n PERSPECTIVE = 0,\n X = 1,\n Y = 2,\n Z = 3,\n ROTATION = 4,\n ROTATION_X = 5,\n ROTATION_Y = 6,\n ROTATION_Z = 7,\n SCALE = 8,\n SCALE_X = 9,\n SCALE_Y = 10,\n SCALE_Z = 11,\n SKEW_X = 12,\n SKEW_Y = 13,\n LENGTH = 14\n}\n\n// Map property names to indices\nconst PROP_TO_INDEX: Record<string, TransformIndex> = {\n perspective: TransformIndex.PERSPECTIVE,\n x: TransformIndex.X,\n y: TransformIndex.Y,\n z: TransformIndex.Z,\n rotate: TransformIndex.ROTATION,\n rotateX: TransformIndex.ROTATION_X,\n rotateY: TransformIndex.ROTATION_Y,\n rotateZ: TransformIndex.ROTATION_Z,\n scale: TransformIndex.SCALE,\n scaleX: TransformIndex.SCALE_X,\n scaleY: TransformIndex.SCALE_Y,\n scaleZ: TransformIndex.SCALE_Z,\n skewX: TransformIndex.SKEW_X,\n skewY: TransformIndex.SKEW_Y\n};\n\n// Default units per property index\nconst DEFAULT_UNITS: string[] = [\n 'px', // PERSPECTIVE\n 'px', // X\n 'px', // Y\n 'px', // Z\n 'deg', // ROTATION\n 'deg', // ROTATION_X\n 'deg', // ROTATION_Y\n 'deg', // ROTATION_Z\n '', // SCALE\n '', // SCALE_X\n '', // SCALE_Y\n '', // SCALE_Z\n 'deg', // SKEW_X\n 'deg' // SKEW_Y\n];\n\ninterface TransformData {\n values: Float32Array;\n units: string[];\n dirty: number; // Bitmask of dirty flags (cleared after build)\n used: number; // Bitmask of properties ever set (never cleared, tracks animated props)\n cachedString: string;\n pinDirty: boolean; // True when pin offset changed since last build\n}\n\n// WeakMap to store transform data per element\nconst transformCache = new WeakMap<Element, TransformData>();\n\n// Separate WeakMap for pin offsets (set by PinManager, composed into final transform string)\ninterface PinOffset {\n x: number;\n y: number;\n z: number;\n}\nconst pinOffsetCache = new WeakMap<Element, PinOffset>();\n\n// Pre-allocated array for building transform strings (avoids allocation per frame)\n// Safe to reuse since JS is single-threaded\nconst transformParts: string[] = [];\n\nconst MATRIX_EPSILON = 1e-6;\n\nfunction parseNumberWithUnit(raw: string): { value: number; unit: string } {\n const match = raw.trim().match(/([-+]?\\d*\\.?\\d+(?:e[-+]?\\d+)?)([a-z%]*)/i);\n if (!match) {\n return { value: 0, unit: '' };\n }\n return { value: parseFloat(match[1]), unit: match[2] || '' };\n}\n\nfunction toDegrees(value: number, unit: string): number {\n switch (unit) {\n case 'rad':\n return (value * 180) / Math.PI;\n case 'turn':\n return value * 360;\n case 'grad':\n return value * 0.9;\n default:\n return value;\n }\n}\n\nfunction parseAngle(raw: string): number {\n const parsed = parseNumberWithUnit(raw);\n return toDegrees(parsed.value, parsed.unit || 'deg');\n}\n\nfunction assignTransformValue(\n data: TransformData,\n property: string,\n value: number,\n unit?: string\n): void {\n const index = PROP_TO_INDEX[property];\n if (index === undefined) return;\n data.values[index] = value;\n const resolvedUnit = unit && unit.length > 0 ? unit : DEFAULT_UNITS[index] || '';\n data.units[index] = resolvedUnit;\n data.used |= (1 << index);\n}\n\nfunction parseTransformFunctions(transform: string, data: TransformData): void {\n const regex = /([a-zA-Z0-9]+)\\(([^)]+)\\)/g;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(transform)) !== null) {\n const fn = match[1];\n const args = match[2].match(/[-+]?\\d*\\.?\\d+(?:e[-+]?\\d+)?[a-z%]*/gi) || [];\n\n switch (fn) {\n case 'translate': {\n const x = args[0] ? parseNumberWithUnit(args[0]) : { value: 0, unit: '' };\n const y = args[1] ? parseNumberWithUnit(args[1]) : { value: 0, unit: '' };\n assignTransformValue(data, 'x', x.value, x.unit);\n assignTransformValue(data, 'y', y.value, y.unit);\n break;\n }\n case 'translate3d': {\n const x = args[0] ? parseNumberWithUnit(args[0]) : { value: 0, unit: '' };\n const y = args[1] ? parseNumberWithUnit(args[1]) : { value: 0, unit: '' };\n const z = args[2] ? parseNumberWithUnit(args[2]) : { value: 0, unit: '' };\n assignTransformValue(data, 'x', x.value, x.unit);\n assignTransformValue(data, 'y', y.value, y.unit);\n assignTransformValue(data, 'z', z.value, z.unit);\n break;\n }\n case 'translateX': {\n const x = args[0] ? parseNumberWithUnit(args[0]) : { value: 0, unit: '' };\n assignTransformValue(data, 'x', x.value, x.unit);\n break;\n }\n case 'translateY': {\n const y = args[0] ? parseNumberWithUnit(args[0]) : { value: 0, unit: '' };\n assignTransformValue(data, 'y', y.value, y.unit);\n break;\n }\n case 'translateZ': {\n const z = args[0] ? parseNumberWithUnit(args[0]) : { value: 0, unit: '' };\n assignTransformValue(data, 'z', z.value, z.unit);\n break;\n }\n case 'scale': {\n const x = args[0] ? parseFloat(args[0]) : 1;\n const y = args[1] ? parseFloat(args[1]) : x;\n assignTransformValue(data, 'scaleX', x);\n assignTransformValue(data, 'scaleY', y);\n break;\n }\n case 'scale3d': {\n const x = args[0] ? parseFloat(args[0]) : 1;\n const y = args[1] ? parseFloat(args[1]) : 1;\n const z = args[2] ? parseFloat(args[2]) : 1;\n assignTransformValue(data, 'scaleX', x);\n assignTransformValue(data, 'scaleY', y);\n assignTransformValue(data, 'scaleZ', z);\n break;\n }\n case 'scaleX': {\n const x = args[0] ? parseFloat(args[0]) : 1;\n assignTransformValue(data, 'scaleX', x);\n break;\n }\n case 'scaleY': {\n const y = args[0] ? parseFloat(args[0]) : 1;\n assignTransformValue(data, 'scaleY', y);\n break;\n }\n case 'scaleZ': {\n const z = args[0] ? parseFloat(args[0]) : 1;\n assignTransformValue(data, 'scaleZ', z);\n break;\n }\n case 'rotate':\n assignTransformValue(data, 'rotate', args[0] ? parseAngle(args[0]) : 0, 'deg');\n break;\n case 'rotateX':\n assignTransformValue(data, 'rotateX', args[0] ? parseAngle(args[0]) : 0, 'deg');\n break;\n case 'rotateY':\n assignTransformValue(data, 'rotateY', args[0] ? parseAngle(args[0]) : 0, 'deg');\n break;\n case 'rotateZ':\n assignTransformValue(data, 'rotateZ', args[0] ? parseAngle(args[0]) : 0, 'deg');\n break;\n case 'skew': {\n const x = args[0] ? parseAngle(args[0]) : 0;\n const y = args[1] ? parseAngle(args[1]) : 0;\n assignTransformValue(data, 'skewX', x, 'deg');\n assignTransformValue(data, 'skewY', y, 'deg');\n break;\n }\n case 'skewX':\n assignTransformValue(data, 'skewX', args[0] ? parseAngle(args[0]) : 0, 'deg');\n break;\n case 'skewY':\n assignTransformValue(data, 'skewY', args[0] ? parseAngle(args[0]) : 0, 'deg');\n break;\n case 'perspective': {\n const perspective = args[0] ? parseNumberWithUnit(args[0]) : { value: 0, unit: '' };\n assignTransformValue(data, 'perspective', perspective.value, perspective.unit);\n break;\n }\n default:\n break;\n }\n }\n}\n\nfunction decomposeMatrix(\n m11: number,\n m12: number,\n m13: number,\n m21: number,\n m22: number,\n m23: number,\n m31: number,\n m32: number,\n m33: number,\n m41: number,\n m42: number,\n m43: number,\n is2D: boolean,\n data: TransformData\n): void {\n if (is2D) {\n // 2D decomposition: extract rotation + skew from top-left 2x2 (m11,m12,m21,m22)\n const scaleX = Math.hypot(m11, m12);\n const determinant = m11 * m22 - m12 * m21;\n const scaleY = scaleX !== 0 ? determinant / scaleX : 0;\n\n const rotation = scaleX !== 0 ? Math.atan2(m12, m11) : 0;\n const skewX = scaleX !== 0 ? Math.atan2(m11 * m21 + m12 * m22, scaleX * scaleX) : 0;\n\n assignTransformValue(data, 'x', m41, 'px');\n assignTransformValue(data, 'y', m42, 'px');\n assignTransformValue(data, 'scaleX', scaleX || 1);\n assignTransformValue(data, 'scaleY', scaleY || 1);\n assignTransformValue(data, 'rotate', toDegrees(rotation, 'rad'), 'deg');\n if (Math.abs(skewX) > MATRIX_EPSILON) {\n assignTransformValue(data, 'skewX', toDegrees(skewX, 'rad'), 'deg');\n }\n return;\n }\n\n // 3D decomposition: extract Euler angles from rotation matrix\n const scaleX = Math.hypot(m11, m12, m13) || 1;\n const scaleY = Math.hypot(m21, m22, m23) || 1;\n const scaleZ = Math.hypot(m31, m32, m33) || 1;\n\n const rm11 = m11 / scaleX;\n const rm12 = m12 / scaleX;\n const rm13 = m13 / scaleX;\n const rm21 = m21 / scaleY;\n const rm22 = m22 / scaleY;\n const rm23 = m23 / scaleY;\n const rm31 = m31 / scaleZ;\n const rm32 = m32 / scaleZ;\n const rm33 = m33 / scaleZ;\n\n const sy = Math.sqrt(rm11 * rm11 + rm21 * rm21);\n const singular = sy < MATRIX_EPSILON;\n\n let rotateX = 0;\n let rotateY = 0;\n let rotateZ = 0;\n\n if (!singular) {\n rotateX = Math.atan2(rm32, rm33);\n rotateY = Math.atan2(-rm31, sy);\n rotateZ = Math.atan2(rm21, rm11);\n } else {\n rotateX = Math.atan2(-rm23, rm22);\n rotateY = Math.atan2(-rm31, sy);\n rotateZ = 0;\n }\n\n assignTransformValue(data, 'x', m41, 'px');\n assignTransformValue(data, 'y', m42, 'px');\n assignTransformValue(data, 'z', m43, 'px');\n assignTransformValue(data, 'scaleX', scaleX);\n assignTransformValue(data, 'scaleY', scaleY);\n assignTransformValue(data, 'scaleZ', scaleZ);\n assignTransformValue(data, 'rotateX', toDegrees(rotateX, 'rad'), 'deg');\n assignTransformValue(data, 'rotateY', toDegrees(rotateY, 'rad'), 'deg');\n assignTransformValue(data, 'rotateZ', toDegrees(rotateZ, 'rad'), 'deg');\n}\n\nfunction parseComputedTransform(transform: string, data: TransformData): void {\n if (!transform || transform === 'none') return;\n\n if (typeof DOMMatrixReadOnly !== 'undefined') {\n const matrix = new DOMMatrixReadOnly(transform);\n decomposeMatrix(\n matrix.m11, matrix.m12, matrix.m13,\n matrix.m21, matrix.m22, matrix.m23,\n matrix.m31, matrix.m32, matrix.m33,\n matrix.m41, matrix.m42, matrix.m43,\n matrix.is2D, data\n );\n return;\n }\n\n const values = transform.match(/[-+]?\\d*\\.?\\d+(?:e[-+]?\\d+)?/gi) || [];\n\n if (transform.startsWith('matrix3d') && values.length >= 16) {\n const nums = values.map(Number);\n decomposeMatrix(\n nums[0], nums[1], nums[2],\n nums[4], nums[5], nums[6],\n nums[8], nums[9], nums[10],\n nums[12], nums[13], nums[14],\n false, data\n );\n return;\n }\n\n if (transform.startsWith('matrix') && values.length >= 6) {\n const nums = values.map(Number);\n decomposeMatrix(\n nums[0], nums[1], 0,\n nums[2], nums[3], 0,\n 0, 0, 1,\n nums[4], nums[5], 0,\n true, data\n );\n }\n}\n\nfunction hydrateTransformData(element: Element, data: TransformData): void {\n if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) return;\n\n const htmlEl = element as HTMLElement;\n const inlineTransform = htmlEl.style.transform;\n\n if (inlineTransform && inlineTransform !== 'none') {\n parseTransformFunctions(inlineTransform, data);\n data.cachedString = inlineTransform;\n return;\n }\n\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(element);\n const computedTransform = computed.transform || computed.getPropertyValue('transform');\n if (computedTransform && computedTransform !== 'none') {\n parseComputedTransform(computedTransform, data);\n data.cachedString = computedTransform;\n }\n }\n } catch {\n // Ignore computed style failures (e.g., test environment)\n }\n}\n\n/**\n * Get or create transform data for an element\n */\nexport function getTransformData(element: Element): TransformData {\n let data = transformCache.get(element);\n\n if (!data) {\n // Initialize with default values\n const values = new Float32Array(TransformIndex.LENGTH);\n values[TransformIndex.SCALE] = 1;\n values[TransformIndex.SCALE_X] = 1;\n values[TransformIndex.SCALE_Y] = 1;\n values[TransformIndex.SCALE_Z] = 1;\n\n data = {\n values,\n units: [...DEFAULT_UNITS],\n dirty: 0,\n used: 0,\n cachedString: '',\n pinDirty: false\n };\n\n hydrateTransformData(element, data);\n\n transformCache.set(element, data);\n }\n\n return data;\n}\n\n/**\n * Set a transform value and unit, mark as dirty\n */\nexport function setTransformValue(\n element: Element,\n property: string,\n value: number,\n unit?: string\n): void {\n const data = getTransformData(element);\n const index = PROP_TO_INDEX[property];\n\n if (index !== undefined) {\n data.values[index] = value;\n if (unit !== undefined) {\n data.units[index] = unit;\n }\n data.used |= (1 << index);\n\n if (property === 'scale') {\n data.values[TransformIndex.SCALE_X] = value;\n data.values[TransformIndex.SCALE_Y] = value;\n data.dirty |= (1 << TransformIndex.SCALE_X) | (1 << TransformIndex.SCALE_Y);\n } else if (property === 'scaleX' || property === 'scaleY') {\n data.values[TransformIndex.SCALE] = 1;\n data.dirty |= (1 << TransformIndex.SCALE);\n }\n\n if (property === 'rotate') {\n data.values[TransformIndex.ROTATION_Z] = 0;\n data.dirty |= (1 << TransformIndex.ROTATION_Z);\n } else if (property === 'rotateZ') {\n data.values[TransformIndex.ROTATION] = 0;\n data.dirty |= (1 << TransformIndex.ROTATION);\n }\n data.dirty |= (1 << index); // Set dirty bit\n }\n}\n\n/**\n * Get a transform value\n */\nexport function getTransformValue(element: Element, property: string): number {\n const data = getTransformData(element);\n const index = PROP_TO_INDEX[property];\n if (index === undefined) {\n return property.startsWith('scale') ? 1 : 0;\n }\n\n if (property === 'scale') {\n if (data.values[index] !== 1) {\n return data.values[index];\n }\n const scaleX = data.values[TransformIndex.SCALE_X];\n const scaleY = data.values[TransformIndex.SCALE_Y];\n if (Math.abs(scaleX - scaleY) < MATRIX_EPSILON && scaleX !== 1) {\n return scaleX;\n }\n return data.values[index];\n }\n\n if (property === 'rotate') {\n if (data.values[index] !== 0) {\n return data.values[index];\n }\n const rotateZ = data.values[TransformIndex.ROTATION_Z];\n if (\n rotateZ !== 0 &&\n data.values[TransformIndex.ROTATION_X] === 0 &&\n data.values[TransformIndex.ROTATION_Y] === 0\n ) {\n return rotateZ;\n }\n return data.values[index];\n }\n\n return data.values[index];\n}\n\n/**\n * Get a transform unit\n */\nexport function getTransformUnit(element: Element, property: string): string {\n const data = getTransformData(element);\n const index = PROP_TO_INDEX[property];\n if (index === undefined) {\n return '';\n }\n return data.units[index] || DEFAULT_UNITS[index] || '';\n}\n\n/**\n * Format a single length value for a CSS translate function.\n * Emits bare \"0\" (no unit) for zero values, which is valid CSS and matches the\n * format historically written by PinManager (e.g. translate3d(0, 400px, 0)).\n */\nfunction formatPinLength(v: number): string {\n return v === 0 ? '0' : `${Math.round(v)}px`;\n}\n\n/**\n * Build the translate3d string for a pin offset.\n * Zero components are emitted without a unit (`0`) so the output matches the\n * format that tests and the existing PinManager code previously produced.\n */\nfunction buildPinTranslate(x: number, y: number, z: number): string {\n return `translate3d(${formatPinLength(x)}, ${formatPinLength(y)}, ${formatPinLength(z)})`;\n}\n\n/**\n * Register a pin offset for an element.\n * The pin offset is prepended to the animation transform in buildTransformString().\n * Call this instead of writing directly to element.style.transform from PinManager\n * so that both pin position and animation values are composed into a single write.\n */\nexport function setPinOffset(element: Element, x: number, y: number, z: number): void {\n pinOffsetCache.set(element, { x, y, z });\n // Mark the cached transform string as stale so the next build includes the new offset\n const data = transformCache.get(element);\n if (data) {\n data.pinDirty = true;\n }\n}\n\n/**\n * Remove the pin offset for an element (call when element exits pin zone).\n * Marks the cached transform string as stale so the next build omits the offset.\n */\nexport function clearPinOffset(element: Element): void {\n const hadOffset = pinOffsetCache.has(element);\n pinOffsetCache.delete(element);\n if (hadOffset) {\n const data = transformCache.get(element);\n if (data) {\n data.pinDirty = true;\n }\n }\n}\n\n/**\n * Build the CSS transform string from cached values\n */\nexport function buildTransformString(element: Element): string {\n const data = transformCache.get(element);\n const pinOffset = pinOffsetCache.get(element);\n\n // When there is no animation data at all, return just the pin offset (or nothing)\n if (!data) {\n if (pinOffset && (pinOffset.x !== 0 || pinOffset.y !== 0 || pinOffset.z !== 0)) {\n return buildPinTranslate(pinOffset.x, pinOffset.y, pinOffset.z);\n }\n return '';\n }\n\n // If neither animation nor pin offset changed, return the cached composite string\n if (data.dirty === 0 && !data.pinDirty && data.cachedString) {\n return data.cachedString;\n }\n\n const values = data.values;\n const units = data.units;\n\n // Clear pre-allocated array without allocation\n transformParts.length = 0;\n\n // Build transform string in optimal order\n // perspective -> translate -> rotate -> scale -> skew\n //\n // IMPORTANT: If a property was animated (tracked in `used` bitmask), we must\n // always emit it even at default value. Otherwise the inline style becomes \"\"\n // and stylesheet CSS snaps back.\n const used = data.used;\n\n // Perspective (must be first for 3D context)\n // NOTE: perspective(0px) causes extreme visual distortion (viewer at zero distance),\n // so we intentionally do NOT emit perspective at 0 even if it was animated.\n const perspectiveVal = values[TransformIndex.PERSPECTIVE];\n if (perspectiveVal !== 0) {\n const perspectiveUnit = units[TransformIndex.PERSPECTIVE];\n transformParts.push(`perspective(${perspectiveVal}${perspectiveUnit})`);\n }\n\n // Translate - handle mixed units by using separate translateX/Y/Z\n const xVal = values[TransformIndex.X];\n const yVal = values[TransformIndex.Y];\n const zVal = values[TransformIndex.Z];\n const xUnit = units[TransformIndex.X];\n const yUnit = units[TransformIndex.Y];\n const zUnit = units[TransformIndex.Z];\n const xUsed = used & (1 << TransformIndex.X);\n const yUsed = used & (1 << TransformIndex.Y);\n const zUsed = used & (1 << TransformIndex.Z);\n\n if (xVal !== 0 || yVal !== 0 || zVal !== 0 || xUsed || yUsed || zUsed) {\n // Check if all units match for potential translate3d optimization\n const allSameUnit = xUnit === yUnit && ((zVal === 0 && !zUsed) || yUnit === zUnit);\n\n if (zVal !== 0 || zUsed) {\n if (allSameUnit) {\n transformParts.push(`translate3d(${xVal}${xUnit}, ${yVal}${yUnit}, ${zVal}${zUnit})`);\n } else {\n if (xVal !== 0 || xUsed) transformParts.push(`translateX(${xVal}${xUnit})`);\n if (yVal !== 0 || yUsed) transformParts.push(`translateY(${yVal}${yUnit})`);\n transformParts.push(`translateZ(${zVal}${zUnit})`);\n }\n } else if (allSameUnit && (xVal !== 0 || xUsed) && (yVal !== 0 || yUsed)) {\n transformParts.push(`translate(${xVal}${xUnit}, ${yVal}${yUnit})`);\n } else {\n if (xVal !== 0 || xUsed) transformParts.push(`translateX(${xVal}${xUnit})`);\n if (yVal !== 0 || yUsed) transformParts.push(`translateY(${yVal}${yUnit})`);\n }\n }\n\n // Rotate\n if (values[TransformIndex.ROTATION] !== 0 || (used & (1 << TransformIndex.ROTATION))) {\n transformParts.push(`rotate(${values[TransformIndex.ROTATION]}deg)`);\n }\n if (values[TransformIndex.ROTATION_X] !== 0 || (used & (1 << TransformIndex.ROTATION_X))) {\n transformParts.push(`rotateX(${values[TransformIndex.ROTATION_X]}deg)`);\n }\n if (values[TransformIndex.ROTATION_Y] !== 0 || (used & (1 << TransformIndex.ROTATION_Y))) {\n transformParts.push(`rotateY(${values[TransformIndex.ROTATION_Y]}deg)`);\n }\n if (values[TransformIndex.ROTATION_Z] !== 0 || (used & (1 << TransformIndex.ROTATION_Z))) {\n transformParts.push(`rotateZ(${values[TransformIndex.ROTATION_Z]}deg)`);\n }\n\n // Scale\n const scaleUsed = used & (1 << TransformIndex.SCALE);\n const scaleXUsed = used & (1 << TransformIndex.SCALE_X);\n const scaleYUsed = used & (1 << TransformIndex.SCALE_Y);\n if (values[TransformIndex.SCALE] !== 1 || scaleUsed) {\n transformParts.push(`scale(${values[TransformIndex.SCALE]})`);\n } else if (\n values[TransformIndex.SCALE_X] !== 1 ||\n values[TransformIndex.SCALE_Y] !== 1 ||\n scaleXUsed || scaleYUsed\n ) {\n transformParts.push(`scaleX(${values[TransformIndex.SCALE_X]})`);\n transformParts.push(`scaleY(${values[TransformIndex.SCALE_Y]})`);\n }\n if (values[TransformIndex.SCALE_Z] !== 1 || (used & (1 << TransformIndex.SCALE_Z))) {\n transformParts.push(`scaleZ(${values[TransformIndex.SCALE_Z]})`);\n }\n\n // Skew\n if (values[TransformIndex.SKEW_X] !== 0 || (used & (1 << TransformIndex.SKEW_X))) {\n transformParts.push(`skewX(${values[TransformIndex.SKEW_X]}deg)`);\n }\n if (values[TransformIndex.SKEW_Y] !== 0 || (used & (1 << TransformIndex.SKEW_Y))) {\n transformParts.push(`skewY(${values[TransformIndex.SKEW_Y]}deg)`);\n }\n\n // Compose animation parts with pin offset (pin offset is prepended so it acts as\n // the base-position shift; animation transforms apply on top of it)\n const animStr = transformParts.join(' ');\n if (pinOffset && (pinOffset.x !== 0 || pinOffset.y !== 0 || pinOffset.z !== 0)) {\n const pinStr = buildPinTranslate(pinOffset.x, pinOffset.y, pinOffset.z);\n data.cachedString = animStr ? `${pinStr} ${animStr}` : pinStr;\n } else {\n data.cachedString = animStr;\n }\n\n data.dirty = 0; // Clear animation dirty flags\n data.pinDirty = false; // Clear pin dirty flag\n\n return data.cachedString;\n}\n\n/**\n * Clear transform cache for an element\n * @param clearInlineStyle - If true, also clears element's inline transform to prevent hydration from reading stale values\n */\nexport function clearTransformCache(element: Element, clearInlineStyle: boolean = false): void {\n transformCache.delete(element);\n // Optionally clear inline transform so hydration doesn't read stale rotation/position\n if (clearInlineStyle && element instanceof HTMLElement) {\n element.style.transform = '';\n }\n}\n",
|
|
13
13
|
"/**\n * RenderBatch - Batched DOM write system\n *\n * Collects all property changes during a frame and flushes them\n * in a single batch at frame end. This eliminates layout thrashing\n * and reduces DOM writes from O(properties) to O(elements).\n *\n * Key optimizations:\n * - One transform string build per element (not per property)\n * - One DOM write per element for transforms\n * - Batched CSS property writes\n * - Zero allocations during animation (uses Sets/Maps)\n */\n\nimport { buildTransformString } from './TransformCache';\n\n// Elements with pending transform changes\nconst pendingTransforms: Set<Element> = new Set();\n\n// Elements with pending CSS property changes: Element -> Map<property, value>\nconst pendingStyles: Map<Element, Map<string, string>> = new Map();\n\n// Track if we have pending work\nlet hasPendingWork = false;\n\n/**\n * Queue a transform update for an element\n * Called after setTransformValue updates the cache\n */\nexport function queueTransform(element: Element): void {\n pendingTransforms.add(element);\n hasPendingWork = true;\n}\n\n/**\n * Queue a CSS property update for an element\n */\nexport function queueStyle(element: Element, property: string, value: string): void {\n let elementStyles = pendingStyles.get(element);\n if (!elementStyles) {\n elementStyles = new Map();\n pendingStyles.set(element, elementStyles);\n }\n elementStyles.set(property, value);\n hasPendingWork = true;\n}\n\n/**\n * Flush all pending DOM writes\n * Called once at end of each animation frame\n */\nexport function flush(): void {\n if (!hasPendingWork) return;\n\n // 1. Flush all transform updates (one DOM write per element)\n for (const element of pendingTransforms) {\n const htmlElement = element as HTMLElement;\n if (htmlElement.style) {\n // buildTransformString uses the dirty flag to optimize string building\n const transformString = buildTransformString(element);\n htmlElement.style.transform = transformString;\n }\n }\n pendingTransforms.clear();\n\n // 2. Flush all CSS property updates\n for (const [element, properties] of pendingStyles) {\n const htmlElement = element as HTMLElement;\n if (htmlElement.style) {\n for (const [property, value] of properties) {\n try {\n if (property.startsWith('--')) {\n // CSS variables use setProperty with raw name\n htmlElement.style.setProperty(property, value);\n } else {\n (htmlElement.style as CSSStyleDeclaration & Record<string, string>)[property] = value;\n }\n } catch {\n // Silently fail for invalid properties\n }\n }\n }\n }\n pendingStyles.clear();\n\n hasPendingWork = false;\n}\n",
|
|
14
14
|
"/**\n * Ticker Singleton - Single RAF loop for all animations\n *\n * Manages the requestAnimationFrame loop with:\n * - Lag smoothing (cap delta when tab backgrounded)\n * - Priority-based listener ordering\n * - Automatic start/stop based on listener count\n * - Batched DOM writes at end of frame via RenderBatch\n */\n\nimport { flush as flushRenderBatch } from '../render/RenderBatch';\n\n// Module-level inTick flag for fast access (avoids getInstance() calls in hot path)\nlet _inTick = false;\n\n/**\n * Fast check if we're inside a tick (exported for hot path performance)\n * Use this instead of Ticker.getInstance().isInTick() in render code\n */\nexport function isInTick(): boolean {\n return _inTick;\n}\n\n/**\n * Run a callback in \"tick mode\" — writes are batched, then flushed at the end.\n *\n * Use this when updating timeline progress from outside the RAF loop\n * (e.g. scroll event handlers) to avoid layout thrashing from immediate\n * per-property DOM writes. The callback's DOM mutations are queued via\n * RenderBatch and flushed in a single pass after the callback returns.\n */\nexport function runBatched(callback: () => void): void {\n if (_inTick) {\n // Already inside a tick — just run directly, flush will happen at frame end\n callback();\n return;\n }\n\n _inTick = true;\n try {\n callback();\n flushRenderBatch();\n } finally {\n _inTick = false;\n }\n}\n\ntype TickerListener = (deltaTime: number, elapsedTime: number) => void;\n\ninterface Listener {\n callback: TickerListener;\n priority: number;\n}\n\nexport class Ticker {\n private static instance: Ticker;\n\n private listeners: Listener[] = [];\n private _listenersDirty: boolean = true;\n private _listenersSnapshot: Listener[] = [];\n private _isRunning: boolean = false;\n private rafId: number | null = null;\n private lastTime: number = 0;\n private elapsedTime: number = 0;\n private lagThreshold: number = 500; // Max delta in ms (0.5 seconds)\n private maxDelta: number = 33; // Cap delta at ~30fps when lagging\n\n // FPS tracking\n private fpsFrames: number = 0;\n private fpsTime: number = 0;\n private currentFPS: number = 60;\n private lastFrameTime: number = 0;\n\n private constructor() {\n // Private constructor for singleton\n }\n\n /**\n * Get the singleton Ticker instance\n */\n static getInstance(): Ticker {\n if (!Ticker.instance) {\n Ticker.instance = new Ticker();\n }\n return Ticker.instance;\n }\n\n /**\n * Add a listener to the ticker\n * @param callback Function to call on each tick\n * @param priority Higher priority callbacks execute first (default: 0)\n */\n add(callback: TickerListener, priority: number = 0): void {\n // Check if callback already exists\n const existingIndex = this.listeners.findIndex(l => l.callback === callback);\n if (existingIndex !== -1) {\n // Update priority if already exists\n this.listeners[existingIndex].priority = priority;\n } else {\n this.listeners.push({ callback, priority });\n }\n\n // Sort by priority (higher priority first)\n this.listeners.sort((a, b) => b.priority - a.priority);\n this._listenersDirty = true;\n\n // Start RAF loop if this is the first listener\n if (!this._isRunning && this.listeners.length > 0) {\n this.start();\n }\n }\n\n /**\n * Remove a listener from the ticker\n * @param callback Function to remove\n */\n remove(callback: TickerListener): void {\n const index = this.listeners.findIndex(l => l.callback === callback);\n if (index !== -1) {\n this.listeners.splice(index, 1);\n this._listenersDirty = true;\n }\n\n // Stop RAF loop if no more listeners\n if (this.listeners.length === 0 && this._isRunning) {\n this.stop();\n }\n }\n\n /**\n * Start the RAF loop\n */\n private start(): void {\n if (this._isRunning) return;\n\n // SSR / Node guard: requestAnimationFrame is not available outside browsers\n if (typeof requestAnimationFrame === 'undefined') {\n this._isRunning = false;\n return;\n }\n\n this._isRunning = true;\n this.lastTime = performance.now();\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n /**\n * Stop the RAF loop\n */\n private stop(): void {\n if (!this._isRunning) return;\n\n this._isRunning = false;\n if (this.rafId !== null && typeof cancelAnimationFrame !== 'undefined') {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n /**\n * Pause the ticker (for debug purposes)\n */\n pause(): void {\n this.stop();\n }\n\n /**\n * Resume the ticker (for debug purposes)\n */\n resume(): void {\n if (this.listeners.length > 0) {\n this.start();\n }\n }\n\n /**\n * Main tick function called by RAF\n */\n private tick = (): void => {\n if (!this._isRunning) return;\n\n const currentTime = performance.now();\n let delta = currentTime - this.lastTime;\n\n // Apply lag smoothing - cap delta when tab was backgrounded\n if (delta > this.lagThreshold) {\n // Tab was likely backgrounded, use maxDelta instead\n delta = this.maxDelta;\n this.lastTime = currentTime - delta; // Adjust lastTime\n } else {\n this.lastTime = currentTime;\n }\n\n // Convert to seconds\n const deltaSeconds = delta / 1000;\n this.elapsedTime += deltaSeconds;\n this.lastFrameTime = delta;\n\n // Update FPS tracking\n this.updateFPS(delta);\n\n // Mark that we're inside a tick - renders will queue instead of write immediately\n _inTick = true;\n\n // Call all listeners in priority order\n // Use copy-on-write snapshot: only rebuild when listeners change, not every frame\n if (this._listenersDirty) {\n this._listenersSnapshot = [...this.listeners];\n this._listenersDirty = false;\n }\n\n for (const listener of this._listenersSnapshot) {\n try {\n listener.callback(deltaSeconds, this.elapsedTime);\n } catch (e) {\n console.error('[Motion] Uncaught error in animation callback:', e);\n }\n }\n\n // Flush all batched DOM writes at end of frame\n try {\n flushRenderBatch();\n } catch (e) {\n console.error('[Motion] Uncaught error in render batch flush:', e);\n }\n\n // Mark tick complete - subsequent renders will write immediately\n _inTick = false;\n\n // Schedule next tick (guard for SSR safety — tick should only run in browser, but be defensive)\n if (typeof requestAnimationFrame !== 'undefined') {\n this.rafId = requestAnimationFrame(this.tick);\n }\n };\n\n /**\n * Update FPS calculation\n */\n private updateFPS(delta: number): void {\n this.fpsFrames++;\n this.fpsTime += delta;\n\n // Update FPS every second\n if (this.fpsTime >= 1000) {\n this.currentFPS = Math.round((this.fpsFrames * 1000) / this.fpsTime);\n this.fpsFrames = 0;\n this.fpsTime = 0;\n }\n }\n\n /**\n * Get current FPS\n */\n getFPS(): number {\n return this.currentFPS;\n }\n\n /**\n * Get last frame delta time in ms\n */\n getLastDelta(): number {\n return this.lastFrameTime;\n }\n\n /**\n * Check if ticker is running\n */\n isRunning(): boolean {\n return this._isRunning;\n }\n\n /**\n * Check if we're currently inside a tick\n * Used by render system to decide: queue (in tick) or write immediately (outside tick)\n * Note: For hot path performance, use the exported isInTick() function instead\n */\n isInTick(): boolean {\n return _inTick;\n }\n\n /**\n * Get listener count\n */\n getListenerCount(): number {\n return this.listeners.length;\n }\n\n /**\n * Remove all listeners (for testing)\n */\n removeAll(): void {\n this.listeners = [];\n this.stop();\n }\n}\n",
|
|
15
15
|
"/**\n * PathParser - Parse and animate elements along SVG paths\n *\n * Handles:\n * - CSS selector to existing <path> element\n * - Direct Element reference\n * - Raw SVG path data string (starting with M/m)\n * - Position and angle calculation along path\n * - Alignment offset calculation\n */\n\n/**\n * Point on a path with position and angle\n */\nexport interface PathPoint {\n x: number;\n y: number;\n angle: number; // rotation in degrees\n}\n\n/**\n * Parsed path data ready for animation\n */\nexport interface ParsedPathData {\n pathData: string;\n length: number;\n}\n\n// Hidden SVG elements for path calculations (created lazily)\nlet hiddenSVG: SVGSVGElement | null = null;\nlet hiddenPath: SVGPathElement | null = null;\n\n// Cache for path lengths (path data string -> length)\n// Uses simple LRU eviction when cache exceeds max size\nconst PATH_CACHE_MAX_SIZE = 100;\nconst pathLengthCache = new Map<string, number>();\n\n/**\n * Get from cache with LRU update (move to end)\n */\nfunction cacheGet(key: string): number | undefined {\n const value = pathLengthCache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used)\n pathLengthCache.delete(key);\n pathLengthCache.set(key, value);\n }\n return value;\n}\n\n/**\n * Set cache with LRU eviction\n */\nfunction cacheSet(key: string, value: number): void {\n // If key exists, delete first to update position\n if (pathLengthCache.has(key)) {\n pathLengthCache.delete(key);\n }\n // Evict oldest entries if at capacity\n while (pathLengthCache.size >= PATH_CACHE_MAX_SIZE) {\n const firstKey = pathLengthCache.keys().next().value;\n if (firstKey !== undefined) {\n pathLengthCache.delete(firstKey);\n } else {\n break;\n }\n }\n pathLengthCache.set(key, value);\n}\n\n/**\n * Check if a string is raw SVG path data (starts with M or m command)\n */\nexport function isPathData(value: string): boolean {\n return /^\\s*[Mm]/.test(value);\n}\n\n/**\n * Get or create the hidden SVG element for path calculations\n * \n * Note: This uses a single shared path element. This is safe because:\n * 1. JavaScript is single-threaded\n * 2. Each getPointAtProgress call sets path data immediately before use\n * 3. LUT building loops are synchronous (no await between iterations)\n */\nfunction getHiddenPath(): SVGPathElement {\n if (hiddenPath) return hiddenPath;\n\n // Check if we're in a browser environment\n if (typeof document === 'undefined') {\n throw new Error('PathParser requires a browser environment');\n }\n\n // Create hidden SVG container\n hiddenSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n hiddenSVG.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;visibility:hidden;';\n hiddenSVG.setAttribute('aria-hidden', 'true');\n\n // Create path element\n hiddenPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n hiddenSVG.appendChild(hiddenPath);\n\n // Append to document\n document.body.appendChild(hiddenSVG);\n\n return hiddenPath;\n}\n\n/**\n * Resolve path target to path data string\n * \n * @param target - CSS selector, Element, or raw path data\n * @returns Path data string or null if not found\n */\nexport function resolvePathData(target: string | Element): string | null {\n // Direct Element reference\n if (target instanceof Element) {\n return target.getAttribute('d');\n }\n\n // String - could be path data or selector\n if (typeof target === 'string') {\n // Check if it's raw path data\n if (isPathData(target)) {\n return target;\n }\n\n // Try as CSS selector\n if (typeof document !== 'undefined') {\n const el = document.querySelector(target);\n if (el) {\n return el.getAttribute('d');\n }\n }\n }\n\n return null;\n}\n\n/**\n * Get the total length of a path\n * Results are cached per path data string\n */\nexport function getPathLength(pathData: string): number {\n // Check cache first (with LRU update)\n const cached = cacheGet(pathData);\n if (cached !== undefined) {\n return cached;\n }\n\n try {\n const path = getHiddenPath();\n path.setAttribute('d', pathData);\n const length = path.getTotalLength();\n cacheSet(pathData, length);\n return length;\n } catch {\n return 0;\n }\n}\n\n/**\n * Clear the path length cache\n * Call when path data changes dynamically\n */\nexport function clearPathLengthCache(): void {\n pathLengthCache.clear();\n}\n\n/**\n * Get a point on the path at a given progress (0-1)\n * \n * @param pathData - SVG path data string\n * @param progress - Position on path (0-1)\n * @param calculateAngle - Whether to calculate rotation angle\n * @returns Point with x, y, and angle\n */\nexport function getPointAtProgress(\n pathData: string,\n progress: number,\n calculateAngle: boolean = false\n): PathPoint {\n try {\n const path = getHiddenPath();\n path.setAttribute('d', pathData);\n\n const length = path.getTotalLength();\n const clampedProgress = Math.max(0, Math.min(1, progress));\n const distance = length * clampedProgress;\n\n const point = path.getPointAtLength(distance);\n\n let angle = 0;\n if (calculateAngle) {\n angle = getAngleAtLength(path, distance, length);\n }\n\n return {\n x: point.x,\n y: point.y,\n angle,\n };\n } catch {\n return { x: 0, y: 0, angle: 0 };\n }\n}\n\n/**\n * Calculate the tangent angle at a point on the path\n * Uses finite difference method with two close points\n */\nfunction getAngleAtLength(\n path: SVGPathElement,\n distance: number,\n totalLength: number\n): number {\n const delta = Math.min(0.1, totalLength * 0.001); // Small offset, proportional to path length\n\n // Get two points close together\n const d1 = Math.max(0, distance - delta);\n const d2 = Math.min(totalLength, distance + delta);\n\n const p1 = path.getPointAtLength(d1);\n const p2 = path.getPointAtLength(d2);\n\n // Calculate angle from the difference\n const dx = p2.x - p1.x;\n const dy = p2.y - p1.y;\n\n // Convert to degrees\n return Math.atan2(dy, dx) * (180 / Math.PI);\n}\n\n/**\n * Calculate the offset for the animated element itself\n * This is used to position the element's center (or specified origin) on the path\n * \n * @param element - The element being animated (selector, Element, or undefined)\n * @param alignAt - Origin point as [x%, y%], default [50, 50] (center)\n * @returns Offset to subtract from path position\n */\nexport function calculateElementOffset(\n element: string | Element | undefined,\n alignAt: [number, number] = [50, 50]\n): { x: number; y: number } {\n if (!element) {\n return { x: 0, y: 0 };\n }\n\n let el: Element | null = null;\n\n if (element instanceof Element) {\n el = element;\n } else if (typeof element === 'string' && typeof document !== 'undefined') {\n el = document.querySelector(element);\n }\n\n if (!el || typeof el.getBoundingClientRect !== 'function') {\n return { x: 0, y: 0 };\n }\n\n const rect = el.getBoundingClientRect();\n\n // Calculate offset based on alignAt percentages relative to element size\n const offsetX = (rect.width * alignAt[0]) / 100;\n const offsetY = (rect.height * alignAt[1]) / 100;\n\n return { x: offsetX, y: offsetY };\n}\n\n/**\n * Calculate offset to position path relative to an align element\n * The path's first point (M command) will be positioned at the align element's center\n * \n * @param align - Element or selector to align the path to\n * @param pathData - SVG path data string\n * @param animatedElement - The element being animated (to calculate relative offset)\n * @returns Offset to add to path coordinates\n */\nexport function calculatePathAlignOffset(\n align: string | Element | undefined,\n pathData: string,\n animatedElement?: Element\n): { x: number; y: number } {\n if (!align) {\n return { x: 0, y: 0 };\n }\n\n // Get align element\n let alignEl: Element | null = null;\n if (align instanceof Element) {\n alignEl = align;\n } else if (typeof align === 'string' && typeof document !== 'undefined') {\n alignEl = document.querySelector(align);\n }\n\n if (!alignEl) {\n return { x: 0, y: 0 };\n }\n\n // Get the first point of the path (start position)\n const firstPoint = getPointAtProgress(pathData, 0, false);\n\n // If we have the animated element, calculate relative offset\n if (animatedElement && animatedElement instanceof HTMLElement) {\n // Get element's original position (without animation transforms)\n // Only clear transforms if element has them - avoids unnecessary reflows\n const currentTransform = animatedElement.style.transform;\n const hasTransform = currentTransform && currentTransform !== 'none' && currentTransform !== '';\n \n if (hasTransform) {\n animatedElement.style.transform = 'none';\n }\n \n const animatedRect = animatedElement.getBoundingClientRect();\n const alignRect = alignEl.getBoundingClientRect();\n \n if (hasTransform) {\n animatedElement.style.transform = currentTransform;\n }\n \n // Calculate offset from animated element's original position to align element's center\n const relativeX = (alignRect.left + alignRect.width / 2) - (animatedRect.left + animatedRect.width / 2);\n const relativeY = (alignRect.top + alignRect.height / 2) - (animatedRect.top + animatedRect.height / 2);\n \n // Subtract the path's first point so path starts at align element\n return {\n x: relativeX - firstPoint.x,\n y: relativeY - firstPoint.y,\n };\n }\n\n // Fallback: just offset by negative of path's first point\n // This makes the path start at the element's current position\n return {\n x: -firstPoint.x,\n y: -firstPoint.y,\n };\n}\n\n/**\n * Parse a path configuration and prepare it for animation\n */\nexport function parsePath(\n target: string | Element,\n align?: string | Element,\n alignAt: [number, number] = [50, 50]\n): ParsedPathData | null {\n const pathData = resolvePathData(target);\n\n if (!pathData) {\n return null;\n }\n\n const length = getPathLength(pathData);\n\n if (length === 0) {\n return null;\n }\n\n return {\n pathData,\n length,\n };\n}\n\n/**\n * Cleanup function - removes hidden SVG from DOM\n * Call when SDK is destroyed or for testing cleanup\n */\nexport function cleanupPathParser(): void {\n if (hiddenSVG && hiddenSVG.parentNode) {\n hiddenSVG.parentNode.removeChild(hiddenSVG);\n }\n hiddenSVG = null;\n hiddenPath = null;\n pathLengthCache.clear();\n}\n",
|
|
16
|
-
"/**\n * PropertyParser - Parse and normalize animation properties\n *\n * Handles:\n * - Transform shortcuts (x, y, rotate, scale, etc.)\n * - Relative values (+=, -=, *=, /=)\n * - Unit parsing and defaulting\n * - Color parsing (hex, rgb, hsl, named, CSS variables) - OPTIONAL\n * - Filter parsing (blur, brightness, contrast, etc.) - OPTIONAL\n * - DrawSVG parsing (SVG stroke animations) - OPTIONAL\n * \n * Optional parsers are loaded conditionally via SDKRegistry.\n * If a parser is not loaded, those property types will be skipped.\n */\n\nimport type { AnimationTarget } from '../types';\nimport { clearTransformCache, getTransformValue as getCachedTransformValue } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport type { ParsedFilterFunction } from './FilterParser';\nimport type { ParsedDrawSVG } from './DrawSVGParser';\nimport {\n resolvePathData,\n getPathLength as getMotionPathLength,\n calculateElementOffset,\n calculatePathAlignOffset,\n type ParsedPathData\n} from './PathParser';\nimport type { PathConfig } from '../types';\n\n/**\n * Valid property value types for animation\n */\nexport type PropertyValue = number | string;\n\n// Transform properties that map to CSS transforms\nconst TRANSFORM_PROPS = new Set([\n 'perspective',\n 'x', 'y', 'z',\n 'rotate', 'rotateX', 'rotateY', 'rotateZ',\n 'scale', 'scaleX', 'scaleY', 'scaleZ',\n 'skewX', 'skewY'\n]);\n\n// Properties that default to px units\nconst PX_PROPS = new Set([\n 'perspective',\n 'x', 'y', 'z',\n 'width', 'height',\n 'top', 'left', 'right', 'bottom',\n 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',\n 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',\n 'borderRadius', 'fontSize', 'lineHeight'\n]);\n\n// Properties that default to deg units\nconst DEG_PROPS = new Set([\n 'rotate', 'rotateX', 'rotateY', 'rotateZ',\n 'skewX', 'skewY'\n]);\n\n// Properties that are unitless numbers\nconst UNITLESS_PROPS = new Set([\n 'opacity', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'z-index', 'zIndex'\n]);\n\n// Properties that are colors\nconst COLOR_PROPS = new Set([\n 'color',\n 'backgroundColor',\n 'borderColor',\n 'borderTopColor',\n 'borderRightColor',\n 'borderBottomColor',\n 'borderLeftColor',\n 'outlineColor',\n 'textDecorationColor',\n 'caretColor',\n 'fill',\n 'stroke'\n]);\n\n// Named CSS colors for looksLikeColor detection (hoisted to avoid per-call allocation)\nconst NAMED_COLORS = new Set([\n 'red', 'green', 'blue', 'yellow', 'orange', 'purple', 'pink', 'cyan',\n 'magenta', 'white', 'black', 'gray', 'grey', 'transparent',\n 'crimson', 'gold', 'silver', 'navy', 'teal', 'maroon', 'olive',\n 'lime', 'aqua', 'fuchsia', 'coral', 'salmon', 'tomato', 'indigo'\n]);\n\n/**\n * Base fields shared by all parsed property types\n */\ninterface ParsedPropertyBase {\n property: string;\n isTransform: boolean;\n}\n\n/**\n * Scalar property (numbers with units)\n */\ninterface ParsedScalarProperty extends ParsedPropertyBase {\n type: 'scalar';\n startValue: number;\n endValue: number;\n unit: string;\n}\n\n/**\n * Color property (RGBA values)\n */\ninterface ParsedColorProperty extends ParsedPropertyBase {\n type: 'color';\n startColor: Float32Array;\n endColor: Float32Array;\n}\n\n/**\n * Filter property (CSS filters)\n */\ninterface ParsedFilterProperty extends ParsedPropertyBase {\n type: 'filter';\n startFilters: ParsedFilterFunction[];\n endFilters: ParsedFilterFunction[];\n}\n\n/**\n * DrawSVG property (SVG stroke animation)\n */\ninterface ParsedDrawSVGProperty extends ParsedPropertyBase {\n type: 'drawSVG';\n startDraw: ParsedDrawSVG; // Start state { start: 0-1, end: 0-1 }\n endDraw: ParsedDrawSVG; // End state { start: 0-1, end: 0-1 }\n length: number; // Cached path length\n}\n\n/**\n * Path property (motion along SVG path)\n */\ninterface ParsedPathProperty extends ParsedPropertyBase {\n type: 'path';\n pathData: string; // SVG path \"d\" attribute\n pathLength: number; // Cached total length\n startProgress: number; // 0-1\n endProgress: number; // 0-1\n rotate: boolean; // Auto-rotate along tangent\n alignOffset: { x: number; y: number }; // Element origin offset (alignAt)\n pathOffset: { x: number; y: number }; // Path position offset (align element)\n}\n\n/**\n * Discriminated union of all parsed property types\n */\nexport type ParsedProperty = ParsedScalarProperty | ParsedColorProperty | ParsedFilterProperty | ParsedDrawSVGProperty | ParsedPathProperty;\n\n/**\n * Check if a property is a transform property\n */\nexport function isTransformProp(prop: string): boolean {\n return TRANSFORM_PROPS.has(prop);\n}\n\n/**\n * Check if a property is a color property\n */\nexport function isColorProp(prop: string): boolean {\n return COLOR_PROPS.has(prop);\n}\n\n/**\n * Check if a property is the filter property\n */\nexport function isFilterProp(prop: string): boolean {\n return prop === 'filter';\n}\n\n/**\n * Check if a property is the drawSVG property\n */\nexport function isDrawSVGProp(prop: string): boolean {\n return prop === 'drawSVG';\n}\n\n/**\n * Check if a property is the path property (motion path)\n */\nexport function isPathProp(prop: string): boolean {\n return prop === 'path';\n}\n\n/**\n * Check if a property is a CSS variable (custom property)\n */\nexport function isCSSVariable(prop: string): boolean {\n return prop.startsWith('--');\n}\n\n/**\n * Check if a value looks like a color (for CSS variable type detection)\n */\nexport function looksLikeColor(value: PropertyValue): boolean {\n if (typeof value !== 'string') return false;\n const trimmed = value.trim().toLowerCase();\n // Hex colors\n if (trimmed.startsWith('#')) return true;\n // RGB/RGBA/HSL/HSLA functions\n if (/^(rgb|rgba|hsl|hsla)\\s*\\(/.test(trimmed)) return true;\n // Delegate to ColorParser for full CSS named color coverage (148+ colors via browser engine)\n const colorParser = SDKRegistry.color;\n if (colorParser) return colorParser.parseColor(trimmed) !== null;\n return NAMED_COLORS.has(trimmed); // graceful fallback if ColorParser not loaded\n}\n\n/**\n * Parse a property value and extract numeric value and unit\n */\nexport function parseValue(value: PropertyValue): { value: number; unit: string } {\n if (typeof value === 'number') {\n if (isNaN(value) || !isFinite(value)) {\n console.warn(`[Motion] Property value is ${value}. This property will not animate correctly.`);\n return { value: 0, unit: '' };\n }\n return { value, unit: '' };\n }\n\n if (typeof value === 'string') {\n // Check for relative operators\n const relativeMatch = value.match(/^([+\\-*/])=(.+)/);\n if (relativeMatch) {\n const numMatch = relativeMatch[2].match(/([-+]?[\\d.]+(?:e[-+]?\\d+)?)([a-z%]*)/i);\n if (numMatch) {\n return {\n value: parseFloat(numMatch[1]),\n unit: numMatch[2] || ''\n };\n }\n }\n\n // Regular value with unit (supports scientific notation like 2.5e-16)\n const match = value.match(/([-+]?[\\d.]+(?:e[-+]?\\d+)?)([a-z%]*)/i);\n if (match) {\n return {\n value: parseFloat(match[1]),\n unit: match[2] || ''\n };\n }\n }\n\n return { value: 0, unit: '' };\n}\n\n/**\n * Get the default unit for a property\n */\nexport function getDefaultUnit(prop: string): string {\n if (UNITLESS_PROPS.has(prop)) {\n return '';\n }\n if (DEG_PROPS.has(prop)) {\n return 'deg';\n }\n if (PX_PROPS.has(prop)) {\n return 'px';\n }\n return '';\n}\n\n/**\n * Get the default value for a property when no computed value is available\n */\nfunction getDefaultPropertyValue(prop: string): number {\n // Opacity defaults to 1 (fully visible)\n if (prop === 'opacity') {\n return 1;\n }\n // Scale properties default to 1\n if (prop.startsWith('scale')) {\n return 1;\n }\n // Most other properties default to 0\n return 0;\n}\n\n/**\n * Get the current computed value of a property from a target\n * Handles both DOM Elements and plain objects\n */\nexport function getCurrentValue(target: AnimationTarget, prop: string): number {\n // Plain object target - read property directly\n if (!(target instanceof Element)) {\n const obj = target as Record<string, number>;\n if (prop in obj) {\n return obj[prop];\n }\n // Return default for known properties\n return getDefaultTransformValue(prop);\n }\n\n // DOM Element - get from transform cache or computed style\n if (isTransformProp(prop)) {\n return getCachedTransformValue(target, prop);\n }\n\n // Get from computed style (handle test environments)\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(target);\n const value = computed.getPropertyValue(camelToKebab(prop));\n\n if (value) {\n const parsed = parseValue(value);\n return parsed.value;\n }\n }\n } catch (e) {\n // Fail gracefully in test environments\n }\n\n // Return property-specific default (not just 0 for everything)\n return getDefaultPropertyValue(prop);\n}\n\n/**\n * Get the original CSS value AND unit of a property (excluding inline styles)\n * Returns both value and unit to preserve the original CSS unit (%, rem, em, vh, etc.)\n */\nexport function getOriginalCSSValueWithUnit(target: Element, prop: string): { value: number; unit: string } {\n const htmlEl = target as HTMLElement;\n const kebabProp = camelToKebab(prop);\n\n // For transform properties, we need special handling\n if (isTransformProp(prop)) {\n // Save current inline transform\n const inlineTransform = htmlEl.style.transform;\n // Clear it\n clearTransformCache(target);\n htmlEl.style.transform = '';\n // Get computed (will now reflect stylesheet only)\n const value = getCachedTransformValue(target, prop);\n // Restore inline\n htmlEl.style.transform = inlineTransform;\n clearTransformCache(target);\n // Transform properties use their default units\n return { value, unit: getDefaultUnit(prop) };\n }\n\n // For regular CSS properties\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n // Save current inline value\n const inlineValue = htmlEl.style.getPropertyValue(kebabProp);\n // Temporarily remove inline style\n htmlEl.style.removeProperty(kebabProp);\n // Get computed style (now reflects stylesheet only)\n const computed = window.getComputedStyle(target);\n const value = computed.getPropertyValue(kebabProp);\n // Restore inline style\n if (inlineValue) {\n htmlEl.style.setProperty(kebabProp, inlineValue);\n }\n\n if (value) {\n const parsed = parseValue(value);\n return { value: parsed.value, unit: parsed.unit };\n }\n }\n } catch (e) {\n // Fail gracefully\n }\n\n return { value: getDefaultPropertyValue(prop), unit: getDefaultUnit(prop) };\n}\n\n/**\n * Get default value for transform properties\n */\nfunction getDefaultTransformValue(prop: string): number {\n if (prop.startsWith('scale')) {\n return 1;\n }\n return 0;\n}\n\n/**\n * Convert camelCase to kebab-case\n */\nexport function camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);\n}\n\n/**\n * Check if a value is relative (+=, -=, *=, /=)\n */\nexport function isRelativeValue(value: PropertyValue): boolean {\n if (typeof value === 'string') {\n return /^[+\\-*/]=/.test(value);\n }\n return false;\n}\n\n/**\n * Get relative operator from value string\n */\nexport function getRelativeOperator(value: string): '+=' | '-=' | '*=' | '/=' | null {\n const match = value.match(/^([+\\-*/])=/);\n return match ? (match[0] as '+=' | '-=' | '*=' | '/=') : null;\n}\n\n/**\n * Calculate relative end value\n */\nexport function calculateRelativeValue(\n startValue: number,\n relativeValue: number,\n operator: '+=' | '-=' | '*=' | '/='\n): number {\n switch (operator) {\n case '+=':\n return startValue + relativeValue;\n case '-=':\n return startValue - relativeValue;\n case '*=':\n return startValue * relativeValue;\n case '/=':\n return relativeValue !== 0 ? startValue / relativeValue : startValue;\n default:\n return relativeValue;\n }\n}\n\n/**\n * Parse a color property for animation\n * Returns null if ColorParser is not loaded\n */\nfunction parseColorProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedColorProperty | null {\n const colorParser = SDKRegistry.color;\n if (!colorParser) {\n return null; // ColorParser not loaded\n }\n\n // Empty string means \"use current computed color\" (used when from() has color but to() doesn't)\n const targetStr = String(targetValue);\n const endColor = targetStr === ''\n ? colorParser.getCurrentColor(element, prop)\n : colorParser.parseColor(targetStr, element);\n\n const startColor = fromValue !== undefined\n ? colorParser.parseColor(String(fromValue), element)\n : colorParser.getCurrentColor(element, prop);\n\n return {\n type: 'color',\n property: prop,\n isTransform: false,\n startColor: startColor || new Float32Array([0, 0, 0, 1]),\n endColor: endColor || new Float32Array([0, 0, 0, 1]),\n };\n}\n\n/**\n * Parse a filter property for animation\n * Returns null if FilterParser is not loaded\n */\nfunction parseFilterProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedFilterProperty | null {\n const filterParser = SDKRegistry.filter;\n if (!filterParser) {\n return null; // FilterParser not loaded\n }\n\n // Parse target filter (empty string means \"use current\")\n const targetStr = String(targetValue);\n let endFilters = targetStr === ''\n ? filterParser.getCurrentFilter(element)\n : filterParser.parseFilter(targetStr);\n\n // Parse start filter\n let startFilters = fromValue !== undefined\n ? filterParser.parseFilter(String(fromValue))\n : filterParser.getCurrentFilter(element);\n\n // Ensure both are arrays (default to empty if parsing failed)\n startFilters = startFilters || [];\n endFilters = endFilters || [];\n\n // Merge arrays to ensure same length and order\n const merged = filterParser.mergeFilterArrays(startFilters, endFilters);\n\n return {\n type: 'filter',\n property: prop,\n isTransform: false,\n startFilters: merged.start,\n endFilters: merged.end,\n };\n}\n\n/**\n * Parse a CSS variable property for animation\n */\nfunction parseCSSVariableProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedProperty {\n // Detect if value is a color by checking if it looks like one\n const isColorValue = looksLikeColor(targetValue) || (fromValue !== undefined && looksLikeColor(fromValue));\n\n if (isColorValue) {\n const colorParser = SDKRegistry.color;\n if (colorParser) {\n // Parse as color\n const targetStr = String(targetValue);\n const endColor = colorParser.parseColor(targetStr, element);\n\n const startColor = fromValue !== undefined\n ? colorParser.parseColor(String(fromValue), element)\n : getCSSVariableColor(element, prop);\n\n return {\n type: 'color',\n property: prop,\n isTransform: false,\n startColor: startColor || new Float32Array([0, 0, 0, 1]),\n endColor: endColor || new Float32Array([0, 0, 0, 1]),\n };\n }\n // ColorParser not loaded, fall through to scalar\n }\n\n // Parse as scalar (numeric value)\n let startValue: number;\n let endValue: number;\n let unit: string;\n\n if (fromValue !== undefined && targetValue !== undefined) {\n // Both from and to specified\n const parsedFrom = parseValue(fromValue);\n const parsedTo = parseValue(targetValue);\n startValue = parsedFrom.value;\n endValue = parsedTo.value;\n unit = parsedTo.unit || parsedFrom.unit;\n } else if (fromValue !== undefined) {\n // From-only: resolve 'to' from computed CSS variable value\n const parsedFrom = parseValue(fromValue);\n startValue = parsedFrom.value;\n endValue = getCSSVariableNumber(element, prop);\n unit = parsedFrom.unit;\n } else {\n // To-only: resolve 'from' from computed CSS variable value\n const parsedTo = parseValue(targetValue);\n startValue = getCSSVariableNumber(element, prop);\n endValue = parsedTo.value;\n unit = parsedTo.unit;\n }\n\n return {\n type: 'scalar',\n property: prop,\n isTransform: false,\n startValue,\n endValue,\n unit,\n };\n}\n\n/**\n * Get current CSS variable value as a color\n */\nfunction getCSSVariableColor(element: Element, prop: string): Float32Array | null {\n const colorParser = SDKRegistry.color;\n if (!colorParser) return null;\n\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(element);\n const value = computed.getPropertyValue(prop).trim();\n if (value) {\n return colorParser.parseColor(value, element);\n }\n }\n } catch {\n // Fail gracefully\n }\n return null;\n}\n\n/**\n * Get current CSS variable value as a number\n */\nfunction getCSSVariableNumber(element: Element, prop: string): number {\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(element);\n const value = computed.getPropertyValue(prop).trim();\n if (value) {\n return parseValue(value).value;\n }\n }\n } catch {\n // Fail gracefully\n }\n return 0;\n}\n\n/**\n * Parse a drawSVG property for animation\n * Returns null if DrawSVGParser is not loaded\n */\nfunction parseDrawSVGProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedDrawSVGProperty | null {\n const drawSVGParser = SDKRegistry.drawSVG;\n if (!drawSVGParser) {\n return null; // DrawSVGParser not loaded\n }\n\n // Get path length (cached)\n const length = drawSVGParser.getPathLength(element);\n\n // Parse target (end) value\n const endDraw = drawSVGParser.parseDrawSVG(targetValue as string | { start?: number; end?: number }, element)\n || { start: 0, end: 1 };\n\n // Parse start value\n const startDraw = fromValue !== undefined\n ? drawSVGParser.parseDrawSVG(fromValue as string | { start?: number; end?: number }, element) || { start: 0, end: 1 }\n : drawSVGParser.getCurrentDrawSVG(element);\n\n return {\n type: 'drawSVG',\n property: prop,\n isTransform: false,\n startDraw,\n endDraw,\n length,\n };\n}\n\n/**\n * Parse a path property for motion path animation\n * Supports path in TO only, FROM only, or both FROM and TO\n */\nfunction parsePathProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedPathProperty | null {\n // Path config can be in targetValue (TO), fromValue (FROM), or both\n const toConfig = targetValue as unknown as PathConfig | undefined;\n const fromConfig = fromValue as unknown as PathConfig | undefined;\n\n // Get the primary config (prefer TO, fall back to FROM)\n const primaryConfig = toConfig?.target ? toConfig : fromConfig;\n if (!primaryConfig?.target) {\n return null;\n }\n\n // Resolve path data from target (selector, element, or raw path data)\n const pathData = resolvePathData(primaryConfig.target);\n if (!pathData) {\n return null;\n }\n\n // Get path length\n const pathLength = getMotionPathLength(pathData);\n if (pathLength === 0) {\n return null;\n }\n\n // Get progress values - support FROM only, TO only, or both\n let startProgress: number;\n let endProgress: number;\n\n if (toConfig?.target && fromConfig?.target) {\n // Both FROM and TO specified\n startProgress = fromConfig.start ?? 0;\n endProgress = toConfig.end ?? 1;\n } else if (toConfig?.target) {\n // TO only - animate from start to end on the path\n startProgress = toConfig.start ?? 0;\n endProgress = toConfig.end ?? 1;\n } else {\n // FROM only - animate from the from position to end of path\n startProgress = fromConfig!.start ?? 0;\n endProgress = fromConfig!.end ?? 1;\n }\n\n // Get rotation setting (from either config)\n const rotate = toConfig?.rotate ?? fromConfig?.rotate ?? false;\n\n // Calculate element origin offset (alignAt - where on the element to position)\n const alignAt = toConfig?.alignAt ?? fromConfig?.alignAt ?? [50, 50];\n const alignOffset = calculateElementOffset(element, alignAt);\n\n // Calculate path offset (align - position path relative to another element)\n const alignTarget = toConfig?.align ?? fromConfig?.align;\n const pathOffset = calculatePathAlignOffset(alignTarget, pathData, element);\n\n return {\n type: 'path',\n property: prop,\n isTransform: false,\n pathData,\n pathLength,\n startProgress,\n endProgress,\n rotate,\n alignOffset,\n pathOffset,\n };\n}\n\n/**\n * Parse a property for animation\n * Handles both DOM Elements and plain objects\n */\nexport function parseProperty(\n target: AnimationTarget,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedProperty {\n const isTargetElement = target instanceof Element;\n\n // Handle CSS variables (DOM elements only)\n if (isTargetElement && isCSSVariable(prop)) {\n return parseCSSVariableProperty(target, prop, targetValue, fromValue);\n }\n\n // Handle color properties (DOM elements only, requires ColorParser)\n if (isTargetElement && isColorProp(prop)) {\n const colorParsed = parseColorProperty(target, prop, targetValue, fromValue);\n if (colorParsed) {\n return colorParsed;\n }\n // ColorParser not loaded - skip this property by returning a no-op scalar\n // The property will animate from 0 to 0 (no visible effect)\n }\n\n // Handle filter property (DOM elements only, requires FilterParser)\n if (isTargetElement && isFilterProp(prop)) {\n const filterParsed = parseFilterProperty(target, prop, targetValue, fromValue);\n if (filterParsed) {\n return filterParsed;\n }\n // FilterParser not loaded - skip this property\n }\n\n // Handle drawSVG property (DOM elements only, requires DrawSVGParser)\n if (isTargetElement && isDrawSVGProp(prop)) {\n const drawSVGParsed = parseDrawSVGProperty(target, prop, targetValue, fromValue);\n if (drawSVGParsed) {\n return drawSVGParsed;\n }\n // DrawSVGParser not loaded - skip this property\n }\n\n // Handle path property (motion path, DOM elements only)\n if (isTargetElement && isPathProp(prop)) {\n const parsed = parsePathProperty(target, prop, targetValue, fromValue);\n if (parsed) {\n return parsed;\n }\n // Fall through to scalar if path parsing failed\n }\n\n // Scalar properties work for both DOM elements and plain objects\n const isTransform = isTransformProp(prop);\n const isRelative = fromValue === undefined && isRelativeValue(targetValue);\n\n let startValue: number;\n let endValue: number;\n let unit: string;\n\n if (fromValue !== undefined) {\n // FromTo animation - from value is specified\n const parsedFrom = parseValue(fromValue);\n\n if (targetValue !== undefined) {\n // Both from and to specified — use explicit values\n const parsedTo = parseValue(targetValue);\n startValue = parsedFrom.value;\n endValue = parsedTo.value;\n unit = parsedTo.unit || parsedFrom.unit || getDefaultUnit(prop);\n } else {\n // From-only: property exists in 'from' but not 'to'.\n // Resolve 'to' from the element's current/computed value so the\n // animation returns to the natural state instead of snapping to 0.\n startValue = parsedFrom.value;\n endValue = getCurrentValue(target, prop);\n unit = parsedFrom.unit || getDefaultUnit(prop);\n }\n } else if (isRelative) {\n // Relative animation (+=, -=, *=, /=)\n const relativeOp = getRelativeOperator(targetValue as string)!;\n const current = getCurrentValue(target, prop);\n const parsed = parseValue(targetValue);\n\n startValue = current;\n endValue = calculateRelativeValue(current, parsed.value, relativeOp);\n unit = parsed.unit || getDefaultUnit(prop);\n } else {\n // Regular \"to\" animation\n const current = getCurrentValue(target, prop);\n const parsed = parseValue(targetValue);\n\n startValue = current;\n endValue = parsed.value;\n unit = parsed.unit || getDefaultUnit(prop);\n }\n\n return {\n type: 'scalar',\n property: prop,\n isTransform,\n startValue,\n endValue,\n unit,\n };\n}\n",
|
|
16
|
+
"/**\n * PropertyParser - Parse and normalize animation properties\n *\n * Handles:\n * - Transform shortcuts (x, y, rotate, scale, etc.)\n * - Relative values (+=, -=, *=, /=)\n * - Unit parsing and defaulting\n * - Color parsing (hex, rgb, hsl, named, CSS variables) - OPTIONAL\n * - Filter parsing (blur, brightness, contrast, etc.) - OPTIONAL\n * - DrawSVG parsing (SVG stroke animations) - OPTIONAL\n * \n * Optional parsers are loaded conditionally via SDKRegistry.\n * If a parser is not loaded, those property types will be skipped.\n */\n\nimport type { AnimationTarget } from '../types';\nimport { clearTransformCache, getTransformValue as getCachedTransformValue } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport type { ParsedFilterFunction } from './FilterParser';\nimport type { ParsedDrawSVG } from './DrawSVGParser';\nimport type { ParsedClipPath } from './ClipPathParser';\nimport {\n resolvePathData,\n getPathLength as getMotionPathLength,\n calculateElementOffset,\n calculatePathAlignOffset,\n type ParsedPathData\n} from './PathParser';\nimport type { PathConfig } from '../types';\n\n/**\n * Valid property value types for animation\n */\nexport type PropertyValue = number | string;\n\n// Transform properties that map to CSS transforms\nconst TRANSFORM_PROPS = new Set([\n 'perspective',\n 'x', 'y', 'z',\n 'rotate', 'rotateX', 'rotateY', 'rotateZ',\n 'scale', 'scaleX', 'scaleY', 'scaleZ',\n 'skewX', 'skewY'\n]);\n\n// Properties that default to px units\nconst PX_PROPS = new Set([\n 'perspective',\n 'x', 'y', 'z',\n 'width', 'height',\n 'top', 'left', 'right', 'bottom',\n 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',\n 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',\n 'borderRadius', 'fontSize', 'lineHeight'\n]);\n\n// Properties that default to deg units\nconst DEG_PROPS = new Set([\n 'rotate', 'rotateX', 'rotateY', 'rotateZ',\n 'skewX', 'skewY'\n]);\n\n// Properties that are unitless numbers\nconst UNITLESS_PROPS = new Set([\n 'opacity', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'z-index', 'zIndex'\n]);\n\n// Properties that are colors\nconst COLOR_PROPS = new Set([\n 'color',\n 'backgroundColor',\n 'borderColor',\n 'borderTopColor',\n 'borderRightColor',\n 'borderBottomColor',\n 'borderLeftColor',\n 'outlineColor',\n 'textDecorationColor',\n 'caretColor',\n 'fill',\n 'stroke'\n]);\n\n// Named CSS colors for looksLikeColor detection (hoisted to avoid per-call allocation)\nconst NAMED_COLORS = new Set([\n 'red', 'green', 'blue', 'yellow', 'orange', 'purple', 'pink', 'cyan',\n 'magenta', 'white', 'black', 'gray', 'grey', 'transparent',\n 'crimson', 'gold', 'silver', 'navy', 'teal', 'maroon', 'olive',\n 'lime', 'aqua', 'fuchsia', 'coral', 'salmon', 'tomato', 'indigo'\n]);\n\n/**\n * Base fields shared by all parsed property types\n */\ninterface ParsedPropertyBase {\n property: string;\n isTransform: boolean;\n}\n\n/**\n * Scalar property (numbers with units)\n */\ninterface ParsedScalarProperty extends ParsedPropertyBase {\n type: 'scalar';\n startValue: number;\n endValue: number;\n unit: string;\n}\n\n/**\n * Color property (RGBA values)\n */\ninterface ParsedColorProperty extends ParsedPropertyBase {\n type: 'color';\n startColor: Float32Array;\n endColor: Float32Array;\n}\n\n/**\n * Filter property (CSS filters)\n */\ninterface ParsedFilterProperty extends ParsedPropertyBase {\n type: 'filter';\n startFilters: ParsedFilterFunction[];\n endFilters: ParsedFilterFunction[];\n}\n\n/**\n * DrawSVG property (SVG stroke animation)\n */\ninterface ParsedDrawSVGProperty extends ParsedPropertyBase {\n type: 'drawSVG';\n startDraw: ParsedDrawSVG; // Start state { start: 0-1, end: 0-1 }\n endDraw: ParsedDrawSVG; // End state { start: 0-1, end: 0-1 }\n length: number; // Cached path length\n}\n\n/**\n * Clip-path property (CSS clip-path shape function interpolation)\n */\ninterface ParsedClipPathProperty extends ParsedPropertyBase {\n type: 'clipPath';\n startClipPath: ParsedClipPath;\n endClipPath: ParsedClipPath;\n}\n\n/**\n * Path property (motion along SVG path)\n */\ninterface ParsedPathProperty extends ParsedPropertyBase {\n type: 'path';\n pathData: string; // SVG path \"d\" attribute\n pathLength: number; // Cached total length\n startProgress: number; // 0-1\n endProgress: number; // 0-1\n rotate: boolean; // Auto-rotate along tangent\n alignOffset: { x: number; y: number }; // Element origin offset (alignAt)\n pathOffset: { x: number; y: number }; // Path position offset (align element)\n}\n\n/**\n * Discriminated union of all parsed property types\n */\nexport type ParsedProperty = ParsedScalarProperty | ParsedColorProperty | ParsedFilterProperty | ParsedDrawSVGProperty | ParsedPathProperty | ParsedClipPathProperty;\n\n/**\n * Check if a property is a transform property\n */\nexport function isTransformProp(prop: string): boolean {\n return TRANSFORM_PROPS.has(prop);\n}\n\n/**\n * Check if a property is a color property\n */\nexport function isColorProp(prop: string): boolean {\n return COLOR_PROPS.has(prop);\n}\n\n/**\n * Check if a property is the filter property\n */\nexport function isFilterProp(prop: string): boolean {\n return prop === 'filter';\n}\n\n/**\n * Check if a property is the drawSVG property\n */\nexport function isDrawSVGProp(prop: string): boolean {\n return prop === 'drawSVG';\n}\n\n/**\n * Check if a property is the path property (motion path)\n */\nexport function isPathProp(prop: string): boolean {\n return prop === 'path';\n}\n\n/**\n * Check if a property is the clip-path property (accepts both kebab and camel case)\n */\nexport function isClipPathProp(prop: string): boolean {\n return prop === 'clip-path' || prop === 'clipPath';\n}\n\n/**\n * Check if a property is a CSS variable (custom property)\n */\nexport function isCSSVariable(prop: string): boolean {\n return prop.startsWith('--');\n}\n\n/**\n * Check if a value looks like a color (for CSS variable type detection)\n */\nexport function looksLikeColor(value: PropertyValue): boolean {\n if (typeof value !== 'string') return false;\n const trimmed = value.trim().toLowerCase();\n // Hex colors\n if (trimmed.startsWith('#')) return true;\n // RGB/RGBA/HSL/HSLA functions\n if (/^(rgb|rgba|hsl|hsla)\\s*\\(/.test(trimmed)) return true;\n // Delegate to ColorParser for full CSS named color coverage (148+ colors via browser engine)\n const colorParser = SDKRegistry.color;\n if (colorParser) return colorParser.parseColor(trimmed) !== null;\n return NAMED_COLORS.has(trimmed); // graceful fallback if ColorParser not loaded\n}\n\n/**\n * Parse a property value and extract numeric value and unit\n */\nexport function parseValue(value: PropertyValue): { value: number; unit: string } {\n if (typeof value === 'number') {\n if (isNaN(value) || !isFinite(value)) {\n console.warn(`[Motion] Property value is ${value}. This property will not animate correctly.`);\n return { value: 0, unit: '' };\n }\n return { value, unit: '' };\n }\n\n if (typeof value === 'string') {\n // Check for relative operators\n const relativeMatch = value.match(/^([+\\-*/])=(.+)/);\n if (relativeMatch) {\n const numMatch = relativeMatch[2].match(/([-+]?[\\d.]+(?:e[-+]?\\d+)?)([a-z%]*)/i);\n if (numMatch) {\n return {\n value: parseFloat(numMatch[1]),\n unit: numMatch[2] || ''\n };\n }\n }\n\n // Regular value with unit (supports scientific notation like 2.5e-16)\n const match = value.match(/([-+]?[\\d.]+(?:e[-+]?\\d+)?)([a-z%]*)/i);\n if (match) {\n return {\n value: parseFloat(match[1]),\n unit: match[2] || ''\n };\n }\n }\n\n return { value: 0, unit: '' };\n}\n\n/**\n * Get the default unit for a property\n */\nexport function getDefaultUnit(prop: string): string {\n if (UNITLESS_PROPS.has(prop)) {\n return '';\n }\n if (DEG_PROPS.has(prop)) {\n return 'deg';\n }\n if (PX_PROPS.has(prop)) {\n return 'px';\n }\n return '';\n}\n\n/**\n * Get the default value for a property when no computed value is available\n */\nfunction getDefaultPropertyValue(prop: string): number {\n // Opacity defaults to 1 (fully visible)\n if (prop === 'opacity') {\n return 1;\n }\n // Scale properties default to 1\n if (prop.startsWith('scale')) {\n return 1;\n }\n // Most other properties default to 0\n return 0;\n}\n\n/**\n * Get the current computed value of a property from a target\n * Handles both DOM Elements and plain objects\n */\nexport function getCurrentValue(target: AnimationTarget, prop: string): number {\n // Plain object target - read property directly\n if (!(target instanceof Element)) {\n const obj = target as Record<string, number>;\n if (prop in obj) {\n return obj[prop];\n }\n // Return default for known properties\n return getDefaultTransformValue(prop);\n }\n\n // DOM Element - get from transform cache or computed style\n if (isTransformProp(prop)) {\n return getCachedTransformValue(target, prop);\n }\n\n // Get from computed style (handle test environments)\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(target);\n const value = computed.getPropertyValue(camelToKebab(prop));\n\n if (value) {\n const parsed = parseValue(value);\n return parsed.value;\n }\n }\n } catch (e) {\n // Fail gracefully in test environments\n }\n\n // Return property-specific default (not just 0 for everything)\n return getDefaultPropertyValue(prop);\n}\n\n/**\n * Convert a pixel value to a target CSS unit by measuring the element in the DOM.\n *\n * Uses the browser's layout engine: temporarily sets `100{targetUnit}` on the\n * element, reads the computed pixel equivalent, then derives the conversion ratio.\n * This handles %, em, rem, vw, vh, vmin, vmax, ch, ex — any unit the browser supports.\n *\n * When the target unit is 'px' or empty, returns the value unchanged (no conversion).\n * Falls back to the raw pixel value if measurement fails (e.g. detached element).\n */\nexport function convertPxToUnit(\n element: Element,\n prop: string,\n pxValue: number,\n targetUnit: string\n): number {\n // No conversion needed for px or unitless\n if (!targetUnit || targetUnit === 'px') {\n return pxValue;\n }\n\n // Skip conversion for non-DOM contexts\n if (typeof window === 'undefined' || !window.getComputedStyle) {\n return pxValue;\n }\n\n const htmlEl = element as HTMLElement;\n const kebabProp = camelToKebab(prop);\n\n try {\n // Save the current inline value so we can restore it\n const savedInline = htmlEl.style.getPropertyValue(kebabProp);\n\n // Set a known reference value in the target unit (100 of that unit)\n htmlEl.style.setProperty(kebabProp, `100${targetUnit}`);\n\n // Read back the computed pixel equivalent\n const computed = window.getComputedStyle(element);\n const pxPer100Units = parseFloat(computed.getPropertyValue(kebabProp));\n\n // Restore the original inline value\n if (savedInline) {\n htmlEl.style.setProperty(kebabProp, savedInline);\n } else {\n htmlEl.style.removeProperty(kebabProp);\n }\n\n // Convert: if 100% = 1600px, then 400px = (400/1600)*100 = 25%\n if (pxPer100Units && isFinite(pxPer100Units) && pxPer100Units !== 0) {\n return (pxValue / pxPer100Units) * 100;\n }\n } catch {\n // Fail gracefully — return raw value\n }\n\n return pxValue;\n}\n\n/**\n * Get the current computed value of a property, converted to the specified unit.\n *\n * Combines getCurrentValue (which always returns px for layout properties)\n * with convertPxToUnit to return the value in the desired CSS unit.\n * Only performs conversion for DOM Element targets with non-px units.\n */\nexport function getCurrentValueInUnit(\n target: AnimationTarget,\n prop: string,\n targetUnit: string\n): number {\n const pxValue = getCurrentValue(target, prop);\n\n // Only convert for DOM elements with non-px units\n if (target instanceof Element && targetUnit && targetUnit !== 'px') {\n return convertPxToUnit(target, prop, pxValue, targetUnit);\n }\n\n return pxValue;\n}\n\n/**\n * Get the original CSS value AND unit of a property (excluding inline styles)\n * Returns both value and unit to preserve the original CSS unit (%, rem, em, vh, etc.)\n */\nexport function getOriginalCSSValueWithUnit(target: Element, prop: string): { value: number; unit: string } {\n const htmlEl = target as HTMLElement;\n const kebabProp = camelToKebab(prop);\n\n // For transform properties, we need special handling\n if (isTransformProp(prop)) {\n // Save current inline transform\n const inlineTransform = htmlEl.style.transform;\n // Clear it\n clearTransformCache(target);\n htmlEl.style.transform = '';\n // Get computed (will now reflect stylesheet only)\n const value = getCachedTransformValue(target, prop);\n // Restore inline\n htmlEl.style.transform = inlineTransform;\n clearTransformCache(target);\n // Transform properties use their default units\n return { value, unit: getDefaultUnit(prop) };\n }\n\n // For regular CSS properties\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n // Save current inline value\n const inlineValue = htmlEl.style.getPropertyValue(kebabProp);\n // Temporarily remove inline style\n htmlEl.style.removeProperty(kebabProp);\n // Get computed style (now reflects stylesheet only)\n const computed = window.getComputedStyle(target);\n const value = computed.getPropertyValue(kebabProp);\n // Restore inline style\n if (inlineValue) {\n htmlEl.style.setProperty(kebabProp, inlineValue);\n }\n\n if (value) {\n const parsed = parseValue(value);\n return { value: parsed.value, unit: parsed.unit };\n }\n }\n } catch (e) {\n // Fail gracefully\n }\n\n return { value: getDefaultPropertyValue(prop), unit: getDefaultUnit(prop) };\n}\n\n/**\n * Get default value for transform properties\n */\nfunction getDefaultTransformValue(prop: string): number {\n if (prop.startsWith('scale')) {\n return 1;\n }\n return 0;\n}\n\n/**\n * Convert camelCase to kebab-case\n */\nexport function camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);\n}\n\n/**\n * Check if a value is the keyword \"auto\"\n */\nfunction isAutoValue(value: PropertyValue): boolean {\n return typeof value === 'string' && value.trim().toLowerCase() === 'auto';\n}\n\n/**\n * Properties that support \"auto\" value resolution.\n * Only layout properties where the browser can compute a natural value make sense.\n */\nconst AUTO_PROPS = new Set(['width', 'height']);\n\n/**\n * Resolve \"auto\" to a concrete pixel value by temporarily setting the property\n * to \"auto\" and measuring the element's layout.\n *\n * Works by:\n * 1. Saving the element's current inline style for the property\n * 2. Setting the property to \"auto\"\n * 3. Reading the computed pixel dimension via offsetWidth/offsetHeight\n * 4. Restoring the original inline style\n *\n * Only valid for width/height on DOM elements.\n * Returns 0 if measurement fails or the property doesn't support auto.\n */\nfunction resolveAutoValue(element: Element, prop: string): number {\n if (!AUTO_PROPS.has(prop)) return 0;\n\n const htmlEl = element as HTMLElement;\n const kebabProp = camelToKebab(prop);\n\n try {\n // Save current inline value\n const savedInline = htmlEl.style.getPropertyValue(kebabProp);\n\n // Set to auto so the browser computes the natural dimension\n htmlEl.style.setProperty(kebabProp, 'auto');\n\n // Read the computed pixel value\n const pxValue = prop === 'width' ? htmlEl.offsetWidth : htmlEl.offsetHeight;\n\n // Restore original inline style\n if (savedInline) {\n htmlEl.style.setProperty(kebabProp, savedInline);\n } else {\n htmlEl.style.removeProperty(kebabProp);\n }\n\n return pxValue;\n } catch {\n return 0;\n }\n}\n\n/**\n * Check if a value is relative (+=, -=, *=, /=)\n */\nexport function isRelativeValue(value: PropertyValue): boolean {\n if (typeof value === 'string') {\n return /^[+\\-*/]=/.test(value);\n }\n return false;\n}\n\n/**\n * Get relative operator from value string\n */\nexport function getRelativeOperator(value: string): '+=' | '-=' | '*=' | '/=' | null {\n const match = value.match(/^([+\\-*/])=/);\n return match ? (match[0] as '+=' | '-=' | '*=' | '/=') : null;\n}\n\n/**\n * Calculate relative end value\n */\nexport function calculateRelativeValue(\n startValue: number,\n relativeValue: number,\n operator: '+=' | '-=' | '*=' | '/='\n): number {\n switch (operator) {\n case '+=':\n return startValue + relativeValue;\n case '-=':\n return startValue - relativeValue;\n case '*=':\n return startValue * relativeValue;\n case '/=':\n return relativeValue !== 0 ? startValue / relativeValue : startValue;\n default:\n return relativeValue;\n }\n}\n\n/**\n * Parse a color property for animation\n * Returns null if ColorParser is not loaded\n */\nfunction parseColorProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedColorProperty | null {\n const colorParser = SDKRegistry.color;\n if (!colorParser) {\n return null; // ColorParser not loaded\n }\n\n // Empty string means \"use current computed color\" (used when from() has color but to() doesn't)\n const targetStr = String(targetValue);\n const endColor = targetStr === ''\n ? colorParser.getCurrentColor(element, prop)\n : colorParser.parseColor(targetStr, element);\n\n const startColor = fromValue !== undefined\n ? colorParser.parseColor(String(fromValue), element)\n : colorParser.getCurrentColor(element, prop);\n\n return {\n type: 'color',\n property: prop,\n isTransform: false,\n startColor: startColor || new Float32Array([0, 0, 0, 1]),\n endColor: endColor || new Float32Array([0, 0, 0, 1]),\n };\n}\n\n/**\n * Parse a filter property for animation\n * Returns null if FilterParser is not loaded\n */\nfunction parseFilterProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedFilterProperty | null {\n const filterParser = SDKRegistry.filter;\n if (!filterParser) {\n return null; // FilterParser not loaded\n }\n\n // Parse target filter (empty string means \"use current\")\n const targetStr = String(targetValue);\n let endFilters = targetStr === ''\n ? filterParser.getCurrentFilter(element)\n : filterParser.parseFilter(targetStr);\n\n // Parse start filter\n let startFilters = fromValue !== undefined\n ? filterParser.parseFilter(String(fromValue))\n : filterParser.getCurrentFilter(element);\n\n // Ensure both are arrays (default to empty if parsing failed)\n startFilters = startFilters || [];\n endFilters = endFilters || [];\n\n // Merge arrays to ensure same length and order\n const merged = filterParser.mergeFilterArrays(startFilters, endFilters);\n\n return {\n type: 'filter',\n property: prop,\n isTransform: false,\n startFilters: merged.start,\n endFilters: merged.end,\n };\n}\n\n/**\n * Parse a CSS variable property for animation\n */\nfunction parseCSSVariableProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedProperty {\n // Detect if value is a color by checking if it looks like one\n const isColorValue = looksLikeColor(targetValue) || (fromValue !== undefined && looksLikeColor(fromValue));\n\n if (isColorValue) {\n const colorParser = SDKRegistry.color;\n if (colorParser) {\n // Parse as color\n const targetStr = String(targetValue);\n const endColor = colorParser.parseColor(targetStr, element);\n\n const startColor = fromValue !== undefined\n ? colorParser.parseColor(String(fromValue), element)\n : getCSSVariableColor(element, prop);\n\n return {\n type: 'color',\n property: prop,\n isTransform: false,\n startColor: startColor || new Float32Array([0, 0, 0, 1]),\n endColor: endColor || new Float32Array([0, 0, 0, 1]),\n };\n }\n // ColorParser not loaded, fall through to scalar\n }\n\n // Parse as scalar (numeric value)\n let startValue: number;\n let endValue: number;\n let unit: string;\n\n if (fromValue !== undefined && targetValue !== undefined) {\n // Both from and to specified\n const parsedFrom = parseValue(fromValue);\n const parsedTo = parseValue(targetValue);\n startValue = parsedFrom.value;\n endValue = parsedTo.value;\n unit = parsedTo.unit || parsedFrom.unit;\n } else if (fromValue !== undefined) {\n // From-only: resolve 'to' from computed CSS variable value\n const parsedFrom = parseValue(fromValue);\n startValue = parsedFrom.value;\n endValue = getCSSVariableNumber(element, prop);\n unit = parsedFrom.unit;\n } else {\n // To-only: resolve 'from' from computed CSS variable value\n const parsedTo = parseValue(targetValue);\n startValue = getCSSVariableNumber(element, prop);\n endValue = parsedTo.value;\n unit = parsedTo.unit;\n }\n\n return {\n type: 'scalar',\n property: prop,\n isTransform: false,\n startValue,\n endValue,\n unit,\n };\n}\n\n/**\n * Get current CSS variable value as a color\n */\nfunction getCSSVariableColor(element: Element, prop: string): Float32Array | null {\n const colorParser = SDKRegistry.color;\n if (!colorParser) return null;\n\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(element);\n const value = computed.getPropertyValue(prop).trim();\n if (value) {\n return colorParser.parseColor(value, element);\n }\n }\n } catch {\n // Fail gracefully\n }\n return null;\n}\n\n/**\n * Get current CSS variable value as a number\n */\nfunction getCSSVariableNumber(element: Element, prop: string): number {\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(element);\n const value = computed.getPropertyValue(prop).trim();\n if (value) {\n return parseValue(value).value;\n }\n }\n } catch {\n // Fail gracefully\n }\n return 0;\n}\n\n/**\n * Parse a drawSVG property for animation\n * Returns null if DrawSVGParser is not loaded\n */\nfunction parseDrawSVGProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedDrawSVGProperty | null {\n const drawSVGParser = SDKRegistry.drawSVG;\n if (!drawSVGParser) {\n return null; // DrawSVGParser not loaded\n }\n\n // Get path length (cached)\n const length = drawSVGParser.getPathLength(element);\n\n // Parse target (end) value\n const endDraw = drawSVGParser.parseDrawSVG(targetValue as string | { start?: number; end?: number }, element)\n || { start: 0, end: 1 };\n\n // Parse start value\n const startDraw = fromValue !== undefined\n ? drawSVGParser.parseDrawSVG(fromValue as string | { start?: number; end?: number }, element) || { start: 0, end: 1 }\n : drawSVGParser.getCurrentDrawSVG(element);\n\n return {\n type: 'drawSVG',\n property: prop,\n isTransform: false,\n startDraw,\n endDraw,\n length,\n };\n}\n\n/**\n * Parse a clip-path property for animation.\n *\n * Returns null when the ClipPathParser is not loaded — the caller falls back\n * to scalar handling, which won't produce a meaningful result for shape\n * functions but at least won't crash.\n *\n * Resolves missing FROM by reading the element's current computed clip-path\n * (mirrors how parseFilterProperty handles the same case). When the element\n * has no clip-path set, the START defaults to a copy of END so the property\n * has a stable starting structure.\n */\nfunction parseClipPathProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue,\n): ParsedClipPathProperty | null {\n const clipPathParser = SDKRegistry.clipPath;\n if (!clipPathParser) return null;\n\n // Resolve END:\n // - targetValue undefined (from-only) or empty → read current computed clip-path\n // - targetValue is a string → parse it\n // Matches scalar from-only behavior (animates back to the element's natural state).\n let endClipPath: ParsedClipPath | null;\n if (targetValue === undefined || targetValue === '') {\n endClipPath = clipPathParser.getCurrentClipPath(element);\n } else {\n endClipPath = clipPathParser.parseClipPath(String(targetValue));\n }\n\n // Resolve START:\n // - fromValue specified → parse it\n // - fromValue undefined (to-only) → read current computed clip-path\n // captureStartValues re-reads current at play time for to-only animations so\n // the start reflects the latest element state (matches filter/color behavior).\n let startClipPath: ParsedClipPath | null;\n if (fromValue !== undefined) {\n startClipPath = clipPathParser.parseClipPath(String(fromValue));\n } else {\n startClipPath = clipPathParser.getCurrentClipPath(element);\n }\n\n // If we couldn't parse end, this property is unrenderable.\n if (!endClipPath) return null;\n // If we couldn't capture a usable start, mirror end so render is at least stable.\n if (!startClipPath) startClipPath = endClipPath;\n\n return {\n type: 'clipPath',\n property: prop,\n isTransform: false,\n startClipPath,\n endClipPath,\n };\n}\n\n/**\n * Parse a path property for motion path animation\n * Supports path in TO only, FROM only, or both FROM and TO\n */\nfunction parsePathProperty(\n element: Element,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedPathProperty | null {\n // Path config can be in targetValue (TO), fromValue (FROM), or both\n const toConfig = targetValue as unknown as PathConfig | undefined;\n const fromConfig = fromValue as unknown as PathConfig | undefined;\n\n // Get the primary config (prefer TO, fall back to FROM)\n const primaryConfig = toConfig?.target ? toConfig : fromConfig;\n if (!primaryConfig?.target) {\n return null;\n }\n\n // Resolve path data from target (selector, element, or raw path data)\n const pathData = resolvePathData(primaryConfig.target);\n if (!pathData) {\n return null;\n }\n\n // Get path length\n const pathLength = getMotionPathLength(pathData);\n if (pathLength === 0) {\n return null;\n }\n\n // Get progress values - support FROM only, TO only, or both\n let startProgress: number;\n let endProgress: number;\n\n if (toConfig?.target && fromConfig?.target) {\n // Both FROM and TO specified\n startProgress = fromConfig.start ?? 0;\n endProgress = toConfig.end ?? 1;\n } else if (toConfig?.target) {\n // TO only - animate from start to end on the path\n startProgress = toConfig.start ?? 0;\n endProgress = toConfig.end ?? 1;\n } else {\n // FROM only - animate from the from position to end of path\n startProgress = fromConfig!.start ?? 0;\n endProgress = fromConfig!.end ?? 1;\n }\n\n // Get rotation setting (from either config)\n const rotate = toConfig?.rotate ?? fromConfig?.rotate ?? false;\n\n // Calculate element origin offset (alignAt - where on the element to position)\n const alignAt = toConfig?.alignAt ?? fromConfig?.alignAt ?? [50, 50];\n const alignOffset = calculateElementOffset(element, alignAt);\n\n // Calculate path offset (align - position path relative to another element)\n const alignTarget = toConfig?.align ?? fromConfig?.align;\n const pathOffset = calculatePathAlignOffset(alignTarget, pathData, element);\n\n return {\n type: 'path',\n property: prop,\n isTransform: false,\n pathData,\n pathLength,\n startProgress,\n endProgress,\n rotate,\n alignOffset,\n pathOffset,\n };\n}\n\n/**\n * Parse a property for animation\n * Handles both DOM Elements and plain objects\n */\nexport function parseProperty(\n target: AnimationTarget,\n prop: string,\n targetValue: PropertyValue,\n fromValue?: PropertyValue\n): ParsedProperty {\n const isTargetElement = target instanceof Element;\n\n // Handle CSS variables (DOM elements only)\n if (isTargetElement && isCSSVariable(prop)) {\n return parseCSSVariableProperty(target, prop, targetValue, fromValue);\n }\n\n // Handle color properties (DOM elements only, requires ColorParser)\n if (isTargetElement && isColorProp(prop)) {\n const colorParsed = parseColorProperty(target, prop, targetValue, fromValue);\n if (colorParsed) {\n return colorParsed;\n }\n // ColorParser not loaded - skip this property by returning a no-op scalar\n // The property will animate from 0 to 0 (no visible effect)\n }\n\n // Handle filter property (DOM elements only, requires FilterParser)\n if (isTargetElement && isFilterProp(prop)) {\n const filterParsed = parseFilterProperty(target, prop, targetValue, fromValue);\n if (filterParsed) {\n return filterParsed;\n }\n // FilterParser not loaded - skip this property\n }\n\n // Handle drawSVG property (DOM elements only, requires DrawSVGParser)\n if (isTargetElement && isDrawSVGProp(prop)) {\n const drawSVGParsed = parseDrawSVGProperty(target, prop, targetValue, fromValue);\n if (drawSVGParsed) {\n return drawSVGParsed;\n }\n // DrawSVGParser not loaded - skip this property\n }\n\n // Handle clip-path property (DOM elements only, requires ClipPathParser)\n if (isTargetElement && isClipPathProp(prop)) {\n const clipPathParsed = parseClipPathProperty(target, prop, targetValue, fromValue);\n if (clipPathParsed) {\n return clipPathParsed;\n }\n // ClipPathParser not loaded or value unparseable — skip this property\n // (falls through to scalar, which will silently no-op for shape values).\n }\n\n // Handle path property (motion path, DOM elements only)\n if (isTargetElement && isPathProp(prop)) {\n const parsed = parsePathProperty(target, prop, targetValue, fromValue);\n if (parsed) {\n return parsed;\n }\n // Fall through to scalar if path parsing failed\n }\n\n // Resolve \"auto\" values to concrete pixel measurements before scalar parsing.\n // \"auto\" is only valid for width/height on DOM elements. We measure the element's\n // natural dimension and substitute a px value so the rest of the pipeline can\n // interpolate normally.\n const targetIsAuto = isAutoValue(targetValue);\n const fromIsAuto = fromValue !== undefined && isAutoValue(fromValue);\n\n if (isTargetElement && (targetIsAuto || fromIsAuto) && AUTO_PROPS.has(prop)) {\n const autoPx = resolveAutoValue(target, prop);\n\n // Replace \"auto\" with the resolved pixel value, keep non-auto side as-is\n const resolvedTarget: PropertyValue = targetIsAuto ? autoPx : targetValue;\n const resolvedFrom: PropertyValue | undefined = fromIsAuto ? autoPx : fromValue;\n\n // Re-enter parseProperty with concrete values (no infinite recursion since\n // the resolved values are numbers, not \"auto\")\n return parseProperty(target, prop, resolvedTarget, resolvedFrom);\n }\n\n // Scalar properties work for both DOM elements and plain objects\n const isTransform = isTransformProp(prop);\n const isRelative = fromValue === undefined && isRelativeValue(targetValue);\n\n let startValue: number;\n let endValue: number;\n let unit: string;\n\n if (fromValue !== undefined) {\n // FromTo animation - from value is specified\n const parsedFrom = parseValue(fromValue);\n\n if (targetValue !== undefined) {\n // Both from and to specified — use explicit values\n const parsedTo = parseValue(targetValue);\n startValue = parsedFrom.value;\n endValue = parsedTo.value;\n unit = parsedTo.unit || parsedFrom.unit || getDefaultUnit(prop);\n } else {\n // From-only: property exists in 'from' but not 'to'.\n // Resolve 'to' from the element's current/computed value so the\n // animation returns to the natural state instead of snapping to 0.\n startValue = parsedFrom.value;\n unit = parsedFrom.unit || getDefaultUnit(prop);\n // getCurrentValue returns pixels for layout properties (width, height, etc.)\n // but the animation unit may be %, em, rem, etc. — convert to match.\n endValue = getCurrentValueInUnit(target, prop, unit);\n }\n } else if (isRelative) {\n // Relative animation (+=, -=, *=, /=)\n const relativeOp = getRelativeOperator(targetValue as string)!;\n const current = getCurrentValue(target, prop);\n const parsed = parseValue(targetValue);\n\n startValue = current;\n endValue = calculateRelativeValue(current, parsed.value, relativeOp);\n unit = parsed.unit || getDefaultUnit(prop);\n } else {\n // Regular \"to\" animation\n const parsed = parseValue(targetValue);\n unit = parsed.unit || getDefaultUnit(prop);\n // getCurrentValue returns pixels for layout properties (width, height, etc.)\n // but the animation unit may be %, em, rem, etc. — convert to match.\n startValue = getCurrentValueInUnit(target, prop, unit);\n endValue = parsed.value;\n }\n\n return {\n type: 'scalar',\n property: prop,\n isTransform,\n startValue,\n endValue,\n unit,\n };\n}\n",
|
|
17
17
|
"/**\n * StyleReset - Clear animation-related inline styles from elements\n *\n * Only clears styles that our SDK has animated or set for pinning,\n * preserving user's original inline styles.\n * Uses registries to track which CSS properties have been set by the SDK.\n */\n\nimport { isTransformProp, isFilterProp, isCSSVariable, isDrawSVGProp } from './PropertyParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\n// ============================================================================\n// Generic prop registry factory (internal)\n// ============================================================================\n\ninterface PropRegistry {\n register(element: Element, prop: string): void;\n registerMany(element: Element, props: string[]): void;\n get(element: Element): Set<string> | undefined;\n clearRegistry(element: Element): void;\n}\n\n/**\n * Creates a WeakMap-backed registry that tracks which CSS properties have been\n * set on each element. The WeakMap ensures elements are garbage collected when\n * removed from the DOM without needing explicit cleanup.\n */\nfunction createPropRegistry(): PropRegistry {\n const registry = new WeakMap<Element, Set<string>>();\n\n return {\n register(element: Element, prop: string): void {\n let props = registry.get(element);\n if (!props) {\n props = new Set<string>();\n registry.set(element, props);\n }\n props.add(prop);\n },\n\n registerMany(element: Element, props: string[]): void {\n for (const prop of props) {\n this.register(element, prop);\n }\n },\n\n get(element: Element): Set<string> | undefined {\n return registry.get(element);\n },\n\n clearRegistry(element: Element): void {\n registry.delete(element);\n },\n };\n}\n\n// ============================================================================\n// Registry instances\n// ============================================================================\n\n/**\n * Tracks which CSS properties have been animated on each element.\n * Uses WeakMap so elements can be garbage collected when removed from DOM.\n */\nconst animatedProps = createPropRegistry();\n\n/**\n * Registry of original inline transform values for animated elements.\n */\nconst originalTransformRegistry = new WeakMap<Element, string | null>();\n\n/**\n * Tracks which pin-related CSS properties have been set on each element.\n * Separate from animation props because pin styles are set/restored by PinManager.\n */\nconst pinnedProps = createPropRegistry();\n\n/**\n * Map animation property names to actual CSS property names.\n * Transform properties (x, y, rotate, scale, etc.) all map to 'transform'.\n * Filter property maps to 'filter'.\n * DrawSVG maps to strokeDasharray and strokeDashoffset.\n * Other properties map directly.\n */\nfunction animPropToCssProps(prop: string): string[] {\n if (isTransformProp(prop)) {\n return ['transform'];\n }\n if (isFilterProp(prop)) {\n return ['filter'];\n }\n if (isDrawSVGProp(prop)) {\n return ['strokeDasharray', 'strokeDashoffset'];\n }\n // CSS variables and other properties map directly\n return [prop];\n}\n\n/**\n * Register that a CSS property has been animated on an element.\n * Call this when setting inline styles during animation.\n */\nexport function registerAnimatedProp(element: Element, prop: string): void {\n // Capture the element's original inline transform before the first animation\n if (isTransformProp(prop) && !originalTransformRegistry.has(element)) {\n if (element instanceof HTMLElement || element instanceof SVGElement) {\n const inlineTransform = (element as HTMLElement).style.transform;\n originalTransformRegistry.set(element, inlineTransform || null);\n }\n }\n\n // Normalize animation prop name(s) to CSS property name(s) and register each\n const cssProps = animPropToCssProps(prop);\n for (const cssProp of cssProps) {\n animatedProps.register(element, cssProp);\n }\n}\n\n/**\n * Register multiple animated properties at once.\n */\nexport function registerAnimatedProps(element: Element, props: string[]): void {\n for (const prop of props) {\n registerAnimatedProp(element, prop);\n }\n}\n\n/**\n * Get all CSS properties that have been animated on an element.\n */\nexport function getAnimatedProps(element: Element): Set<string> {\n return animatedProps.get(element) || new Set();\n}\n\n/**\n * Clear tracking for an element (called after Motion.reset).\n */\nexport function clearAnimatedPropsRegistry(element: Element): void {\n animatedProps.clearRegistry(element);\n originalTransformRegistry.delete(element);\n}\n\n/**\n * Clear specific animation properties from an element's inline styles.\n * Only clears the properties specified, preserving other inline styles.\n *\n * @param element - The element to clear styles from\n * @param props - Animation property names to clear (e.g., 'x', 'opacity', 'backgroundColor')\n */\nexport function clearAnimationStylesForProps(element: Element, props: string[]): void {\n if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) return;\n\n // Convert animation props to CSS props and dedupe\n const cssPropsSet = new Set<string>();\n for (const prop of props) {\n for (const cssProp of animPropToCssProps(prop)) {\n cssPropsSet.add(cssProp);\n }\n }\n\n // Clear each CSS property\n for (const cssProp of cssPropsSet) {\n if (isCSSVariable(cssProp)) {\n // CSS variables need special handling\n element.style.removeProperty(cssProp);\n } else {\n // Regular properties\n (element.style as unknown as Record<string, string>)[cssProp] = '';\n }\n }\n}\n\n/**\n * Clear all animation-related inline styles that our SDK has set on an element.\n * Only clears properties tracked in the registry, preserving user's original inline styles.\n */\nexport function clearAnimationStyles(element: Element): void {\n if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) return;\n\n const trackedProps = animatedProps.get(element);\n if (!trackedProps || trackedProps.size === 0) {\n return; // Nothing to clear\n }\n\n // Clear each tracked CSS property\n for (const cssProp of trackedProps) {\n if (cssProp === 'transform') {\n // Restore the element's original inline transform rather than simply blanking it\n const originalTransform = originalTransformRegistry.get(element);\n if (originalTransform !== undefined) {\n if (originalTransform) {\n element.style.transform = originalTransform;\n } else {\n element.style.removeProperty('transform');\n }\n continue;\n }\n }\n if (isCSSVariable(cssProp)) {\n element.style.removeProperty(cssProp);\n } else {\n (element.style as unknown as Record<string, string>)[cssProp] = '';\n }\n }\n}\n\n/**\n * Clear animation styles and remove from registry (full reset).\n * Use this for Motion.reset() to completely clean up.\n */\nexport function clearAnimationStylesAndUnregister(element: Element): void {\n clearAnimationStyles(element);\n clearAnimatedPropsRegistry(element);\n}\n\n/**\n * Clear animation styles from multiple elements\n */\nexport function clearAnimationStylesAll(elements: Element[]): void {\n for (const element of elements) {\n clearAnimationStyles(element);\n }\n}\n\n// ============================================================================\n// Pin Style Tracking\n// ============================================================================\n\n/**\n * Register that a pin-related CSS property has been set on an element.\n * Call this when PinManager sets styles during pinning.\n */\nexport function registerPinnedProp(element: Element, prop: string): void {\n pinnedProps.register(element, prop);\n}\n\n/**\n * Register multiple pinned properties at once.\n */\nexport function registerPinnedProps(element: Element, props: string[]): void {\n pinnedProps.registerMany(element, props);\n}\n\n/**\n * Get all pin-related CSS properties that have been set on an element.\n */\nexport function getPinnedProps(element: Element): Set<string> {\n return pinnedProps.get(element) || new Set();\n}\n\n/**\n * Clear tracking for pinned properties on an element.\n */\nexport function clearPinnedPropsRegistry(element: Element): void {\n pinnedProps.clearRegistry(element);\n}\n\n/**\n * Clear pin-related inline styles that our SDK has set on an element.\n * Only clears properties tracked in the pin registry.\n */\nexport function clearPinStyles(element: Element): void {\n if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) return;\n\n const trackedProps = pinnedProps.get(element);\n if (!trackedProps || trackedProps.size === 0) {\n return; // Nothing to clear\n }\n\n // Clear each tracked pin property\n for (const prop of trackedProps) {\n (element.style as unknown as Record<string, string>)[prop] = '';\n }\n}\n\n/**\n * Clear pin styles and remove from registry.\n * Use this for Motion.reset() to completely clean up pin styles.\n */\nexport function clearPinStylesAndUnregister(element: Element): void {\n clearPinStyles(element);\n clearPinnedPropsRegistry(element);\n}\n\n// Self-register with the registry\nSDKRegistry.styleReset = {\n registerAnimatedProps,\n clearAnimationStylesForProps,\n clearAnimationStylesAndUnregister,\n clearPinStylesAndUnregister,\n};\n",
|
|
18
18
|
"/**\n * PinManager\n *\n * Handles element pinning during scroll animations.\n * Extracted from ScrollTrigger for better separation of concerns.\n *\n * Two pinning strategies based on scroller type:\n * 1. Window scroller: position:fixed (seamless, no jitter)\n * 2. Custom scroller: translate3d (counters scroll movement)\n *\n * State machine: before → pinned → after\n */\n\nimport { registerPinnedProps, clearPinnedPropsRegistry } from '../utils/StyleReset';\nimport { setPinOffset, clearPinOffset, buildTransformString } from '../render/TransformCache';\n\ninterface OriginalStyles {\n position: string;\n top: string;\n left: string;\n width: string;\n marginTop: string;\n marginBottom: string;\n marginLeft: string;\n marginRight: string;\n zIndex: string;\n}\n\n/**\n * Tracks an ancestor element whose CSS properties were overridden to allow\n * position:fixed pinning to work correctly.\n *\n * Several CSS properties on ancestors break position:fixed by creating a\n * new containing block. When position:fixed is inside a containing block,\n * it positions relative to that ancestor instead of the viewport, causing\n * pinned sections to scroll off-screen and become invisible.\n *\n * Properties that create a containing block for fixed elements:\n * - `filter` (even `blur(0px)`) — common in animation libraries\n * - `transform` (even `translate(0)`) — used by smooth scroll (Lenis)\n * - `perspective` — 3D transforms\n * - `will-change: transform|filter|perspective`\n * - `contain: paint|layout|strict|content`\n * - `backdrop-filter`\n * - `overflow: clip` — clips fixed descendants (different from hidden)\n *\n * We detect these ancestors and temporarily override them during pin.\n */\ninterface FixedBreakingAncestor {\n element: HTMLElement;\n // Which properties were overridden (so we only restore what we changed)\n overrides: {\n property: string;\n originalValue: string;\n }[];\n}\n\nexport type PinState = 'before' | 'pinned' | 'after';\n\nexport class PinManager {\n // -------------------------------------------------------------------------\n // Global registry: maps each DOM element to the single PinManager that owns it.\n // Prevents multiple ScrollTrigger instances (e.g. from each:true + pin string)\n // from wrapping the same pinned element in nested spacers.\n // -------------------------------------------------------------------------\n private static _registry: Map<HTMLElement, PinManager> = new Map();\n\n // Tracks which ScrollTrigger IDs are currently referencing each PinManager.\n // The shared manager is only cleaned up when the last user calls release().\n private static _users: Map<HTMLElement, Set<number>> = new Map();\n\n /**\n * Get or create a shared PinManager for the given element.\n * If another ScrollTrigger already pinned this element, returns that same\n * PinManager so no additional spacer is created. Tracks `id` as a user.\n */\n static getOrCreate(id: number, element: HTMLElement): PinManager {\n let manager = PinManager._registry.get(element);\n if (!manager) {\n manager = new PinManager(id);\n PinManager._registry.set(element, manager);\n }\n // Register this ScrollTrigger as a user of the shared manager\n let users = PinManager._users.get(element);\n if (!users) {\n users = new Set();\n PinManager._users.set(element, users);\n }\n users.add(id);\n return manager;\n }\n\n /**\n * Release a ScrollTrigger's reference to a pinned element's PinManager.\n * When the last user releases, cleanup() is called and registry entries removed.\n */\n static release(id: number, element: HTMLElement): void {\n const users = PinManager._users.get(element);\n if (!users) return;\n users.delete(id);\n if (users.size === 0) {\n // Last user released — remove from registry first, then tear down the pin\n const manager = PinManager._registry.get(element);\n PinManager._registry.delete(element);\n PinManager._users.delete(element);\n manager?.cleanup();\n }\n }\n\n // Unique ID for spacer data attribute\n private _id: number;\n\n // Element being pinned\n private _element: HTMLElement | null = null;\n\n // Saved styles for restoration\n private _originalStyles: OriginalStyles | null = null;\n\n // Spacer element to preserve layout during fixed pin\n private _spacer: HTMLElement | null = null;\n\n // Current pin state\n private _currentState: PinState = 'before';\n\n // Pin range (scroll positions)\n private _pinStart: number = 0;\n private _pinEnd: number = 0;\n private _pinDistance: number = 0;\n\n // Strategy flag\n private _useFixedPin: boolean = false;\n\n // Pin spacing mode\n private _pinSpacing: boolean | 'margin' | 'padding' | undefined;\n\n // Element height at setup time (used for 'after' offset calculation with sibling spacer)\n private _elementHeight: number = 0;\n\n // Fixed position coordinates (for fixed pin strategy)\n private _fixedTop: number = 0;\n private _fixedLeft: number = 0;\n private _width: number = 0;\n\n // Ancestors with properties that break position:fixed pinning\n private _fixedBreakingAncestors: FixedBreakingAncestor[] = [];\n\n // Body-pin strategy: avoid invalid DOM wrapping (html > div > body)\n private _isBodyPin: boolean = false;\n private _originalHtmlHeight: string = '';\n private _originalHtmlOverflow: string = '';\n\n constructor(id: number) {\n this._id = id;\n }\n\n /**\n * Setup pinning for an element\n *\n * @param element - The element to pin\n * @param pinStart - Scroll position where pin starts\n * @param pinEnd - Scroll position where pin ends\n * @param scrollerStartOffset - Viewport offset for fixed positioning\n * @param useFixedPin - true for window scroller (position:fixed), false for custom (transform)\n */\n setup(\n element: HTMLElement,\n pinStart: number,\n pinEnd: number,\n scrollerStartOffset: number,\n useFixedPin: boolean,\n pinSpacing?: boolean | 'margin' | 'padding'\n ): void {\n if (typeof document === 'undefined') return;\n\n // Detect body pin before the isRefresh check so branching is correct on first call.\n // Wrapping <body> in a spacer produces invalid DOM (html → div → body), so body\n // pins use a different strategy: html height extension instead of a spacer wrapper.\n this._isBodyPin = element === document.body;\n\n this._element = element;\n this._pinStart = pinStart;\n this._pinEnd = pinEnd;\n // Save old pin distance BEFORE updating so the margin-mode spacer calculation\n // can subtract it from the spacer's accumulated margin-bottom and recover the\n // original element margin, preventing snowball growth on repeated refreshes.\n const oldPinDistance = this._pinDistance;\n this._pinDistance = pinEnd - pinStart;\n this._useFixedPin = useFixedPin;\n this._pinSpacing = pinSpacing;\n\n // Body pin has no spacer; use originalStyles presence to detect refresh instead.\n const isRefresh = this._isBodyPin\n ? this._originalStyles !== null\n : this._spacer !== null;\n\n // If refreshing while the element is position:fixed (pinned state), temporarily\n // restore it to document flow before measuring. A fixed element returns\n // viewport-relative coordinates from getBoundingClientRect(), so reading\n // elementRect.left / elementRect.width while fixed would produce stale\n // viewport-relative values for _fixedLeft and _width — wrong after a resize.\n // Unpinning here ensures elementRect reflects the element's actual post-resize\n // natural dimensions. _onScroll() at the end of ScrollTrigger.refresh() will\n // re-derive the correct state and call _updateFixed() to re-apply the pin with\n // the freshly-measured values.\n if (isRefresh && this._currentState === 'pinned' && useFixedPin) {\n this._updateFixed('before');\n }\n\n // On refresh, temporarily clear the spacer's explicit width so it reflows to\n // its container's natural width. Without this, the spacer retains its old\n // pixel width from the previous setup, constraining the element inside it and\n // causing getBoundingClientRect() to return a stale width after a window resize.\n if (isRefresh && this._spacer) {\n this._spacer.style.width = '';\n }\n\n // Body-pin refresh: temporarily reset html height before measuring.\n // On first setup we set html.height = bodyHeight + pinDistance. If the body's\n // CSS contains `height: 100%` it will inherit the inflated html height, causing\n // getBoundingClientRect().height to return (bodyHeight + pinDistance) instead of\n // the body's natural content height. Re-applying the formula with the inflated\n // value produces html.height = (bodyHeight + pinDistance) + pinDistance — doubling\n // the scroll room (\"double pin spacing\" bug). Resetting html.height to its\n // original value before measuring ensures we always read the body's natural height.\n const savedHtmlHeight = isRefresh && this._isBodyPin\n ? document.documentElement.style.height\n : null;\n if (savedHtmlHeight !== null) {\n document.documentElement.style.height = this._originalHtmlHeight;\n }\n\n // Always measure dimensions from the element itself, never the spacer.\n // The spacer's BCR height includes padding-bottom (= pinDistance) because its\n // box-sizing is content-box, so using spacer.getBoundingClientRect().height would\n // cause _elementHeight to snowball on each refresh cycle:\n // height(n+1) = height(n) + pinDistance\n // The element's BCR height is its natural content height even while position:fixed.\n const elementRect = element.getBoundingClientRect();\n\n // Restore html height immediately after measuring so the rest of setup() and\n // any concurrent scroll handlers see the (soon to be overwritten) correct value\n // rather than the temporarily-cleared original.\n if (savedHtmlHeight !== null) {\n document.documentElement.style.height = savedHtmlHeight;\n }\n\n // Margins were transferred to the spacer during first setup, so read them from\n // the spacer on refresh — the element's own margins are zeroed at that point.\n const computedStyle = (isRefresh && this._spacer)\n ? window.getComputedStyle(this._spacer)\n : window.getComputedStyle(element);\n\n // Store the element's actual content height (never the spacer's inflated height).\n this._elementHeight = elementRect.height;\n\n // Only capture original styles on first setup, not on refresh\n if (!isRefresh) {\n this._originalStyles = {\n position: element.style.position,\n top: element.style.top,\n left: element.style.left,\n width: element.style.width,\n marginTop: element.style.marginTop,\n marginBottom: element.style.marginBottom,\n marginLeft: element.style.marginLeft,\n marginRight: element.style.marginRight,\n zIndex: element.style.zIndex,\n };\n }\n\n if (useFixedPin) {\n // _fixedTop is viewport-relative and doesn't depend on DOM layout.\n // _fixedLeft and _width are measured AFTER the spacer is in place (see below)\n // so the coordinates reflect the element's actual pinned position, not its\n // pre-spacer position which may differ in flex/grid contexts.\n this._fixedTop = scrollerStartOffset;\n\n if (this._isBodyPin) {\n // Body pin has no spacer, so measuring now gives the correct position.\n this._fixedLeft = elementRect.left;\n this._width = elementRect.width;\n // ----------------------------------------------------------------\n // Body-pin strategy: no spacer (wrapping body in a div is invalid\n // DOM). Instead, extend documentElement height to create the same\n // amount of scroll room that a spacer's padding-bottom would give.\n // ----------------------------------------------------------------\n if (!isRefresh) {\n this._originalHtmlHeight = document.documentElement.style.height;\n this._originalHtmlOverflow = document.documentElement.style.overflow;\n this._detectFixedBreakingAncestors(element);\n }\n document.documentElement.style.height = `${this._elementHeight + this._pinDistance}px`;\n } else {\n // ----------------------------------------------------------------\n // Regular pin: spacer wrapper strategy (GSAP-compatible).\n // The spacer wraps the element, holds its place in document flow,\n // and carries the extra pinDistance via padding or margin-bottom.\n // ----------------------------------------------------------------\n const resolved = this._resolvePinSpacing();\n\n // Capture the element's computed display and flex-direction so the spacer\n // participates in the parent layout (flex/grid) exactly as the original\n // element did. If the spacer were forced to 'block' it would occupy a\n // different position in a flex/grid context, causing a jump when the element\n // switches to position:fixed using coordinates from the pre-spacer layout.\n //\n // flex-direction MUST also be copied: when the element has `display: flex`\n // the spacer becomes a flex container wrapping the element as a flex item.\n // Without an explicit flex-direction the spacer defaults to `row`, causing\n // the element to shrink-wrap to its content width instead of stretching to\n // the spacer's full width. On refresh (when spacer width is temporarily\n // cleared for re-measurement) this produces a wrong element width that then\n // gets locked into the spacer — the \"pin jump-to-left\" bug.\n const elementComputed = window.getComputedStyle(element);\n const elementDisplay = elementComputed.display;\n const elementFlexDir = elementComputed.flexDirection;\n\n // Build spacer CSS — the spacer wraps the element (GSAP-compatible pattern).\n // It replaces the element in the document flow and provides pinDistance of\n // extra scroll room via padding or margin.\n // Use elementRect dimensions so the spacer content-height always equals the\n // element's natural height, regardless of refresh cycles.\n // Detect flex parent so we can set flex-shrink: 0 on the spacer\n // (prevents the flex container from collapsing the spacer below its\n // needed size, matching GSAP's approach for flex contexts).\n // On first setup, parentElement is the actual parent; on refresh it's\n // the spacer (which may also be flex since it copies element display).\n // Either way, flex-shrink: 0 is harmless on non-flex items.\n const parentEl = isRefresh && this._spacer?.parentElement\n ? this._spacer.parentElement\n : element.parentElement;\n const isFlexParent = parentEl\n ? /^(inline-)?flex$/.test(getComputedStyle(parentEl).display)\n : false;\n\n // When the spacer copies a flex display, also copy flex-direction so the\n // pinned element stretches correctly as a flex item (column → stretch width,\n // row → stretch height). For non-flex displays this is harmless.\n const isFlexDisplay = /^(inline-)?flex$/.test(elementDisplay);\n\n let spacerCSS = `\n display: ${elementDisplay};\n ${isFlexDisplay ? `flex-direction: ${elementFlexDir};` : ''}\n box-sizing: content-box;\n width: ${elementRect.width}px;\n height: ${elementRect.height}px;\n margin-top: ${computedStyle.marginTop};\n margin-left: ${computedStyle.marginLeft};\n margin-right: ${computedStyle.marginRight};\n overflow: visible;\n ${isFlexParent ? 'flex-shrink: 0;' : ''}\n `;\n\n if (resolved === 'padding') {\n // Add pin distance as padding-bottom on spacer to push content down\n spacerCSS += `padding-bottom: ${this._pinDistance}px;`;\n spacerCSS += `margin-bottom: ${computedStyle.marginBottom};`;\n } else if (resolved === 'margin') {\n // Add pin distance as margin-bottom on spacer to push content down.\n // On refresh, computedStyle is from the spacer, whose margin-bottom already\n // includes (original + oldPinDistance) from the previous setup call.\n // Subtracting oldPinDistance recovers the original element margin before\n // adding the new pinDistance — this prevents snowball growth across refreshes.\n const spacerMarginBottom = parseFloat(computedStyle.marginBottom) || 0;\n const originalMarginBottom = isRefresh\n ? spacerMarginBottom - oldPinDistance\n : spacerMarginBottom;\n spacerCSS += `margin-bottom: ${originalMarginBottom + this._pinDistance}px;`;\n } else {\n // No spacing — spacer only preserves element's original space\n spacerCSS += `margin-bottom: ${computedStyle.marginBottom};`;\n }\n\n if (!this._spacer) {\n // First setup: detect ancestors that would clip position:fixed\n this._detectFixedBreakingAncestors(element);\n\n // First setup: create spacer and wrap element inside it\n this._spacer = document.createElement('div');\n element.parentNode?.insertBefore(this._spacer, element);\n this._spacer.appendChild(element);\n\n // Transfer margins to spacer — element's margins are now zero\n // so they don't double-count within the wrapper\n element.style.marginTop = '0';\n element.style.marginBottom = '0';\n element.style.marginLeft = '0';\n element.style.marginRight = '0';\n }\n\n // Update spacer styles (works for both first and subsequent setups / refresh)\n this._spacer.style.cssText = spacerCSS;\n this._spacer.setAttribute('data-scrolltrigger-spacer', String(this._id));\n\n // Measure _fixedLeft and _width AFTER the spacer is in the DOM and the\n // element is inside it. Inserting the spacer can trigger a layout reflow\n // (especially in flex/grid containers), so coordinates measured before\n // insertion would not match where the element actually sits during the pin.\n // Use the SPACER's bounding rect (not the element's) because the element\n // may have CSS transforms active from an animation `from` state (e.g.\n // `x: '-13%'`), which shifts its visual bounding box. When the element\n // is later pinned with `position: fixed; left: _fixedLeft`, the transform\n // is still applied on top, causing a double offset. The spacer has no\n // transforms and faithfully represents the element's natural layout position.\n const spacerRect = this._spacer!.getBoundingClientRect();\n this._fixedLeft = spacerRect.left;\n this._width = spacerRect.width;\n }\n }\n\n this._currentState = 'before';\n }\n\n /**\n * Update pin state based on scroll position\n */\n update(scrollTop: number): void {\n if (!this._element) return;\n\n const newState: PinState =\n scrollTop < this._pinStart\n ? 'before'\n : scrollTop < this._pinEnd\n ? 'pinned'\n : 'after';\n\n if (this._useFixedPin) {\n // Fixed pinning: only update DOM on state change\n if (newState !== this._currentState) {\n this._updateFixed(newState);\n this._currentState = newState;\n }\n } else {\n // Transform pinning: must update transform continuously during 'pinned' state\n if (newState !== this._currentState || newState === 'pinned') {\n this._updateTransform(newState, scrollTop);\n this._currentState = newState;\n }\n }\n }\n\n /**\n * Clean up pin - restore original styles and remove spacer\n */\n cleanup(): void {\n // Safety guard: if cleanup() is called directly (bypassing release()),\n // remove this manager from the registry so no stale references remain.\n if (this._element && PinManager._registry.get(this._element) === this) {\n PinManager._registry.delete(this._element);\n PinManager._users.delete(this._element);\n }\n\n if (this._element && this._originalStyles) {\n const el = this._element;\n\n // Remove pin offset from TransformCache so subsequent animation renders\n // do not include the pin translation\n clearPinOffset(el);\n\n // Restore all original styles including margins (transferred to spacer during setup)\n el.style.position = this._originalStyles.position;\n el.style.top = this._originalStyles.top;\n el.style.left = this._originalStyles.left;\n el.style.width = this._originalStyles.width;\n el.style.marginTop = this._originalStyles.marginTop;\n el.style.marginBottom = this._originalStyles.marginBottom;\n el.style.marginLeft = this._originalStyles.marginLeft;\n el.style.marginRight = this._originalStyles.marginRight;\n el.style.zIndex = this._originalStyles.zIndex;\n el.style.transform = '';\n\n // Clear pin styles from registry since we've restored originals\n clearPinnedPropsRegistry(el);\n }\n\n if (this._isBodyPin) {\n // Body pin: restore documentElement dimensions (no spacer to unwrap)\n if (typeof document !== 'undefined') {\n document.documentElement.style.height = this._originalHtmlHeight;\n document.documentElement.style.overflow = this._originalHtmlOverflow;\n }\n } else {\n // Regular pin: unwrap element from spacer and remove spacer\n if (this._spacer?.parentNode && this._element) {\n this._spacer.parentNode.insertBefore(this._element, this._spacer);\n this._spacer.parentNode.removeChild(this._spacer);\n }\n }\n\n // Restore any overridden clipping ancestors before clearing references\n this._restoreFixedBreakingAncestors();\n this._fixedBreakingAncestors = [];\n\n this._element = null;\n this._originalStyles = null;\n this._spacer = null;\n this._currentState = 'before';\n this._pinSpacing = undefined;\n this._isBodyPin = false;\n this._originalHtmlHeight = '';\n this._originalHtmlOverflow = '';\n }\n\n /**\n * Get the pinned element\n */\n getElement(): HTMLElement | null {\n return this._element;\n }\n\n /**\n * Get the pin distance (scroll range over which element is pinned)\n */\n getPinDistance(): number {\n return this._pinDistance;\n }\n\n /**\n * Get current pin state\n */\n getState(): PinState {\n return this._currentState;\n }\n\n /**\n * Get a layout rect for trigger-position calculations.\n *\n * Used by ScrollTrigger._calculateTriggerPositions() and refresh() to obtain the\n * correct document-relative position of the pinned element when it is position:fixed\n * — querying the element itself in that state returns viewport-relative coordinates,\n * which produce incorrect trigger positions.\n *\n * Returns a synthetic rect that combines:\n * - top/left/width from the spacer (document-flow position)\n * - height from _elementHeight (the element's natural content height)\n *\n * The spacer's own BCR height is NOT used because it includes padding-bottom\n * (= pinDistance, added for pin spacing), which would inflate callers' end-position\n * calculations and grow the pinDistance on every refresh cycle.\n *\n * Returns `null` when the spacer is not in the DOM (transform-pin strategy or\n * before first setup), signalling the caller to fall back to the element's own rect.\n */\n getLayoutRect(): { top: number; left: number; width: number; height: number; bottom: number; right: number } | null {\n if (this._spacer?.parentNode) {\n const spacerRect = (this._spacer as HTMLElement).getBoundingClientRect();\n // Use spacer's position for document-flow coordinates, but _elementHeight\n // for the height so callers see the element's natural dimensions — not the\n // spacer's inflated height that includes padding-bottom = pinDistance.\n return {\n top: spacerRect.top,\n left: spacerRect.left,\n width: spacerRect.width,\n height: this._elementHeight,\n bottom: spacerRect.top + this._elementHeight,\n right: spacerRect.left + spacerRect.width,\n };\n }\n return null;\n }\n\n /**\n * Whether this manager is using the body-pin strategy (html height extension).\n */\n isBodyPin(): boolean {\n return this._isBodyPin;\n }\n\n /**\n * Temporarily reset html.style.height to its original (pre-pin) value so that\n * callers can measure the body's natural height without the inflation caused\n * by the pin spacing. Returns a restore function that re-applies the current\n * (inflated) height. No-op for non-body pins.\n *\n * Used by ScrollTrigger.refresh() before _calculateTriggerPositions() so\n * trigger positions are derived from the body's real content height.\n */\n suspendHtmlHeight(): (() => void) | null {\n if (!this._isBodyPin) return null;\n const current = document.documentElement.style.height;\n document.documentElement.style.height = this._originalHtmlHeight;\n return () => {\n document.documentElement.style.height = current;\n };\n }\n\n /**\n * Check if using fixed pin strategy\n */\n isFixedPin(): boolean {\n return this._useFixedPin;\n }\n\n /**\n * Detect ancestor elements with CSS properties that break position:fixed.\n *\n * Any of these on an ancestor creates a new containing block, causing\n * position:fixed to position relative to that ancestor instead of the\n * viewport — making pinned elements scroll off-screen:\n *\n * - filter (even blur(0px)) — set by animation libraries as initial state\n * - transform (even translate(0)) — set by smooth scroll libraries (Lenis)\n * - will-change: transform/filter/perspective\n * - perspective\n * - contain: paint/layout/strict/content\n * - backdrop-filter\n * - overflow: clip — clips fixed descendants at element's padding box\n *\n * Only needs to run once during first setup (not on refresh).\n */\n private _detectFixedBreakingAncestors(element: HTMLElement): void {\n this._fixedBreakingAncestors = [];\n let ancestor = element.parentElement;\n\n while (ancestor && ancestor !== document.documentElement) {\n const cs = window.getComputedStyle(ancestor);\n const overrides: FixedBreakingAncestor['overrides'] = [];\n\n // Check filter (any value other than 'none' creates containing block)\n if (cs.filter && cs.filter !== 'none') {\n overrides.push({ property: 'filter', originalValue: '' });\n }\n\n // Check transform\n if (cs.transform && cs.transform !== 'none') {\n overrides.push({ property: 'transform', originalValue: '' });\n }\n\n // Check perspective\n if (cs.perspective && cs.perspective !== 'none') {\n overrides.push({ property: 'perspective', originalValue: '' });\n }\n\n // Check will-change for containing-block-creating values\n if (cs.willChange && cs.willChange !== 'auto') {\n const wcValues = cs.willChange.split(',').map(v => v.trim());\n if (wcValues.some(v => v === 'transform' || v === 'filter' || v === 'perspective')) {\n overrides.push({ property: 'willChange', originalValue: '' });\n }\n }\n\n // Check contain (paint, layout, strict, content all create containing block)\n if (cs.contain && cs.contain !== 'none') {\n const containsBlockCreating = ['paint', 'layout', 'strict', 'content'].some(\n v => cs.contain.includes(v)\n );\n if (containsBlockCreating) {\n overrides.push({ property: 'contain', originalValue: '' });\n }\n }\n\n // Check backdrop-filter\n if (cs.backdropFilter && cs.backdropFilter !== 'none') {\n overrides.push({ property: 'backdropFilter', originalValue: '' });\n }\n\n // Check overflow:clip (clips fixed descendants)\n if (cs.overflow === 'clip' || cs.overflowX === 'clip' || cs.overflowY === 'clip') {\n // Track each axis separately for precise restoration\n if (cs.overflowY === 'clip') {\n overrides.push({ property: 'overflowY', originalValue: '' });\n }\n if (cs.overflowX === 'clip') {\n overrides.push({ property: 'overflowX', originalValue: '' });\n }\n }\n\n if (overrides.length > 0) {\n this._fixedBreakingAncestors.push({ element: ancestor, overrides });\n }\n\n ancestor = ancestor.parentElement;\n }\n }\n\n /**\n * Override properties on ancestors that break position:fixed.\n *\n * IMPORTANT: Captures the current inline style value RIGHT BEFORE overriding,\n * not at detection time. This prevents restoring stale values — e.g. if an\n * animation set `filter: blur(5px)` at detection time but has since completed\n * and the SDK cleaned it to `none`, we'd wrongly restore `blur(5px)` on unpin.\n *\n * Safe fallback values:\n * - filter/transform/perspective → 'none'\n * - will-change → 'auto'\n * - contain → 'none'\n * - overflow-x:clip → 'hidden' (still prevents horizontal scrollbar)\n * - overflow-y:clip → 'visible'\n */\n private _overrideFixedBreakingAncestors(): void {\n for (const { element, overrides } of this._fixedBreakingAncestors) {\n for (const override of overrides) {\n // Snapshot the CURRENT inline value just before we override it.\n // This ensures restore() puts back the most recent state, not a\n // stale value from when detection ran (which may have been during\n // an animation that has since completed).\n override.originalValue = (element.style as any)[override.property] || '';\n\n switch (override.property) {\n case 'filter':\n case 'backdropFilter':\n case 'transform':\n case 'perspective':\n (element.style as any)[override.property] = 'none';\n break;\n case 'willChange':\n element.style.willChange = 'auto';\n break;\n case 'contain':\n element.style.contain = 'none';\n break;\n case 'overflowY':\n element.style.overflowY = 'visible';\n break;\n case 'overflowX':\n // Use 'hidden' instead of 'visible' to still prevent horizontal scrollbar\n element.style.overflowX = 'hidden';\n break;\n }\n }\n }\n }\n\n /**\n * Restore original values on ancestors that were overridden during pin.\n * The values restored are the ones captured at override time (pin entry),\n * not detection time — so they reflect the element's actual state just\n * before the pin started.\n */\n private _restoreFixedBreakingAncestors(): void {\n for (const { element, overrides } of this._fixedBreakingAncestors) {\n for (const { property, originalValue } of overrides) {\n (element.style as any)[property] = originalValue;\n }\n }\n }\n\n /**\n * Resolve pinSpacing config to a concrete value.\n *\n * Auto-detection rules (when pinSpacing is undefined or true):\n * - Body pin: scroll room is handled via documentElement height, not a spacer.\n * Return false so the (non-existent) spacer gets no extra padding/margin.\n * - Everything else: default to 'padding' (adds pinDistance as padding-bottom).\n * Works reliably in both flex and non-flex contexts with content-box sizing.\n *\n * Flex-aware behavior is handled separately in setup() by adding flex-shrink: 0\n * to the spacer when inside a flex container, preventing the flex algorithm from\n * collapsing the spacer below its needed size (GSAP-compatible).\n */\n private _resolvePinSpacing(): 'padding' | 'margin' | false {\n if (this._pinSpacing === false) return false;\n if (this._pinSpacing === 'margin') return 'margin';\n\n // Auto-detection when pinSpacing is undefined (\"Auto\" from builder) or true\n if (this._pinSpacing === undefined || this._pinSpacing === true) {\n // Body pin: spacer not used — spacing handled via html height extension\n if (this._isBodyPin) return false;\n }\n\n // true, 'padding', undefined all resolve to 'padding' (default)\n return 'padding';\n }\n\n /**\n * Fixed positioning for window scroller (seamless pinning)\n *\n * Wrapper pattern: the spacer wraps the element at all times (created in\n * setup, removed in cleanup). State changes only toggle styles on the\n * element — no spacer insertion/removal between states.\n *\n * Layout math (wrapper):\n * Spacer content-height = elementHeight\n * Spacer padding-bottom = pinDistance (when pinSpacing enabled)\n * Spacer total box = elementHeight + pinDistance\n *\n * 'before': element in flow inside spacer at top — no offset\n * 'pinned': element position:fixed, spacer holds space\n * 'after': element back in flow at spacer top, translateY(pinDistance)\n * moves it visually to (originalPos + pinDistance) — seamless\n * transition with the next section\n */\n private _updateFixed(state: PinState): void {\n const el = this._element!;\n\n if (state === 'before') {\n // Restore to normal flow inside spacer — element sits at spacer top\n el.style.position = this._originalStyles?.position || '';\n el.style.top = this._originalStyles?.top || '';\n el.style.left = this._originalStyles?.left || '';\n el.style.width = this._originalStyles?.width || '';\n if (this._isBodyPin) {\n // Body pin: clear margin-top set by the 'after' state\n el.style.marginTop = this._originalStyles?.marginTop || '';\n } else {\n // Margins stay zeroed — spacer owns them\n el.style.marginTop = '0';\n el.style.marginLeft = '0';\n }\n el.style.zIndex = this._originalStyles?.zIndex || '';\n // Clear pin offset so the next animation render doesn't include it\n clearPinOffset(el);\n el.style.transform = buildTransformString(el);\n // Restore overflow:clip on ancestors — no longer need to escape clipping\n this._restoreFixedBreakingAncestors();\n } else if (state === 'pinned') {\n // Override overflow:clip on ancestors so position:fixed is not clipped\n this._overrideFixedBreakingAncestors();\n // Fix element to viewport — no pin offset needed while position:fixed is active;\n // animation transforms apply directly on top of the fixed viewport position\n el.style.position = 'fixed';\n el.style.top = `${this._fixedTop}px`;\n el.style.left = `${this._fixedLeft}px`;\n el.style.width = `${this._width}px`;\n if (this._isBodyPin) {\n // Clear margin-top set by the 'after' state (scrolling back up into pin zone)\n el.style.marginTop = this._originalStyles?.marginTop || '';\n } else {\n el.style.marginTop = '0';\n el.style.marginLeft = '0';\n }\n el.style.zIndex = '100';\n clearPinOffset(el);\n el.style.transform = buildTransformString(el);\n // Register that we've set these pin styles\n if (this._isBodyPin) {\n registerPinnedProps(el, ['position', 'top', 'left', 'width', 'zIndex', 'transform', 'marginTop']);\n } else {\n registerPinnedProps(el, ['position', 'top', 'left', 'width', 'marginTop', 'marginLeft', 'zIndex', 'transform']);\n }\n } else {\n // After pin zone: element returns to flow.\n // For regular pins, it sits inside the spacer; translateY(pinDistance) moves it\n // visually to (originalPos + pinDistance), while the spacer's total box controls\n // document flow — no extra space because the element is INSIDE the spacer.\n el.style.position = this._originalStyles?.position || '';\n el.style.top = this._originalStyles?.top || '';\n el.style.left = this._originalStyles?.left || '';\n el.style.width = this._originalStyles?.width || '';\n\n if (this._isBodyPin) {\n // Body pin: use margin-top instead of transform to shift body down.\n // CSS transforms on body extend scrollable overflow beyond html's explicit\n // height — the browser computes scrollHeight from the transformed visual\n // extent, doubling the scroll area (\"double pin spacing\" bug).\n // margin-top positions body within document flow so it stays within\n // html's height boundary (html.height = bodyHeight + pinDistance).\n clearPinOffset(el);\n el.style.transform = buildTransformString(el);\n el.style.marginTop = `${this._pinDistance}px`;\n } else {\n // Regular pin: margins stay zeroed — spacer owns them.\n // Use transform to shift element within the spacer.\n el.style.marginTop = '0';\n el.style.marginLeft = '0';\n setPinOffset(el, 0, this._pinDistance, 0);\n el.style.transform = buildTransformString(el);\n }\n\n el.style.zIndex = this._originalStyles?.zIndex || '';\n // Restore overflow:clip on ancestors — no longer need to escape clipping\n this._restoreFixedBreakingAncestors();\n }\n }\n\n /**\n * Transform-based pinning for custom scroll containers\n */\n private _updateTransform(state: PinState, scrollTop: number): void {\n const el = this._element!;\n\n if (state === 'before') {\n el.style.zIndex = this._originalStyles?.zIndex || '';\n // Clear pin offset so subsequent animation renders produce no pin translation\n clearPinOffset(el);\n el.style.transform = buildTransformString(el);\n } else if (state === 'pinned') {\n const scrolledPast = Math.round(scrollTop - this._pinStart);\n el.style.zIndex = '100';\n // Counter-scroll with pin offset; animation transform is composed on top\n setPinOffset(el, 0, scrolledPast, 0);\n el.style.transform = buildTransformString(el);\n // Register that we've set these pin styles\n registerPinnedProps(el, ['transform', 'zIndex']);\n } else {\n el.style.zIndex = this._originalStyles?.zIndex || '';\n // Offset element by full pin distance; animation transform is composed on top\n setPinOffset(el, 0, this._pinDistance, 0);\n el.style.transform = buildTransformString(el);\n // Register transform for the 'after' state offset\n registerPinnedProps(el, ['transform', 'zIndex']);\n }\n }\n}\n",
|
|
19
19
|
"/**\n * ScrollPositionParser\n *\n * Shared utility for parsing scroll trigger position values.\n * Used by ScrollTrigger and MarkerManager.\n *\n * Supports:\n * - Keywords: top, center, bottom\n * - Keywords with offsets: top+100px, center-50%, bottom+20vh\n * - Units: px, %, vh, vw\n */\n\n/**\n * Parse a position value into pixels\n * @param value - Position string (e.g., \"top\", \"center\", \"50%\", \"100px\", \"top+100px\")\n * @param referenceSize - Reference size for percentage calculations (element height)\n * @param viewportHeight - Viewport height for vh calculations\n * @param viewportWidth - Viewport width for vw calculations (optional)\n */\nexport function parseScrollPosition(\n value: string,\n referenceSize: number,\n viewportHeight: number,\n viewportWidth?: number\n): number {\n if (!value) return 0;\n\n const val = value.trim().toLowerCase();\n\n // Check for keyword with offset: \"top+100px\", \"center-50%\", etc.\n const keywordOffsetMatch = val.match(/^(top|center|bottom)([+-])(-?\\d+(?:\\.\\d+)?)(px|%|vh|vw)?$/);\n if (keywordOffsetMatch) {\n const keyword = keywordOffsetMatch[1];\n const sign = keywordOffsetMatch[2] === '-' ? -1 : 1;\n const offsetNum = parseFloat(keywordOffsetMatch[3]);\n const offsetUnit = keywordOffsetMatch[4] || 'px';\n\n // Get base position from keyword\n let base = 0;\n switch (keyword) {\n case 'top': base = 0; break;\n case 'center': base = referenceSize / 2; break;\n case 'bottom': base = referenceSize; break;\n }\n\n // Parse offset\n const offset = parseUnit(offsetNum, offsetUnit, referenceSize, viewportHeight, viewportWidth);\n return base + (sign * offset);\n }\n\n // Simple keywords\n switch (val) {\n case 'top': return 0;\n case 'center': return referenceSize / 2;\n case 'bottom': return referenceSize;\n }\n\n // Plain number with unit: \"100px\", \"50%\", \"-20vh\"\n const match = val.match(/^(-?\\d+(?:\\.\\d+)?)(px|%|vh|vw)?$/);\n if (match) {\n const num = parseFloat(match[1]);\n const unit = match[2] || 'px';\n return parseUnit(num, unit, referenceSize, viewportHeight, viewportWidth);\n }\n\n return 0;\n}\n\n/**\n * Parse a numeric value with unit into pixels\n */\nfunction parseUnit(\n num: number,\n unit: string,\n referenceSize: number,\n viewportHeight: number,\n viewportWidth?: number\n): number {\n switch (unit) {\n case 'px': return num;\n case '%': return (num / 100) * referenceSize;\n case 'vh': return (num / 100) * viewportHeight;\n case 'vw': return (num / 100) * (viewportWidth ?? (typeof window !== 'undefined' ? window.innerWidth : 0));\n default: return num;\n }\n}\n",
|
|
20
|
-
"/**\n * MarkerManager\n *\n * Manages debug markers for ScrollTrigger visualization.\n * Shows 4 markers:\n * - scroller-start/end: Fixed position lines showing where triggers fire\n * - start/end: Element-relative lines showing trigger points on the element\n */\n\nimport type { MarkerConfig } from '../types';\nimport { parseScrollPosition } from '../utils/ScrollPositionParser';\nimport { getLayoutRect } from '../utils/getLayoutRect';\n\ninterface MarkerElements {\n scrollerStart: HTMLElement;\n scrollerEnd: HTMLElement;\n elementStart: HTMLElement;\n elementEnd: HTMLElement;\n}\n\ninterface MarkerSetupConfig {\n scroller: Element | Window;\n triggerElement: HTMLElement | null;\n startConfig: string;\n endConfig: string;\n markerConfig: MarkerConfig | boolean;\n viewportHeight: number;\n}\n\nexport class MarkerManager {\n private _id: number;\n private _markers?: MarkerElements;\n private _markerContainer?: HTMLElement;\n private _triggerElement?: HTMLElement;\n private _scrollerStartOffset: number = 0;\n private _scrollerEndOffset: number = 0;\n private _scroller: Element | Window = window;\n private _startConfig: string = 'top top';\n private _endConfig: string = 'bottom top';\n // Cached element marker offsets in document coordinates (avoids getLayoutRect per frame)\n private _cachedElStartDocTop: number = 0;\n private _cachedElEndDocTop: number = 0;\n private _cachedElPositionValid: boolean = false;\n\n constructor(triggerId: number) {\n this._id = triggerId;\n }\n\n /**\n * Create debug markers showing trigger positions\n */\n setup(config: MarkerSetupConfig): void {\n if (typeof document === 'undefined') return;\n\n // Store config for updates\n this._scroller = config.scroller;\n this._triggerElement = config.triggerElement || undefined;\n this._startConfig = config.startConfig;\n this._endConfig = config.endConfig;\n\n const markerConfig = config.markerConfig;\n const startColor = (typeof markerConfig === 'object' ? markerConfig.startColor : null) || 'lime';\n const endColor = (typeof markerConfig === 'object' ? markerConfig.endColor : null) || 'red';\n const fontSize = (typeof markerConfig === 'object' ? markerConfig.fontSize : null) || '10px';\n\n const isCustomScroller = this._scroller !== window;\n const scrollerHeight = config.viewportHeight;\n\n // Parse scroller positions (where on viewport the trigger line is)\n const startParts = this._startConfig.split(' ');\n const scrollerStartPos = parseScrollPosition(startParts[1] || 'top', scrollerHeight, scrollerHeight);\n\n const scrollerEndPos = this._endConfig.startsWith('+=')\n ? scrollerStartPos\n : parseScrollPosition(this._endConfig.split(' ')[1] || 'top', scrollerHeight, scrollerHeight);\n\n // Create a container for all markers (easier cleanup)\n this._markerContainer = document.createElement('div');\n this._markerContainer.setAttribute('data-scrolltrigger-markers', String(this._id));\n this._markerContainer.style.cssText = 'position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 9999;';\n\n // Helper to create a marker line with label\n const createMarker = (label: string, color: string, side: 'left' | 'right'): HTMLElement => {\n const marker = document.createElement('div');\n marker.setAttribute('data-marker', label);\n marker.style.cssText = `\n position: absolute;\n ${side}: 0;\n height: 1px;\n width: 50px;\n background: ${color};\n pointer-events: none;\n `;\n\n const labelEl = document.createElement('span');\n labelEl.textContent = label;\n labelEl.style.cssText = `\n position: absolute;\n ${side}: 0;\n top: 2px;\n background: ${color};\n color: ${color === 'lime' ? '#000' : '#fff'};\n padding: 1px 3px;\n font-size: ${fontSize};\n font-family: monospace;\n white-space: nowrap;\n border-radius: 2px;\n line-height: 1.2;\n `;\n marker.appendChild(labelEl);\n return marker;\n };\n\n // Create scroller markers (fixed position indicators on right)\n const scrollerStartMarker = createMarker('scroller-start', startColor, 'right');\n const scrollerEndMarker = createMarker('scroller-end', endColor, 'right');\n\n // Create element markers (move with element on left)\n const elementStartMarker = createMarker('start', startColor, 'left');\n const elementEndMarker = createMarker('end', endColor, 'left');\n\n // Hide element markers if no trigger element\n if (!this._triggerElement) {\n elementStartMarker.style.display = 'none';\n elementEndMarker.style.display = 'none';\n }\n\n this._markerContainer.appendChild(scrollerStartMarker);\n this._markerContainer.appendChild(scrollerEndMarker);\n this._markerContainer.appendChild(elementStartMarker);\n this._markerContainer.appendChild(elementEndMarker);\n\n this._markers = {\n scrollerStart: scrollerStartMarker,\n scrollerEnd: scrollerEndMarker,\n elementStart: elementStartMarker,\n elementEnd: elementEndMarker\n };\n\n // Append container to scroller\n if (isCustomScroller) {\n const scrollerEl = this._scroller as HTMLElement;\n // Ensure scroller has position for absolute children\n const computedPosition = window.getComputedStyle(scrollerEl).position;\n if (computedPosition === 'static') {\n scrollerEl.style.position = 'relative';\n }\n scrollerEl.appendChild(this._markerContainer);\n } else {\n document.body.appendChild(this._markerContainer);\n this._markerContainer.style.position = 'fixed';\n }\n\n // Store initial scroller positions\n this._scrollerStartOffset = scrollerStartPos;\n this._scrollerEndOffset = scrollerEndPos;\n\n // Cache element marker positions in document/content coordinates so update()\n // can position them without calling getLayoutRect() every frame.\n this._cacheElementPositions(scrollerHeight);\n }\n\n /**\n * Cache element marker positions in document/content coordinates.\n * Called at setup and on refresh. This is the only place getLayoutRect() is called\n * for element markers — the per-frame update() uses cached values instead.\n */\n private _cacheElementPositions(viewportHeight: number): void {\n if (!this._triggerElement) {\n this._cachedElPositionValid = false;\n return;\n }\n\n const elRect = getLayoutRect(this._triggerElement);\n const elStartOffset = parseScrollPosition(this._startConfig.split(' ')[0] || 'top', elRect.height, viewportHeight);\n const elEndOffset = this._endConfig.startsWith('+=')\n ? elStartOffset\n : parseScrollPosition(this._endConfig.split(' ')[0] || 'bottom', elRect.height, viewportHeight);\n\n const isCustomScroller = this._scroller !== window;\n\n if (isCustomScroller) {\n const scrollerRect = (this._scroller as Element).getBoundingClientRect();\n const scrollTop = (this._scroller as Element).scrollTop;\n const elTopInContent = elRect.top - scrollerRect.top + scrollTop;\n this._cachedElStartDocTop = elTopInContent + elStartOffset;\n this._cachedElEndDocTop = elTopInContent + elEndOffset;\n } else {\n const scrollTop = window.scrollY || window.pageYOffset || 0;\n const elTopInDocument = elRect.top + scrollTop;\n this._cachedElStartDocTop = elTopInDocument + elStartOffset;\n this._cachedElEndDocTop = elTopInDocument + elEndOffset;\n }\n this._cachedElPositionValid = true;\n }\n\n /**\n * Update marker positions on scroll.\n * Uses cached document-relative positions to avoid getLayoutRect() per frame.\n */\n update(scrollTop: number, triggerElement: HTMLElement | null, viewportHeight: number): void {\n if (!this._markers || !this._markerContainer) return;\n\n // Detect trigger element change — recache if needed\n const prevTrigger = this._triggerElement;\n this._triggerElement = triggerElement || undefined;\n\n if (this._triggerElement !== prevTrigger) {\n this._cacheElementPositions(viewportHeight);\n }\n\n const isCustomScroller = this._scroller !== window;\n\n // Position scroller markers (they stay \"fixed\" in the viewport)\n if (isCustomScroller) {\n // For custom scrollers: add scrollTop to keep them visually fixed\n this._markers.scrollerStart.style.top = `${scrollTop + this._scrollerStartOffset}px`;\n this._markers.scrollerEnd.style.top = `${scrollTop + this._scrollerEndOffset}px`;\n } else {\n // For window: truly fixed\n this._markers.scrollerStart.style.top = `${this._scrollerStartOffset}px`;\n this._markers.scrollerEnd.style.top = `${this._scrollerEndOffset}px`;\n }\n\n // Position element markers using cached document-relative positions\n if (this._triggerElement && this._cachedElPositionValid) {\n this._markers.elementStart.style.display = '';\n this._markers.elementEnd.style.display = '';\n\n if (isCustomScroller) {\n // Custom scroller: cached positions are in content coordinates\n this._markers.elementStart.style.top = `${this._cachedElStartDocTop}px`;\n this._markers.elementEnd.style.top = `${this._cachedElEndDocTop}px`;\n } else {\n // Window scroller: convert document coords to viewport coords for fixed container\n this._markers.elementStart.style.top = `${this._cachedElStartDocTop - scrollTop}px`;\n this._markers.elementEnd.style.top = `${this._cachedElEndDocTop - scrollTop}px`;\n }\n } else {\n // No trigger element - hide element markers\n this._markers.elementStart.style.display = 'none';\n this._markers.elementEnd.style.display = 'none';\n }\n }\n\n /**\n * Recache element positions after layout changes (resize, refresh).\n */\n recachePositions(viewportHeight: number): void {\n this._cacheElementPositions(viewportHeight);\n }\n\n /**\n * Remove all markers\n */\n cleanup(): void {\n if (this._markerContainer && this._markerContainer.parentNode) {\n this._markerContainer.parentNode.removeChild(this._markerContainer);\n }\n this._markerContainer = undefined;\n this._markers = undefined;\n this._triggerElement = undefined;\n }\n}\n",
|
|
21
|
-
"/**\n * ScrollTrigger\n *\n * Triggers timelines based on scroll position:\n * - start/end parameters define trigger zone (GSAP-style)\n * - scrub links animation progress to scroll position\n * - scrub: number adds smooth lag (in seconds)\n * - scroller: custom scroll container (default: window)\n * - pin: fix element in place during animation\n * - markers shows debug markers (optional)\n *\n * Pin implementation:\n * - Window scroller: uses position:fixed (seamless, no jitter)\n * - Custom scroller: uses transform (slight lag unavoidable)\n *\n * Start/End format: \"<element_position> <scroller_position>\"\n * - Keywords: top, center, bottom\n * - Units: 100px, 50%, 20vh\n * - Relative: +=500px (for end only)\n */\n\nimport type { ScrollConfig } from '../types';\nimport type { Timeline } from '../core/Timeline';\nimport { Ticker, runBatched } from '../core/Ticker';\nimport { PinManager } from './PinManager';\nimport { MarkerManager } from './MarkerManager';\nimport { parseScrollPosition } from '../utils/ScrollPositionParser';\nimport { executeTimelineAction } from '../utils/executeTimelineAction';\nimport { getLayoutRect } from '../utils/getLayoutRect';\nimport type { TimelineAction } from '../utils/executeTimelineAction';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\n\nexport class ScrollTrigger extends BaseTrigger<ScrollConfig> {\n // Unique ID for this ScrollTrigger instance\n private static _nextId: number = 0;\n private _id: number;\n\n // Priority for smooth-scrub Ticker listener.\n // Engine registers at priority 0; running at 1 ensures the scrub progress update\n // executes BEFORE Engine's timeline.update() in the same frame.\n private static readonly _SCRUB_TICKER_PRIORITY = 1;\n\n // Global registry of active instances for window.load refresh.\n // Images/fonts loading after DOMContentLoaded shift layout, causing incorrect\n // trigger positions. A one-time load listener refreshes all instances.\n private static _activeInstances: Set<ScrollTrigger> = new Set();\n private static _loadListenerAdded: boolean = false;\n\n private _scrollListener?: () => void;\n private _scrubTickListener: ((deltaTime: number, elapsedTime: number) => void) | null = null;\n private _currentProgress: number = 0;\n private _targetProgress: number = 0;\n private _scrubLag: number = 0;\n private _scroller!: Element | Window;\n\n // Trigger zone (calculated from start/end config)\n private _triggerStart: number = 0;\n private _triggerEnd: number = 0;\n\n // Pin manager (handles all pin state and logic)\n private _pinManager: PinManager | null = null;\n\n // Marker manager (handles debug markers)\n private _markerManager: MarkerManager | null = null;\n\n // Track trigger state for non-scrub mode (play once on enter, reverse on leave)\n private _triggerState: 'before' | 'active' | 'after' = 'before';\n private _toggleActions: [TimelineAction, TimelineAction, TimelineAction, TimelineAction] = ['play', 'none', 'none', 'none'];\n private _initializing: boolean = false;\n\n constructor(timeline: Timeline, config: ScrollConfig) {\n super(timeline, config);\n this._id = ScrollTrigger._nextId++;\n this._scrubLag = typeof config.scrub === 'number' ? config.scrub : 0;\n }\n\n /**\n * Register the global window.load listener (once).\n * After all resources finish loading, refresh every active ScrollTrigger\n * so trigger positions account for layout shifts caused by images/fonts.\n */\n private static _ensureLoadListener(): void {\n if (ScrollTrigger._loadListenerAdded || typeof window === 'undefined') return;\n ScrollTrigger._loadListenerAdded = true;\n\n // If the page already loaded, no need for a listener\n if (document.readyState === 'complete') return;\n\n window.addEventListener('load', () => {\n // Small delay to let browsers finalize layout after load\n setTimeout(() => {\n for (const instance of ScrollTrigger._activeInstances) {\n instance.refresh();\n }\n }, 100);\n }, { once: true });\n }\n\n private _resolveScroller(): void {\n if (this._config.scroller) {\n if (typeof this._config.scroller === 'string') {\n if (typeof document === 'undefined') {\n this._scroller = window;\n return;\n }\n try {\n const el = document.querySelector(this._config.scroller);\n this._scroller = el ?? window;\n } catch (e) {\n console.warn(`[Motion] Invalid scroller selector \"${this._config.scroller}\":`, e);\n this._scroller = window;\n }\n } else {\n this._scroller = this._config.scroller;\n }\n } else {\n this._scroller = window;\n }\n }\n\n private _resolvePinElement(): HTMLElement | null {\n if (!this._config.pin || typeof document === 'undefined') return null;\n\n let resolved: HTMLElement | null = null;\n\n if (typeof this._config.pin === 'string') {\n resolved = document.querySelector(this._config.pin);\n } else if (this._config.pin === true) {\n // Use builder-aware target resolution so split-text cases return the\n // original container element rather than a split char/word fragment.\n resolved = this._getFirstAnimatedElement();\n }\n\n return resolved;\n }\n\n /**\n * Default start position — matches GSAP behavior:\n * - With pin: 'top top' (element top hits viewport top)\n * - Without pin: 'top bottom' (element top hits viewport bottom)\n */\n private _defaultStart(): string {\n return this._config.start || (this._config.pin ? 'top top' : 'top bottom');\n }\n\n /**\n * Default end position — matches GSAP behavior:\n * 'bottom top' (element bottom hits viewport top)\n */\n private _defaultEnd(): string {\n return this._config.end || 'bottom top';\n }\n\n private _getFirstAnimatedElement(): HTMLElement | null {\n // Prefer the builder's original targets when text splitting is active.\n // After split, getTargets() returns individual char/word/line <span> fragments —\n // measuring a tiny char span produces wildly incorrect trigger positions.\n // getOriginalTargets() returns the pre-split container element (e.g. .text-gallery).\n const builder = this._timeline.getFirstChildBuilder();\n if (builder) {\n const targets = builder.hasSplit() ? builder.getOriginalTargets() : builder.getTargets();\n for (const target of targets) {\n if (target instanceof HTMLElement) return target;\n }\n }\n\n // Fallback: use the Animation's own target list (no builder available)\n const firstAnimation = this._timeline.getFirstChildAnimation();\n if (firstAnimation) {\n const targets = firstAnimation.getTargets();\n if (targets.length > 0 && targets[0] instanceof HTMLElement) {\n return targets[0];\n }\n }\n return null;\n }\n\n /**\n * Resolve the trigger element from config or first animation\n */\n private _resolveTriggerElement(): HTMLElement | null {\n // If target is an Element, use it directly\n if (this._config.target instanceof HTMLElement) {\n return this._config.target;\n }\n\n // If target is a string selector, query for it\n if (typeof this._config.target === 'string' && typeof document !== 'undefined') {\n const el = document.querySelector(this._config.target);\n if (el instanceof HTMLElement) {\n return el;\n }\n }\n\n // Fall back to first animated element\n return this._getFirstAnimatedElement();\n }\n\n /**\n * Calculate trigger start/end scroll positions based on config.\n *\n * Pin distance equals the trigger range (triggerEnd - triggerStart).\n * The PinManager spacer adds exactly that many pixels of scroll room to the\n * page, so the animation always plays over the full configured distance while\n * the element is pinned — no separate \"extension\" step is needed.\n *\n * When refresh() is called while the element is position:fixed (pinned), we\n * use the spacer's rect instead of the element's rect. A position:fixed\n * element returns viewport-relative coordinates from getBoundingClientRect(),\n * which would produce incorrect document-relative trigger positions. The\n * spacer sits in the element's original place in the document flow, so its\n * rect always reflects the correct base for the calculation.\n */\n private _calculateTriggerPositions(pinElement: HTMLElement | null): void {\n const el = pinElement || this._resolveTriggerElement();\n const scrollerHeight = this._getViewportHeight();\n const scrollTop = this._getScrollTop();\n\n if (!el) {\n this._triggerStart = 0;\n this._triggerEnd = this._getScrollHeight();\n return;\n }\n\n // Prefer the spacer's rect when it is in the DOM: the pinned element is\n // position:fixed and its rect is viewport-relative, not document-relative.\n // For non-pinned elements, strip inline transforms before measuring so the\n // trigger position reflects the element's layout position, not its animated\n // (post-transform) position.\n const elRect = this._pinManager?.getLayoutRect() ?? getLayoutRect(el);\n\n let elTopInDocument: number;\n if (this._scroller instanceof Element) {\n const scrollerRect = this._scroller.getBoundingClientRect();\n elTopInDocument = elRect.top - scrollerRect.top + scrollTop;\n } else {\n elTopInDocument = elRect.top + scrollTop;\n }\n\n // Parse start: \"element_pos scroller_pos\"\n const start = this._defaultStart();\n const startParts = start.split(' ');\n const elStartPos = startParts[0] || 'top';\n const scrollerStartPos = startParts[1] || 'top';\n\n const elStartOffset = parseScrollPosition(elStartPos, elRect.height, scrollerHeight);\n const scrollerStartOffset = parseScrollPosition(scrollerStartPos, scrollerHeight, scrollerHeight);\n\n // Trigger starts when element position reaches scroller position\n this._triggerStart = elTopInDocument + elStartOffset - scrollerStartOffset;\n\n // Parse end\n const end = this._defaultEnd();\n\n if (end.startsWith('+=')) {\n const offset = parseScrollPosition(end.slice(2), scrollerHeight, scrollerHeight);\n this._triggerEnd = this._triggerStart + offset;\n } else {\n const endParts = end.split(' ');\n const elEndPos = endParts[0] || 'bottom';\n const scrollerEndPos = endParts[1] || 'top';\n\n const elEndOffset = parseScrollPosition(elEndPos, elRect.height, scrollerHeight);\n const scrollerEndOffset = parseScrollPosition(scrollerEndPos, scrollerHeight, scrollerHeight);\n\n this._triggerEnd = elTopInDocument + elEndOffset - scrollerEndOffset;\n }\n\n if (this._triggerEnd <= this._triggerStart) {\n this._triggerEnd = this._triggerStart + Math.max(scrollerHeight, 100);\n }\n }\n\n protected _onEnable(): void {\n this._resolveScroller();\n const pinElement = this._resolvePinElement();\n this._calculateTriggerPositions(pinElement);\n\n // Register for window.load refresh and add resize listener\n ScrollTrigger._activeInstances.add(this);\n ScrollTrigger._ensureLoadListener();\n\n // Parse toggleActions string into a 4-tuple (only relevant for non-scrub mode)\n const actionsStr = this._config.toggleActions || 'play none none none';\n const parts = actionsStr.trim().split(/\\s+/);\n this._toggleActions = [\n parts[0] as TimelineAction,\n parts[1] as TimelineAction,\n parts[2] as TimelineAction,\n parts[3] as TimelineAction,\n ];\n\n if (pinElement) {\n // Calculate fixed-pin top position from start config.\n // scrollerStartOffset = viewport Y where the element's reference point was\n // elStartOffset = distance from element's top edge to reference point\n // fixedTop = viewport Y for element's top edge = scrollerStartOffset - elStartOffset\n const start = this._defaultStart();\n const startParts = start.split(' ');\n const viewportHeight = this._getViewportHeight();\n const elStartOffset = parseScrollPosition(startParts[0] || 'top', pinElement.getBoundingClientRect().height, viewportHeight);\n const scrollerStartOffset = parseScrollPosition(startParts[1] || 'top', viewportHeight, viewportHeight);\n const fixedTop = scrollerStartOffset - elStartOffset;\n\n // Pin zone deliberately equals the animation trigger zone:\n // pinStart = _triggerStart, pinEnd = _triggerEnd\n // pinDistance = _triggerEnd - _triggerStart\n //\n // The spacer adds `pinDistance` of scroll room to the page so the\n // animation plays over the full configured distance while the element\n // is fixed — this is the GSAP-compatible behaviour (the spacer IS the\n // \"auto-extension\" of the scrollable range). No separate adjustment to\n // _triggerEnd is required.\n // Use the shared registry to get-or-create the PinManager for this element.\n // This prevents multiple ScrollTrigger instances (e.g. from each:true) from\n // creating nested spacers around the same DOM element.\n this._pinManager = PinManager.getOrCreate(this._id, pinElement);\n this._pinManager.setup(\n pinElement,\n this._triggerStart,\n this._triggerEnd,\n fixedTop,\n this._scroller === window,\n this._config.pinSpacing\n );\n }\n\n if (this._config.markers && typeof document !== 'undefined') {\n const triggerElement = this._pinManager?.getElement() || this._resolveTriggerElement();\n this._markerManager = new MarkerManager(this._id);\n this._markerManager.setup({\n scroller: this._scroller,\n triggerElement,\n startConfig: this._defaultStart(),\n endConfig: this._defaultEnd(),\n markerConfig: this._config.markers,\n viewportHeight: this._getViewportHeight()\n });\n }\n\n this._scrollListener = () => this._onScroll();\n if (this._scroller) {\n this._scroller.addEventListener('scroll', this._scrollListener, { passive: true });\n // Set initial state based on current scroll position without firing actions\n this._initializing = true;\n this._onScroll();\n this._initializing = false;\n\n // Fire initial actions if page loaded mid-scroll (non-scrub mode only).\n // The _initializing pass above synced _triggerState but deliberately skipped\n // actions. Now that state is settled, fire the correct entry actions so\n // elements that are already in (or past) the trigger zone animate immediately.\n if (this._config.scrub === false || this._config.scrub === undefined) {\n if (this._triggerState === 'active') {\n // Page loaded inside trigger zone — fire onEnter\n executeTimelineAction(this._toggleActions[0], this._timeline);\n } else if (this._triggerState === 'after') {\n // Page loaded past trigger zone — show completed state silently.\n // Don't fire toggle actions here because play() + reverse() at time 0\n // leaves the timeline stuck at progress=0 (reverse can't go below 0).\n // Instead, just seek to the end state so elements appear as if the\n // animation already completed naturally.\n this._timeline.progress(1);\n this._timeline.pause();\n }\n }\n }\n }\n\n protected _onDisable(): void {\n if (this._scrollListener && this._scroller) {\n this._scroller.removeEventListener('scroll', this._scrollListener);\n }\n\n this._markerManager?.cleanup();\n this._markerManager = null;\n\n // Release this trigger's share of the pinned element's PinManager.\n // cleanup() only runs when the last user releases (shared registry).\n if (this._pinManager) {\n const pinElement = this._pinManager.getElement();\n if (pinElement) {\n PinManager.release(this._id, pinElement);\n } else {\n this._pinManager.cleanup();\n }\n this._pinManager = null;\n }\n\n this._stopSmoothScrollLoop();\n\n // Unregister from global refresh\n ScrollTrigger._activeInstances.delete(this);\n }\n\n /**\n * Refresh trigger positions (call after layout changes like resize)\n */\n refresh(): void {\n if (!this._enabled) return;\n\n const pinElement = this._pinManager?.getElement() || null;\n\n // Body-pin: temporarily reset html.style.height to its original value before\n // calculating trigger positions. Without this, body's height inherits the\n // inflated html height (bodyHeight + pinDistance), causing trigger positions\n // and pinDistance to snowball on every refresh (\"double pin spacing\" bug).\n // The restore callback re-applies the inflated height; PinManager.setup()\n // will then overwrite it with the correctly re-calculated value.\n const restoreHtmlHeight = this._pinManager?.suspendHtmlHeight?.() ?? null;\n\n this._calculateTriggerPositions(pinElement);\n\n // Restore html height so PinManager.setup() can safely overwrite it.\n restoreHtmlHeight?.();\n\n // Update pin manager with new positions\n if (this._pinManager && pinElement) {\n const start = this._defaultStart();\n const startParts = start.split(' ');\n const viewportHeight = this._getViewportHeight();\n // After Bug 1 fix, getLayoutRect()?.height returns the element's natural height\n // (not the spacer's inflated height that includes padding-bottom = pinDistance).\n const elHeight = this._pinManager.getLayoutRect()?.height ?? pinElement.getBoundingClientRect().height;\n const elStartOffset = parseScrollPosition(startParts[0] || 'top', elHeight, viewportHeight);\n const scrollerStartOffset = parseScrollPosition(startParts[1] || 'top', viewportHeight, viewportHeight);\n const fixedTop = scrollerStartOffset - elStartOffset;\n\n this._pinManager.setup(\n pinElement,\n this._triggerStart,\n this._triggerEnd,\n fixedTop,\n this._scroller === window,\n this._config.pinSpacing\n );\n }\n\n // Update cached marker positions (lightweight — no DOM rebuild, just recache coords)\n if (this._markerManager) {\n this._markerManager.recachePositions(this._getViewportHeight());\n }\n\n // Re-run scroll handler to update state\n this._onScroll();\n }\n private _getViewportHeight(): number {\n if (this._scroller === window) {\n return typeof window !== 'undefined' ? window.innerHeight : 0;\n } else if (this._scroller instanceof Element) {\n return this._scroller.clientHeight;\n }\n return 0;\n }\n\n private _getScrollTop(): number {\n if (this._scroller === window) {\n return typeof window !== 'undefined' ? (window.scrollY || window.pageYOffset) : 0;\n } else if (this._scroller instanceof Element) {\n return this._scroller.scrollTop;\n }\n return 0;\n }\n\n private _getScrollHeight(): number {\n if (this._scroller === window) {\n if (typeof document !== 'undefined' && document.documentElement) {\n return document.documentElement.scrollHeight - window.innerHeight;\n }\n return 0;\n } else if (this._scroller instanceof Element) {\n return this._scroller.scrollHeight - this._scroller.clientHeight;\n }\n return 0;\n }\n\n private _onScroll(): void {\n if (!this._enabled) return;\n\n const scrollTop = this._getScrollTop();\n\n // Calculate progress based on trigger zone\n const triggerRange = this._triggerEnd - this._triggerStart;\n let progress = triggerRange > 0\n ? (scrollTop - this._triggerStart) / triggerRange\n : 0;\n progress = Math.max(0, Math.min(1, progress));\n\n // Apply snap (before assigning to _targetProgress)\n progress = this._applySnap(progress);\n\n this._targetProgress = progress;\n\n // Handle pin\n this._pinManager?.update(scrollTop);\n\n // Update all markers\n if (this._markerManager) {\n const triggerElement = this._pinManager?.getElement() || this._resolveTriggerElement();\n this._markerManager.update(scrollTop, triggerElement, this._getViewportHeight());\n }\n\n // Handle scrub/play\n if (this._config.scrub !== false && this._config.scrub !== undefined) {\n if (this._scrubLag === 0) {\n // Instant scrub: apply progress synchronously.\n // runBatched batches per-property writes into one DOM write per element.\n this._currentProgress = this._targetProgress;\n runBatched(() => this._timeline.progress(this._currentProgress));\n } else {\n this._startSmoothScrollLoop();\n }\n } else {\n // Non-scrub mode: play once on enter, reverse on leave\n // Determine current state\n let newState: 'before' | 'active' | 'after';\n if (progress <= 0) {\n newState = 'before';\n } else if (progress >= 1) {\n newState = 'after';\n } else {\n newState = 'active';\n }\n\n // Handle state transitions\n if (newState !== this._triggerState) {\n const prevState = this._triggerState;\n this._triggerState = newState;\n\n // Don't fire transition actions during initialization — just sync state\n if (this._initializing) return;\n\n // Direction-aware transitions for toggleActions\n // Format: [onEnter, onLeave, onEnterBack, onLeaveBack]\n // Wrapped in runBatched to batch per-property DOM writes\n // (fast-scroll paths call progress() which writes to DOM)\n runBatched(() => {\n if (newState === 'active' && prevState === 'before') {\n // onEnter — scrolling DOWN into trigger zone\n executeTimelineAction(this._toggleActions[0], this._timeline);\n } else if (newState === 'after' && prevState === 'active') {\n // onLeave — scrolling DOWN past trigger zone\n executeTimelineAction(this._toggleActions[1], this._timeline);\n } else if (newState === 'active' && prevState === 'after') {\n // onEnterBack — scrolling UP back into trigger zone\n executeTimelineAction(this._toggleActions[2], this._timeline);\n } else if (newState === 'before' && prevState === 'active') {\n // onLeaveBack — scrolling UP past trigger zone\n executeTimelineAction(this._toggleActions[3], this._timeline);\n } else if (newState === 'after' && prevState === 'before') {\n // Fast-scroll forward: fire onEnter then onLeave\n executeTimelineAction(this._toggleActions[0], this._timeline);\n // Ensure timeline reaches completion before onLeave fires\n this._timeline.progress(1);\n executeTimelineAction(this._toggleActions[1], this._timeline);\n } else if (newState === 'before' && prevState === 'after') {\n // Fast-scroll backward: fire onEnterBack then onLeaveBack\n executeTimelineAction(this._toggleActions[2], this._timeline);\n // Ensure timeline reaches start before onLeaveBack fires\n this._timeline.progress(0);\n executeTimelineAction(this._toggleActions[3], this._timeline);\n }\n });\n }\n }\n }\n\n /**\n * Apply snap to a raw progress value based on config.snap.\n * - number: snap to evenly-spaced increments (e.g. 0.25 → 0, 0.25, 0.5, 0.75, 1)\n * - number[]: snap to nearest value in array\n * - function: custom snap function\n */\n private _applySnap(progress: number): number {\n const snap = this._config.snap;\n if (snap == null) return progress;\n\n if (typeof snap === 'function') {\n return snap(progress);\n }\n\n if (Array.isArray(snap)) {\n // Find nearest value in array\n let closest = snap[0] ?? progress;\n let minDist = Math.abs(progress - closest);\n for (let i = 1; i < snap.length; i++) {\n const dist = Math.abs(progress - snap[i]!);\n if (dist < minDist) {\n minDist = dist;\n closest = snap[i]!;\n }\n }\n return closest;\n }\n\n if (typeof snap === 'number' && snap > 0) {\n // Snap to evenly-spaced increments\n return Math.round(progress / snap) * snap;\n }\n\n return progress;\n }\n\n private _startSmoothScrollLoop(): void {\n if (this._scrubTickListener) return; // idempotent — listener already registered\n\n this._scrubTickListener = (deltaTime: number, _elapsedTime: number) => {\n if (!this._enabled) {\n this._stopSmoothScrollLoop();\n return;\n }\n\n const diff = this._targetProgress - this._currentProgress;\n\n // Frame-rate independent exponential smoothing:\n // exp(-3) ≈ 0.05, so after scrubLag seconds ~95% of distance is covered\n const catchUp = 1 - Math.exp((-deltaTime * 3) / this._scrubLag);\n this._currentProgress += diff * catchUp;\n\n // Snap to target when close enough and unregister the listener\n if (Math.abs(this._targetProgress - this._currentProgress) < 0.0001) {\n this._currentProgress = this._targetProgress;\n this._timeline.progress(this._currentProgress);\n this._stopSmoothScrollLoop();\n return;\n }\n\n this._timeline.progress(this._currentProgress);\n };\n\n Ticker.getInstance().add(this._scrubTickListener, ScrollTrigger._SCRUB_TICKER_PRIORITY);\n }\n\n private _stopSmoothScrollLoop(): void {\n if (this._scrubTickListener) {\n Ticker.getInstance().remove(this._scrubTickListener);\n this._scrubTickListener = null;\n }\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('scroll', ScrollTrigger);\n",
|
|
20
|
+
"/**\n * MarkerManager\n *\n * Manages debug markers for ScrollTrigger visualization.\n * Shows 4 markers:\n * - scroller-start/end: Fixed position lines showing where triggers fire\n * - start/end: Element-relative lines showing trigger points on the element\n */\n\nimport type { MarkerConfig } from '../types';\nimport { parseScrollPosition } from '../utils/ScrollPositionParser';\nimport { getLayoutRect } from '../utils/getLayoutRect';\n\ninterface MarkerElements {\n scrollerStart: HTMLElement;\n scrollerEnd: HTMLElement;\n elementStart: HTMLElement;\n elementEnd: HTMLElement;\n}\n\ninterface MarkerSetupConfig {\n scroller: Element | Window;\n triggerElement: Element | null;\n startConfig: string;\n endConfig: string;\n markerConfig: MarkerConfig | boolean;\n viewportHeight: number;\n}\n\nexport class MarkerManager {\n private _id: number;\n private _markers?: MarkerElements;\n private _markerContainer?: HTMLElement;\n private _triggerElement?: Element;\n private _scrollerStartOffset: number = 0;\n private _scrollerEndOffset: number = 0;\n private _scroller: Element | Window = window;\n private _startConfig: string = 'top top';\n private _endConfig: string = 'bottom top';\n // Cached element marker offsets in document coordinates (avoids getLayoutRect per frame)\n private _cachedElStartDocTop: number = 0;\n private _cachedElEndDocTop: number = 0;\n private _cachedElPositionValid: boolean = false;\n\n constructor(triggerId: number) {\n this._id = triggerId;\n }\n\n /**\n * Create debug markers showing trigger positions\n */\n setup(config: MarkerSetupConfig): void {\n if (typeof document === 'undefined') return;\n\n // Store config for updates\n this._scroller = config.scroller;\n this._triggerElement = config.triggerElement || undefined;\n this._startConfig = config.startConfig;\n this._endConfig = config.endConfig;\n\n const markerConfig = config.markerConfig;\n const startColor = (typeof markerConfig === 'object' ? markerConfig.startColor : null) || 'lime';\n const endColor = (typeof markerConfig === 'object' ? markerConfig.endColor : null) || 'red';\n const fontSize = (typeof markerConfig === 'object' ? markerConfig.fontSize : null) || '10px';\n\n const isCustomScroller = this._scroller !== window;\n const scrollerHeight = config.viewportHeight;\n\n // Parse scroller positions (where on viewport the trigger line is)\n const startParts = this._startConfig.split(' ');\n const scrollerStartPos = parseScrollPosition(startParts[1] || 'top', scrollerHeight, scrollerHeight);\n\n const scrollerEndPos = this._endConfig.startsWith('+=')\n ? scrollerStartPos\n : parseScrollPosition(this._endConfig.split(' ')[1] || 'top', scrollerHeight, scrollerHeight);\n\n // Create a container for all markers (easier cleanup)\n this._markerContainer = document.createElement('div');\n this._markerContainer.setAttribute('data-scrolltrigger-markers', String(this._id));\n this._markerContainer.style.cssText = 'position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 9999;';\n\n // Helper to create a marker line with label\n const createMarker = (label: string, color: string, side: 'left' | 'right'): HTMLElement => {\n const marker = document.createElement('div');\n marker.setAttribute('data-marker', label);\n marker.style.cssText = `\n position: absolute;\n ${side}: 0;\n height: 1px;\n width: 50px;\n background: ${color};\n pointer-events: none;\n `;\n\n const labelEl = document.createElement('span');\n labelEl.textContent = label;\n labelEl.style.cssText = `\n position: absolute;\n ${side}: 0;\n top: 2px;\n background: ${color};\n color: ${color === 'lime' ? '#000' : '#fff'};\n padding: 1px 3px;\n font-size: ${fontSize};\n font-family: monospace;\n white-space: nowrap;\n border-radius: 2px;\n line-height: 1.2;\n `;\n marker.appendChild(labelEl);\n return marker;\n };\n\n // Create scroller markers (fixed position indicators on right)\n const scrollerStartMarker = createMarker('scroller-start', startColor, 'right');\n const scrollerEndMarker = createMarker('scroller-end', endColor, 'right');\n\n // Create element markers (move with element on left)\n const elementStartMarker = createMarker('start', startColor, 'left');\n const elementEndMarker = createMarker('end', endColor, 'left');\n\n // Hide element markers if no trigger element\n if (!this._triggerElement) {\n elementStartMarker.style.display = 'none';\n elementEndMarker.style.display = 'none';\n }\n\n this._markerContainer.appendChild(scrollerStartMarker);\n this._markerContainer.appendChild(scrollerEndMarker);\n this._markerContainer.appendChild(elementStartMarker);\n this._markerContainer.appendChild(elementEndMarker);\n\n this._markers = {\n scrollerStart: scrollerStartMarker,\n scrollerEnd: scrollerEndMarker,\n elementStart: elementStartMarker,\n elementEnd: elementEndMarker\n };\n\n // Append container to scroller\n if (isCustomScroller) {\n const scrollerEl = this._scroller as HTMLElement;\n // Ensure scroller has position for absolute children\n const computedPosition = window.getComputedStyle(scrollerEl).position;\n if (computedPosition === 'static') {\n scrollerEl.style.position = 'relative';\n }\n scrollerEl.appendChild(this._markerContainer);\n } else {\n document.body.appendChild(this._markerContainer);\n this._markerContainer.style.position = 'fixed';\n }\n\n // Store initial scroller positions\n this._scrollerStartOffset = scrollerStartPos;\n this._scrollerEndOffset = scrollerEndPos;\n\n // Cache element marker positions in document/content coordinates so update()\n // can position them without calling getLayoutRect() every frame.\n this._cacheElementPositions(scrollerHeight);\n }\n\n /**\n * Cache element marker positions in document/content coordinates.\n * Called at setup and on refresh. This is the only place getLayoutRect() is called\n * for element markers — the per-frame update() uses cached values instead.\n */\n private _cacheElementPositions(viewportHeight: number): void {\n if (!this._triggerElement) {\n this._cachedElPositionValid = false;\n return;\n }\n\n const elRect = getLayoutRect(this._triggerElement);\n const elStartOffset = parseScrollPosition(this._startConfig.split(' ')[0] || 'top', elRect.height, viewportHeight);\n const elEndOffset = this._endConfig.startsWith('+=')\n ? elStartOffset\n : parseScrollPosition(this._endConfig.split(' ')[0] || 'bottom', elRect.height, viewportHeight);\n\n const isCustomScroller = this._scroller !== window;\n\n if (isCustomScroller) {\n const scrollerRect = (this._scroller as Element).getBoundingClientRect();\n const scrollTop = (this._scroller as Element).scrollTop;\n const elTopInContent = elRect.top - scrollerRect.top + scrollTop;\n this._cachedElStartDocTop = elTopInContent + elStartOffset;\n this._cachedElEndDocTop = elTopInContent + elEndOffset;\n } else {\n const scrollTop = window.scrollY || window.pageYOffset || 0;\n const elTopInDocument = elRect.top + scrollTop;\n this._cachedElStartDocTop = elTopInDocument + elStartOffset;\n this._cachedElEndDocTop = elTopInDocument + elEndOffset;\n }\n this._cachedElPositionValid = true;\n }\n\n /**\n * Update marker positions on scroll.\n * Uses cached document-relative positions to avoid getLayoutRect() per frame.\n */\n update(scrollTop: number, triggerElement: Element | null, viewportHeight: number): void {\n if (!this._markers || !this._markerContainer) return;\n\n // Detect trigger element change — recache if needed\n const prevTrigger = this._triggerElement;\n this._triggerElement = triggerElement || undefined;\n\n if (this._triggerElement !== prevTrigger) {\n this._cacheElementPositions(viewportHeight);\n }\n\n const isCustomScroller = this._scroller !== window;\n\n // Position scroller markers (they stay \"fixed\" in the viewport)\n if (isCustomScroller) {\n // For custom scrollers: add scrollTop to keep them visually fixed\n this._markers.scrollerStart.style.top = `${scrollTop + this._scrollerStartOffset}px`;\n this._markers.scrollerEnd.style.top = `${scrollTop + this._scrollerEndOffset}px`;\n } else {\n // For window: truly fixed\n this._markers.scrollerStart.style.top = `${this._scrollerStartOffset}px`;\n this._markers.scrollerEnd.style.top = `${this._scrollerEndOffset}px`;\n }\n\n // Position element markers using cached document-relative positions\n if (this._triggerElement && this._cachedElPositionValid) {\n this._markers.elementStart.style.display = '';\n this._markers.elementEnd.style.display = '';\n\n if (isCustomScroller) {\n // Custom scroller: cached positions are in content coordinates\n this._markers.elementStart.style.top = `${this._cachedElStartDocTop}px`;\n this._markers.elementEnd.style.top = `${this._cachedElEndDocTop}px`;\n } else {\n // Window scroller: convert document coords to viewport coords for fixed container\n this._markers.elementStart.style.top = `${this._cachedElStartDocTop - scrollTop}px`;\n this._markers.elementEnd.style.top = `${this._cachedElEndDocTop - scrollTop}px`;\n }\n } else {\n // No trigger element - hide element markers\n this._markers.elementStart.style.display = 'none';\n this._markers.elementEnd.style.display = 'none';\n }\n }\n\n /**\n * Recache element positions after layout changes (resize, refresh).\n */\n recachePositions(viewportHeight: number): void {\n this._cacheElementPositions(viewportHeight);\n }\n\n /**\n * Remove all markers\n */\n cleanup(): void {\n if (this._markerContainer && this._markerContainer.parentNode) {\n this._markerContainer.parentNode.removeChild(this._markerContainer);\n }\n this._markerContainer = undefined;\n this._markers = undefined;\n this._triggerElement = undefined;\n }\n}\n",
|
|
21
|
+
"/**\n * ScrollTrigger\n *\n * Triggers timelines based on scroll position:\n * - start/end parameters define trigger zone (GSAP-style)\n * - scrub links animation progress to scroll position\n * - scrub: number adds smooth lag (in seconds)\n * - scroller: custom scroll container (default: window)\n * - pin: fix element in place during animation\n * - markers shows debug markers (optional)\n *\n * Pin implementation:\n * - Window scroller: uses position:fixed (seamless, no jitter)\n * - Custom scroller: uses transform (slight lag unavoidable)\n *\n * Start/End format: \"<element_position> <scroller_position>\"\n * - Keywords: top, center, bottom\n * - Units: 100px, 50%, 20vh\n * - Relative: +=500px (for end only)\n */\n\nimport type { ScrollConfig } from '../types';\nimport type { Timeline } from '../core/Timeline';\nimport { Ticker, runBatched } from '../core/Ticker';\nimport { PinManager } from './PinManager';\nimport { MarkerManager } from './MarkerManager';\nimport { parseScrollPosition } from '../utils/ScrollPositionParser';\nimport { executeTimelineAction } from '../utils/executeTimelineAction';\nimport { getLayoutRect } from '../utils/getLayoutRect';\nimport type { TimelineAction } from '../utils/executeTimelineAction';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\n\nexport class ScrollTrigger extends BaseTrigger<ScrollConfig> {\n // Unique ID for this ScrollTrigger instance\n private static _nextId: number = 0;\n private _id: number;\n\n // Priority for smooth-scrub Ticker listener.\n // Engine registers at priority 0; running at 1 ensures the scrub progress update\n // executes BEFORE Engine's timeline.update() in the same frame.\n private static readonly _SCRUB_TICKER_PRIORITY = 1;\n\n // Global registry of active instances for window.load refresh.\n // Images/fonts loading after DOMContentLoaded shift layout, causing incorrect\n // trigger positions. A one-time load listener refreshes all instances.\n private static _activeInstances: Set<ScrollTrigger> = new Set();\n private static _loadListenerAdded: boolean = false;\n\n private _scrollListener?: () => void;\n private _scrubTickListener: ((deltaTime: number, elapsedTime: number) => void) | null = null;\n private _currentProgress: number = 0;\n private _targetProgress: number = 0;\n private _scrubLag: number = 0;\n private _scroller!: Element | Window;\n\n // Trigger zone (calculated from start/end config)\n private _triggerStart: number = 0;\n private _triggerEnd: number = 0;\n\n // Pin manager (handles all pin state and logic)\n private _pinManager: PinManager | null = null;\n\n // Marker manager (handles debug markers)\n private _markerManager: MarkerManager | null = null;\n\n // Track trigger state for non-scrub mode (play once on enter, reverse on leave)\n private _triggerState: 'before' | 'active' | 'after' = 'before';\n private _toggleActions: [TimelineAction, TimelineAction, TimelineAction, TimelineAction] = ['play', 'none', 'none', 'none'];\n private _initializing: boolean = false;\n\n constructor(timeline: Timeline, config: ScrollConfig) {\n super(timeline, config);\n this._id = ScrollTrigger._nextId++;\n this._scrubLag = typeof config.scrub === 'number' ? config.scrub : 0;\n }\n\n /**\n * Register the global window.load listener (once).\n * After all resources finish loading, refresh every active ScrollTrigger\n * so trigger positions account for layout shifts caused by images/fonts.\n */\n private static _ensureLoadListener(): void {\n if (ScrollTrigger._loadListenerAdded || typeof window === 'undefined') return;\n ScrollTrigger._loadListenerAdded = true;\n\n // If the page already loaded, no need for a listener\n if (document.readyState === 'complete') return;\n\n window.addEventListener('load', () => {\n // Small delay to let browsers finalize layout after load\n setTimeout(() => {\n for (const instance of ScrollTrigger._activeInstances) {\n instance.refresh();\n }\n }, 100);\n }, { once: true });\n }\n\n private _resolveScroller(): void {\n if (this._config.scroller) {\n if (typeof this._config.scroller === 'string') {\n if (typeof document === 'undefined') {\n this._scroller = window;\n return;\n }\n try {\n const el = document.querySelector(this._config.scroller);\n this._scroller = el ?? window;\n } catch (e) {\n console.warn(`[Motion] Invalid scroller selector \"${this._config.scroller}\":`, e);\n this._scroller = window;\n }\n } else {\n this._scroller = this._config.scroller;\n }\n } else {\n this._scroller = window;\n }\n }\n\n private _resolvePinElement(): HTMLElement | null {\n if (!this._config.pin || typeof document === 'undefined') return null;\n\n let resolved: HTMLElement | null = null;\n\n if (typeof this._config.pin === 'string') {\n resolved = document.querySelector(this._config.pin);\n } else if (this._config.pin === true) {\n // Use builder-aware target resolution so split-text cases return the\n // original container element rather than a split char/word fragment.\n // Pin only applies to HTML elements (not SVG), so narrow the type.\n const el = this._getFirstAnimatedElement();\n if (el instanceof HTMLElement) resolved = el;\n }\n\n return resolved;\n }\n\n /**\n * Default start position — matches GSAP behavior:\n * - With pin: 'top top' (element top hits viewport top)\n * - Without pin: 'top bottom' (element top hits viewport bottom)\n */\n private _defaultStart(): string {\n return this._config.start || (this._config.pin ? 'top top' : 'top bottom');\n }\n\n /**\n * Default end position — matches GSAP behavior:\n * 'bottom top' (element bottom hits viewport top)\n */\n private _defaultEnd(): string {\n return this._config.end || 'bottom top';\n }\n\n private _getFirstAnimatedElement(): Element | null {\n // Prefer the builder's original targets when text splitting is active.\n // After split, getTargets() returns individual char/word/line <span> fragments —\n // measuring a tiny char span produces wildly incorrect trigger positions.\n // getOriginalTargets() returns the pre-split container element (e.g. .text-gallery).\n const builder = this._timeline.getFirstChildBuilder();\n if (builder) {\n const targets = builder.hasSplit() ? builder.getOriginalTargets() : builder.getTargets();\n for (const target of targets) {\n if (target instanceof Element) return target;\n }\n }\n\n // Fallback: use the Animation's own target list (no builder available)\n const firstAnimation = this._timeline.getFirstChildAnimation();\n if (firstAnimation) {\n const targets = firstAnimation.getTargets();\n if (targets.length > 0 && targets[0] instanceof Element) {\n return targets[0];\n }\n }\n return null;\n }\n\n /**\n * Resolve the trigger element from config or first animation\n */\n private _resolveTriggerElement(): Element | null {\n // If target is an Element, use it directly (supports both HTML and SVG elements)\n if (this._config.target instanceof Element) {\n return this._config.target;\n }\n\n // If target is a string selector, query for it\n if (typeof this._config.target === 'string' && typeof document !== 'undefined') {\n const el = document.querySelector(this._config.target);\n if (el instanceof Element) {\n return el;\n }\n }\n\n // Fall back to first animated element\n return this._getFirstAnimatedElement();\n }\n\n /**\n * Calculate trigger start/end scroll positions based on config.\n *\n * Pin distance equals the trigger range (triggerEnd - triggerStart).\n * The PinManager spacer adds exactly that many pixels of scroll room to the\n * page, so the animation always plays over the full configured distance while\n * the element is pinned — no separate \"extension\" step is needed.\n *\n * When refresh() is called while the element is position:fixed (pinned), we\n * use the spacer's rect instead of the element's rect. A position:fixed\n * element returns viewport-relative coordinates from getBoundingClientRect(),\n * which would produce incorrect document-relative trigger positions. The\n * spacer sits in the element's original place in the document flow, so its\n * rect always reflects the correct base for the calculation.\n */\n private _calculateTriggerPositions(pinElement: Element | null): void {\n const el = pinElement || this._resolveTriggerElement();\n const scrollerHeight = this._getViewportHeight();\n const scrollTop = this._getScrollTop();\n\n if (!el) {\n this._triggerStart = 0;\n this._triggerEnd = this._getScrollHeight();\n return;\n }\n\n // Prefer the spacer's rect when it is in the DOM: the pinned element is\n // position:fixed and its rect is viewport-relative, not document-relative.\n // For non-pinned elements, strip inline transforms before measuring so the\n // trigger position reflects the element's layout position, not its animated\n // (post-transform) position.\n const elRect = this._pinManager?.getLayoutRect() ?? getLayoutRect(el);\n\n let elTopInDocument: number;\n if (this._scroller instanceof Element) {\n const scrollerRect = this._scroller.getBoundingClientRect();\n elTopInDocument = elRect.top - scrollerRect.top + scrollTop;\n } else {\n elTopInDocument = elRect.top + scrollTop;\n }\n\n // Parse start: \"element_pos scroller_pos\"\n const start = this._defaultStart();\n const startParts = start.split(' ');\n const elStartPos = startParts[0] || 'top';\n const scrollerStartPos = startParts[1] || 'top';\n\n const elStartOffset = parseScrollPosition(elStartPos, elRect.height, scrollerHeight);\n const scrollerStartOffset = parseScrollPosition(scrollerStartPos, scrollerHeight, scrollerHeight);\n\n // Trigger starts when element position reaches scroller position\n this._triggerStart = elTopInDocument + elStartOffset - scrollerStartOffset;\n\n // Parse end\n const end = this._defaultEnd();\n\n if (end.startsWith('+=')) {\n const offset = parseScrollPosition(end.slice(2), scrollerHeight, scrollerHeight);\n this._triggerEnd = this._triggerStart + offset;\n } else {\n const endParts = end.split(' ');\n const elEndPos = endParts[0] || 'bottom';\n const scrollerEndPos = endParts[1] || 'top';\n\n const elEndOffset = parseScrollPosition(elEndPos, elRect.height, scrollerHeight);\n const scrollerEndOffset = parseScrollPosition(scrollerEndPos, scrollerHeight, scrollerHeight);\n\n this._triggerEnd = elTopInDocument + elEndOffset - scrollerEndOffset;\n }\n\n if (this._triggerEnd <= this._triggerStart) {\n this._triggerEnd = this._triggerStart + Math.max(scrollerHeight, 100);\n }\n }\n\n protected _onEnable(): void {\n this._resolveScroller();\n const pinElement = this._resolvePinElement();\n this._calculateTriggerPositions(pinElement);\n\n // Register for window.load refresh and add resize listener\n ScrollTrigger._activeInstances.add(this);\n ScrollTrigger._ensureLoadListener();\n\n // Parse toggleActions string into a 4-tuple (only relevant for non-scrub mode)\n const actionsStr = this._config.toggleActions || 'play none none none';\n const parts = actionsStr.trim().split(/\\s+/);\n this._toggleActions = [\n parts[0] as TimelineAction,\n parts[1] as TimelineAction,\n parts[2] as TimelineAction,\n parts[3] as TimelineAction,\n ];\n\n if (pinElement) {\n // Calculate fixed-pin top position from start config.\n // scrollerStartOffset = viewport Y where the element's reference point was\n // elStartOffset = distance from element's top edge to reference point\n // fixedTop = viewport Y for element's top edge = scrollerStartOffset - elStartOffset\n const start = this._defaultStart();\n const startParts = start.split(' ');\n const viewportHeight = this._getViewportHeight();\n const elStartOffset = parseScrollPosition(startParts[0] || 'top', pinElement.getBoundingClientRect().height, viewportHeight);\n const scrollerStartOffset = parseScrollPosition(startParts[1] || 'top', viewportHeight, viewportHeight);\n const fixedTop = scrollerStartOffset - elStartOffset;\n\n // Pin zone deliberately equals the animation trigger zone:\n // pinStart = _triggerStart, pinEnd = _triggerEnd\n // pinDistance = _triggerEnd - _triggerStart\n //\n // The spacer adds `pinDistance` of scroll room to the page so the\n // animation plays over the full configured distance while the element\n // is fixed — this is the GSAP-compatible behaviour (the spacer IS the\n // \"auto-extension\" of the scrollable range). No separate adjustment to\n // _triggerEnd is required.\n // Use the shared registry to get-or-create the PinManager for this element.\n // This prevents multiple ScrollTrigger instances (e.g. from each:true) from\n // creating nested spacers around the same DOM element.\n this._pinManager = PinManager.getOrCreate(this._id, pinElement);\n this._pinManager.setup(\n pinElement,\n this._triggerStart,\n this._triggerEnd,\n fixedTop,\n this._scroller === window,\n this._config.pinSpacing\n );\n }\n\n if (this._config.markers && typeof document !== 'undefined') {\n const triggerElement = this._pinManager?.getElement() || this._resolveTriggerElement();\n this._markerManager = new MarkerManager(this._id);\n this._markerManager.setup({\n scroller: this._scroller,\n triggerElement,\n startConfig: this._defaultStart(),\n endConfig: this._defaultEnd(),\n markerConfig: this._config.markers,\n viewportHeight: this._getViewportHeight()\n });\n }\n\n this._scrollListener = () => this._onScroll();\n if (this._scroller) {\n this._scroller.addEventListener('scroll', this._scrollListener, { passive: true });\n // Set initial state based on current scroll position without firing actions\n this._initializing = true;\n this._onScroll();\n this._initializing = false;\n\n // Fire initial actions if page loaded mid-scroll (non-scrub mode only).\n // The _initializing pass above synced _triggerState but deliberately skipped\n // actions. Now that state is settled, fire the correct entry actions so\n // elements that are already in (or past) the trigger zone animate immediately.\n if (this._config.scrub === false || this._config.scrub === undefined) {\n if (this._triggerState === 'active') {\n // Page loaded inside trigger zone — fire onEnter\n executeTimelineAction(this._toggleActions[0], this._timeline);\n } else if (this._triggerState === 'after') {\n // Page loaded past trigger zone — show completed state silently.\n // Don't fire toggle actions here because play() + reverse() at time 0\n // leaves the timeline stuck at progress=0 (reverse can't go below 0).\n // Instead, just seek to the end state so elements appear as if the\n // animation already completed naturally.\n this._timeline.progress(1);\n this._timeline.pause();\n }\n }\n }\n }\n\n protected _onDisable(): void {\n if (this._scrollListener && this._scroller) {\n this._scroller.removeEventListener('scroll', this._scrollListener);\n }\n\n this._markerManager?.cleanup();\n this._markerManager = null;\n\n // Release this trigger's share of the pinned element's PinManager.\n // cleanup() only runs when the last user releases (shared registry).\n if (this._pinManager) {\n const pinElement = this._pinManager.getElement();\n if (pinElement) {\n PinManager.release(this._id, pinElement);\n } else {\n this._pinManager.cleanup();\n }\n this._pinManager = null;\n }\n\n this._stopSmoothScrollLoop();\n\n // Unregister from global refresh\n ScrollTrigger._activeInstances.delete(this);\n }\n\n /**\n * Refresh trigger positions (call after layout changes like resize)\n */\n refresh(): void {\n if (!this._enabled) return;\n\n const pinElement = this._pinManager?.getElement() || null;\n\n // Body-pin: temporarily reset html.style.height to its original value before\n // calculating trigger positions. Without this, body's height inherits the\n // inflated html height (bodyHeight + pinDistance), causing trigger positions\n // and pinDistance to snowball on every refresh (\"double pin spacing\" bug).\n // The restore callback re-applies the inflated height; PinManager.setup()\n // will then overwrite it with the correctly re-calculated value.\n const restoreHtmlHeight = this._pinManager?.suspendHtmlHeight?.() ?? null;\n\n this._calculateTriggerPositions(pinElement);\n\n // Restore html height so PinManager.setup() can safely overwrite it.\n restoreHtmlHeight?.();\n\n // Update pin manager with new positions\n if (this._pinManager && pinElement) {\n const start = this._defaultStart();\n const startParts = start.split(' ');\n const viewportHeight = this._getViewportHeight();\n // After Bug 1 fix, getLayoutRect()?.height returns the element's natural height\n // (not the spacer's inflated height that includes padding-bottom = pinDistance).\n const elHeight = this._pinManager.getLayoutRect()?.height ?? pinElement.getBoundingClientRect().height;\n const elStartOffset = parseScrollPosition(startParts[0] || 'top', elHeight, viewportHeight);\n const scrollerStartOffset = parseScrollPosition(startParts[1] || 'top', viewportHeight, viewportHeight);\n const fixedTop = scrollerStartOffset - elStartOffset;\n\n this._pinManager.setup(\n pinElement,\n this._triggerStart,\n this._triggerEnd,\n fixedTop,\n this._scroller === window,\n this._config.pinSpacing\n );\n }\n\n // Update cached marker positions (lightweight — no DOM rebuild, just recache coords)\n if (this._markerManager) {\n this._markerManager.recachePositions(this._getViewportHeight());\n }\n\n // Re-run scroll handler to update state\n this._onScroll();\n }\n private _getViewportHeight(): number {\n if (this._scroller === window) {\n return typeof window !== 'undefined' ? window.innerHeight : 0;\n } else if (this._scroller instanceof Element) {\n return this._scroller.clientHeight;\n }\n return 0;\n }\n\n private _getScrollTop(): number {\n if (this._scroller === window) {\n return typeof window !== 'undefined' ? (window.scrollY || window.pageYOffset) : 0;\n } else if (this._scroller instanceof Element) {\n return this._scroller.scrollTop;\n }\n return 0;\n }\n\n private _getScrollHeight(): number {\n if (this._scroller === window) {\n if (typeof document !== 'undefined' && document.documentElement) {\n return document.documentElement.scrollHeight - window.innerHeight;\n }\n return 0;\n } else if (this._scroller instanceof Element) {\n return this._scroller.scrollHeight - this._scroller.clientHeight;\n }\n return 0;\n }\n\n private _onScroll(): void {\n if (!this._enabled) return;\n\n const scrollTop = this._getScrollTop();\n\n // Calculate progress based on trigger zone\n const triggerRange = this._triggerEnd - this._triggerStart;\n let progress = triggerRange > 0\n ? (scrollTop - this._triggerStart) / triggerRange\n : 0;\n progress = Math.max(0, Math.min(1, progress));\n\n // Apply snap (before assigning to _targetProgress)\n progress = this._applySnap(progress);\n\n this._targetProgress = progress;\n\n // Handle pin\n this._pinManager?.update(scrollTop);\n\n // Update all markers\n if (this._markerManager) {\n const triggerElement = this._pinManager?.getElement() || this._resolveTriggerElement();\n this._markerManager.update(scrollTop, triggerElement, this._getViewportHeight());\n }\n\n // Handle scrub/play\n if (this._config.scrub !== false && this._config.scrub !== undefined) {\n if (this._scrubLag === 0) {\n // Instant scrub: apply progress synchronously.\n // runBatched batches per-property writes into one DOM write per element.\n this._currentProgress = this._targetProgress;\n runBatched(() => this._timeline.progress(this._currentProgress));\n } else {\n this._startSmoothScrollLoop();\n }\n } else {\n // Non-scrub mode: play once on enter, reverse on leave\n // Determine current state\n let newState: 'before' | 'active' | 'after';\n if (progress <= 0) {\n newState = 'before';\n } else if (progress >= 1) {\n newState = 'after';\n } else {\n newState = 'active';\n }\n\n // Handle state transitions\n if (newState !== this._triggerState) {\n const prevState = this._triggerState;\n this._triggerState = newState;\n\n // Don't fire transition actions during initialization — just sync state\n if (this._initializing) return;\n\n // Direction-aware transitions for toggleActions\n // Format: [onEnter, onLeave, onEnterBack, onLeaveBack]\n // Wrapped in runBatched to batch per-property DOM writes\n // (fast-scroll paths call progress() which writes to DOM)\n runBatched(() => {\n if (newState === 'active' && prevState === 'before') {\n // onEnter — scrolling DOWN into trigger zone\n executeTimelineAction(this._toggleActions[0], this._timeline);\n } else if (newState === 'after' && prevState === 'active') {\n // onLeave — scrolling DOWN past trigger zone\n executeTimelineAction(this._toggleActions[1], this._timeline);\n } else if (newState === 'active' && prevState === 'after') {\n // onEnterBack — scrolling UP back into trigger zone\n executeTimelineAction(this._toggleActions[2], this._timeline);\n } else if (newState === 'before' && prevState === 'active') {\n // onLeaveBack — scrolling UP past trigger zone\n executeTimelineAction(this._toggleActions[3], this._timeline);\n } else if (newState === 'after' && prevState === 'before') {\n // Fast-scroll forward: fire onEnter then onLeave\n executeTimelineAction(this._toggleActions[0], this._timeline);\n // Ensure timeline reaches completion before onLeave fires\n this._timeline.progress(1);\n executeTimelineAction(this._toggleActions[1], this._timeline);\n } else if (newState === 'before' && prevState === 'after') {\n // Fast-scroll backward: fire onEnterBack then onLeaveBack\n executeTimelineAction(this._toggleActions[2], this._timeline);\n // Ensure timeline reaches start before onLeaveBack fires\n this._timeline.progress(0);\n executeTimelineAction(this._toggleActions[3], this._timeline);\n }\n });\n }\n }\n }\n\n /**\n * Apply snap to a raw progress value based on config.snap.\n * - number: snap to evenly-spaced increments (e.g. 0.25 → 0, 0.25, 0.5, 0.75, 1)\n * - number[]: snap to nearest value in array\n * - function: custom snap function\n */\n private _applySnap(progress: number): number {\n const snap = this._config.snap;\n if (snap == null) return progress;\n\n if (typeof snap === 'function') {\n return snap(progress);\n }\n\n if (Array.isArray(snap)) {\n // Find nearest value in array\n let closest = snap[0] ?? progress;\n let minDist = Math.abs(progress - closest);\n for (let i = 1; i < snap.length; i++) {\n const dist = Math.abs(progress - snap[i]!);\n if (dist < minDist) {\n minDist = dist;\n closest = snap[i]!;\n }\n }\n return closest;\n }\n\n if (typeof snap === 'number' && snap > 0) {\n // Snap to evenly-spaced increments\n return Math.round(progress / snap) * snap;\n }\n\n return progress;\n }\n\n private _startSmoothScrollLoop(): void {\n if (this._scrubTickListener) return; // idempotent — listener already registered\n\n this._scrubTickListener = (deltaTime: number, _elapsedTime: number) => {\n if (!this._enabled) {\n this._stopSmoothScrollLoop();\n return;\n }\n\n const diff = this._targetProgress - this._currentProgress;\n\n // Frame-rate independent exponential smoothing:\n // exp(-3) ≈ 0.05, so after scrubLag seconds ~95% of distance is covered\n const catchUp = 1 - Math.exp((-deltaTime * 3) / this._scrubLag);\n this._currentProgress += diff * catchUp;\n\n // Snap to target when close enough and unregister the listener\n if (Math.abs(this._targetProgress - this._currentProgress) < 0.0001) {\n this._currentProgress = this._targetProgress;\n this._timeline.progress(this._currentProgress);\n this._stopSmoothScrollLoop();\n return;\n }\n\n this._timeline.progress(this._currentProgress);\n };\n\n Ticker.getInstance().add(this._scrubTickListener, ScrollTrigger._SCRUB_TICKER_PRIORITY);\n }\n\n private _stopSmoothScrollLoop(): void {\n if (this._scrubTickListener) {\n Ticker.getInstance().remove(this._scrubTickListener);\n this._scrubTickListener = null;\n }\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('scroll', ScrollTrigger);\n",
|
|
22
22
|
"/**\n * PageLoadTrigger\n *\n * Triggers timelines based on page load events:\n * - timing: 'before' - plays immediately (before DOMContentLoaded)\n * - timing: 'during' - plays on DOMContentLoaded\n * - timing: 'after' - plays on window load event\n */\n\nimport type { Timeline } from '../core/Timeline';\nimport type { PageLoadTriggerConfig } from './TriggerManager';\nimport { TriggerManager } from './TriggerManager';\n\ninterface PageLoadRegistration {\n timeline: Timeline;\n timing: 'before' | 'during' | 'after';\n paused: boolean;\n}\n\nexport class PageLoadTrigger {\n private _registrations: PageLoadRegistration[] = [];\n private _domContentLoaded: boolean = false;\n private _windowLoaded: boolean = false;\n private _initialized: boolean = false;\n\n constructor() {\n this._initialize();\n }\n\n private _initialize(): void {\n if (this._initialized) return;\n this._initialized = true;\n\n // Check if already loaded\n if (typeof document !== 'undefined') {\n // Always add listeners in case document is still loading\n document.addEventListener('DOMContentLoaded', () => {\n this._domContentLoaded = true;\n this._triggerDuring();\n });\n\n if (typeof window !== 'undefined') {\n window.addEventListener('load', () => {\n this._windowLoaded = true;\n this._triggerAfter();\n });\n }\n\n // Check current state\n if (document.readyState === 'interactive' || document.readyState === 'complete') {\n // DOMContentLoaded already fired\n this._domContentLoaded = true;\n }\n\n if (document.readyState === 'complete') {\n // Everything already loaded\n this._windowLoaded = true;\n }\n }\n }\n\n /**\n * Register a timeline with pageLoad trigger\n */\n register(timeline: Timeline, config: PageLoadTriggerConfig): void {\n const timing = config.timing || 'during';\n const paused = config.paused === true;\n\n this._registrations.push({\n timeline,\n timing,\n paused,\n });\n\n // If paused, the timeline is built but not played — user calls .play() manually\n if (paused) return;\n\n // If timing is 'before' or events already fired, play immediately\n if (timing === 'before') {\n timeline.play();\n } else if (timing === 'during' && this._domContentLoaded) {\n timeline.play();\n } else if (timing === 'after' && this._windowLoaded) {\n timeline.play();\n }\n }\n\n /**\n * Unregister a timeline from page load trigger\n */\n unregister(timeline: Timeline): void {\n this._registrations = this._registrations.filter(reg => reg.timeline !== timeline);\n }\n\n /**\n * Clear all registrations\n */\n clear(): void {\n this._registrations = [];\n }\n\n private _triggerDuring(): void {\n this._registrations\n .filter(reg => reg.timing === 'during' && !reg.paused)\n .forEach(reg => reg.timeline.play());\n }\n\n private _triggerAfter(): void {\n this._registrations\n .filter(reg => reg.timing === 'after' && !reg.paused)\n .forEach(reg => reg.timeline.play());\n }\n\n /**\n * Reset for testing - DO NOT USE IN PRODUCTION\n * @internal\n */\n _reset(): void {\n this._registrations = [];\n this._domContentLoaded = false;\n this._windowLoaded = false;\n this._initialized = false;\n // Re-initialize to set up event listeners with fresh state\n this._initialize();\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerPageLoadTrigger(PageLoadTrigger);\n",
|
|
23
23
|
"/**\n * MouseMoveTrigger\n *\n * Triggers timelines based on mouse movement:\n * - distance: progress based on distance from center of viewport/target\n * - axis: progress based on X/Y position within viewport/target\n *\n * Supports:\n * - Viewport or custom target element tracking\n * - Optional smoothing (lerp interpolation)\n * - Custom start/leave progress values\n * - Per-element triggers with \"each\" mode\n */\n\nimport type { MouseMoveConfig } from '../types';\nimport type { Timeline } from '../core/Timeline';\nimport { Ticker } from '../core/Ticker';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\nimport { getLayoutRect } from '../utils/getLayoutRect';\n\ninterface MouseState {\n /** Current X progress (0-1) */\n x: number;\n /** Current Y progress (0-1) */\n y: number;\n /** Current distance progress (0-1) */\n distance: number;\n /** Whether mouse is currently inside the tracking area */\n isInside: boolean;\n}\n\ninterface TargetState {\n /** Target X progress (before smoothing) */\n targetX: number;\n /** Target Y progress (before smoothing) */\n targetY: number;\n /** Target distance progress (before smoothing) */\n targetDistance: number;\n}\n\nexport class MouseMoveTrigger extends BaseTrigger<MouseMoveConfig> {\n private _targets: Element[] = [];\n private _listeners: Map<EventTarget, Map<string, EventListener>> = new Map();\n\n // Frozen rects snapshotted at rest so that progress calculations remain stable\n // while animations transform target elements.\n private _frozenRects: Map<Element, DOMRect> = new Map();\n\n // Mouse state\n private _state: MouseState = {\n x: 0.5,\n y: 0.5,\n distance: 0.5,\n isInside: false,\n };\n\n // Target state for smoothing\n private _targetState: TargetState = {\n targetX: 0.5,\n targetY: 0.5,\n targetDistance: 0.5,\n };\n\n // Ticker callback for smoothing\n private _tickerCallback?: (deltaTime: number) => void;\n\n // Defaults\n private readonly _defaultStartProgress = 0.5;\n private readonly _defaultLeaveProgress = 0.5;\n\n constructor(timeline: Timeline, config: MouseMoveConfig) {\n super(timeline, config);\n this._config = {\n type: 'distance',\n startProgress: this._defaultStartProgress,\n leaveProgress: this._defaultLeaveProgress,\n ...config,\n };\n\n // Initialize state with start progress\n const startProgress = this._config.startProgress ?? this._defaultStartProgress;\n this._state.x = startProgress;\n this._state.y = startProgress;\n this._state.distance = startProgress;\n this._targetState.targetX = startProgress;\n this._targetState.targetY = startProgress;\n this._targetState.targetDistance = startProgress;\n }\n\n /**\n * Set up mouse event listeners once DOM is ready.\n */\n protected _onEnable(): void {\n // Resolve target elements\n this._targets = this._resolveElements(this._config.target);\n\n // Add mouse event listeners\n if (this._targets.length === 0) {\n // Viewport mode\n this._addViewportListeners();\n } else {\n // Target element mode\n this._addTargetListeners();\n }\n\n // Start ticker for smoothing if enabled\n if (this._config.smooth !== undefined && this._config.smooth > 0) {\n this._startSmoothingTicker();\n }\n\n // Set initial progress\n this._updateTimelineProgress();\n }\n\n /**\n * Remove all event listeners and stop the smoothing ticker.\n */\n protected _onDisable(): void {\n // Stop smoothing ticker\n if (this._tickerCallback) {\n Ticker.getInstance().remove(this._tickerCallback);\n this._tickerCallback = undefined;\n }\n\n // Clear frozen rects\n this._frozenRects.clear();\n\n // Remove all listeners\n this._cleanupListenerMap(this._listeners);\n }\n\n private _addViewportListeners(): void {\n if (typeof window === 'undefined') return;\n\n const listeners = new Map<string, EventListener>();\n\n // Mouse move on window\n const mousemoveListener = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n this._handleViewportMouseMove(mouseEvent.clientX, mouseEvent.clientY);\n };\n window.addEventListener('mousemove', mousemoveListener);\n listeners.set('mousemove', mousemoveListener);\n\n // For viewport mode, mouse is always \"inside\"\n this._state.isInside = true;\n\n this._listeners.set(window, listeners);\n }\n\n /**\n * Re-snapshot frozen rects after layout changes (e.g. window resize).\n * Called by TriggerManager on resize.\n */\n refresh(): void {\n for (const element of this._targets) {\n this._frozenRects.set(element, getLayoutRect(element));\n }\n }\n\n private _addTargetListeners(): void {\n this._targets.forEach(element => {\n // Snapshot bounds at rest position so progress calculations are stable\n // even while the element is being animated (e.g. a translateY hover animation).\n this._frozenRects.set(element, getLayoutRect(element));\n\n const elementListeners = new Map<string, EventListener>();\n\n // Mouse move\n const mousemoveListener = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n this._handleTargetMouseMove(element, mouseEvent.clientX, mouseEvent.clientY);\n };\n element.addEventListener('mousemove', mousemoveListener);\n elementListeners.set('mousemove', mousemoveListener);\n\n // Mouse enter\n const mouseenterListener = () => {\n this._state.isInside = true;\n };\n element.addEventListener('mouseenter', mouseenterListener);\n elementListeners.set('mouseenter', mouseenterListener);\n\n // Mouse leave\n const mouseleaveListener = () => {\n this._state.isInside = false;\n this._handleMouseLeave();\n };\n element.addEventListener('mouseleave', mouseleaveListener);\n elementListeners.set('mouseleave', mouseleaveListener);\n\n this._listeners.set(element, elementListeners);\n });\n }\n\n private _handleViewportMouseMove(clientX: number, clientY: number): void {\n if (typeof window === 'undefined') return;\n\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const centerX = viewportWidth / 2;\n const centerY = viewportHeight / 2;\n\n if (this._config.type === 'axis') {\n // Axis mode: map position to 0-1 range\n // Left/top = 0, Right/bottom = 1\n this._targetState.targetX = clientX / viewportWidth;\n this._targetState.targetY = clientY / viewportHeight;\n } else {\n // Distance mode: calculate distance from center\n const dx = clientX - centerX;\n const dy = clientY - centerY;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // Calculate max distance (to farthest corner)\n const maxDistance = Math.sqrt(centerX * centerX + centerY * centerY);\n\n // Map distance to progress: closer = 1 (end), farther = 0 (start)\n this._targetState.targetDistance = 1 - Math.min(distance / maxDistance, 1);\n }\n\n this._applyProgress();\n }\n\n private _handleTargetMouseMove(element: Element, clientX: number, clientY: number): void {\n // Use the frozen layout rect captured at setup (or last refresh) rather than\n // a live getBoundingClientRect() call. This keeps progress calculations\n // anchored to the element's rest position, unaffected by animation transforms.\n const rect = this._frozenRects.get(element) ?? element.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n const centerY = rect.top + rect.height / 2;\n\n if (this._config.type === 'axis') {\n // Axis mode: map position within element to 0-1 range\n const relativeX = clientX - rect.left;\n const relativeY = clientY - rect.top;\n this._targetState.targetX = Math.max(0, Math.min(1, relativeX / rect.width));\n this._targetState.targetY = Math.max(0, Math.min(1, relativeY / rect.height));\n } else {\n // Distance mode: calculate distance from element center\n const dx = clientX - centerX;\n const dy = clientY - centerY;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // Calculate max distance (to farthest corner of element)\n const maxDistance = Math.sqrt(\n Math.pow(rect.width / 2, 2) + Math.pow(rect.height / 2, 2)\n );\n\n // Map distance to progress: closer = 1 (end), farther = 0 (start)\n this._targetState.targetDistance = 1 - Math.min(distance / maxDistance, 1);\n }\n\n this._applyProgress();\n }\n\n private _handleMouseLeave(): void {\n const leaveProgress = this._config.leaveProgress ?? this._defaultLeaveProgress;\n\n // Set target to leave progress\n this._targetState.targetX = leaveProgress;\n this._targetState.targetY = leaveProgress;\n this._targetState.targetDistance = leaveProgress;\n\n // If no smoothing, apply immediately\n if (!this._config.smooth) {\n this._state.x = leaveProgress;\n this._state.y = leaveProgress;\n this._state.distance = leaveProgress;\n this._updateTimelineProgress();\n }\n // With smoothing, the ticker will handle the transition\n }\n\n private _applyProgress(): void {\n if (this._config.smooth) {\n // Smoothing enabled - ticker handles interpolation\n return;\n }\n\n // No smoothing - apply directly\n this._state.x = this._targetState.targetX;\n this._state.y = this._targetState.targetY;\n this._state.distance = this._targetState.targetDistance;\n this._updateTimelineProgress();\n }\n\n private _startSmoothingTicker(): void {\n // Clamp smooth to valid range (0-1)\n const smooth = Math.max(0, Math.min(1, this._config.smooth ?? 0.1));\n\n this._tickerCallback = (deltaTime: number) => {\n // Lerp current values toward target (frame-rate independent)\n const newX = this._lerp(this._state.x, this._targetState.targetX, smooth, deltaTime);\n const newY = this._lerp(this._state.y, this._targetState.targetY, smooth, deltaTime);\n const newDistance = this._lerp(this._state.distance, this._targetState.targetDistance, smooth, deltaTime);\n\n // Check if values have converged (within epsilon) - skip update if idle\n const epsilon = 0.0001;\n const xConverged = Math.abs(newX - this._state.x) < epsilon && Math.abs(newX - this._targetState.targetX) < epsilon;\n const yConverged = Math.abs(newY - this._state.y) < epsilon && Math.abs(newY - this._targetState.targetY) < epsilon;\n const distConverged = Math.abs(newDistance - this._state.distance) < epsilon && Math.abs(newDistance - this._targetState.targetDistance) < epsilon;\n\n // Update state\n this._state.x = newX;\n this._state.y = newY;\n this._state.distance = newDistance;\n\n // Only update timeline if values actually changed\n if (!xConverged || !yConverged || !distConverged) {\n this._updateTimelineProgress();\n }\n };\n\n Ticker.getInstance().add(this._tickerCallback);\n }\n\n private _lerp(current: number, target: number, smooth: number, deltaTime: number): number {\n // Lerp with small epsilon check to avoid infinite tiny updates\n const diff = target - current;\n if (Math.abs(diff) < 0.0001) {\n return target;\n }\n // Frame-rate independent smoothing using exponential decay\n // smooth is inverted: 0 = instant/fast, 1 = maximum smoothing (slowest)\n // Maps smooth 0→1 to internal lerp factor ~0.5→0.005\n const lerpFactor = 0.5 * Math.pow(1 - smooth, 2) + 0.005;\n const decay = 1 - Math.pow(1 - lerpFactor, deltaTime * 60);\n return current + diff * decay;\n }\n\n private _updateTimelineProgress(): void {\n if (this._config.type === 'axis') {\n // Axis mode: set progress per axis\n this._timeline.setAxisProgress('x', this._state.x);\n this._timeline.setAxisProgress('y', this._state.y);\n } else {\n // Distance mode: set overall progress\n this._timeline.progress(this._state.distance);\n }\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('mouseMove', MouseMoveTrigger);\n",
|
|
24
24
|
"/**\n * GestureTrigger\n *\n * Detects user gestures (pointer, touch, wheel, scroll) and triggers\n * timeline actions based on directional movement and interaction events.\n *\n * Performance optimizations:\n * - RAF synchronization for callback execution\n * - Throttled scroll/wheel event processing\n * - Cached config values\n * - Minimal allocations in hot paths\n */\n\nimport type { GestureConfig, GestureEvent, GestureAction } from '../types';\nimport type { Timeline } from '../core/Timeline';\nimport { executeTimelineAction } from '../utils/executeTimelineAction';\nimport type { TimelineAction } from '../utils/executeTimelineAction';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\nimport { getLayoutRect } from '../utils/getLayoutRect';\n\n/** Local typed view of GestureConfig with properly-typed internal fields for each-mode. */\ninterface GestureConfigTyped extends GestureConfig {\n _siblings?: Timeline[];\n _index?: number;\n}\n\nexport class GestureTrigger extends BaseTrigger<GestureConfig> {\n private _target: EventTarget = window;\n\n // Cached config values (avoid repeated lookups in hot paths)\n private _tolerance: number;\n private _dragMinimum: number;\n private _dragMinimumSquared: number;\n private _wheelSpeed: number;\n private _scrollSpeed: number;\n private _stopDelay: number;\n private _preventDefault: boolean;\n private _lockAxis: boolean;\n private _animationStep: number | Partial<Record<string, number>>;\n private _smooth: number;\n\n // Gesture state\n private _startX: number = 0;\n private _startY: number = 0;\n private _deltaX: number = 0;\n private _deltaY: number = 0;\n private _lockedAxis: 'x' | 'y' | null = null;\n private _isPressed: boolean = false;\n private _isDragging: boolean = false;\n private _lastDirectionX: 1 | -1 | null = null;\n private _lastDirectionY: 1 | -1 | null = null;\n\n // Scroll tracking\n private _lastScrollX: number = 0;\n private _lastScrollY: number = 0;\n\n // Stop detection (timestamp-based to reduce timeout churn)\n private _stopTimeout: ReturnType<typeof setTimeout> | null = null;\n private _isMoving: boolean = false;\n private _lastActivityTime: number = 0;\n\n // RAF synchronization for actions\n private _pendingActions: Map<GestureAction, GestureEvent> = new Map();\n private _rafScheduled: boolean = false;\n private _rafId: number | null = null;\n\n // Wheel/scroll throttling\n private _wheelAccumulatorX: number = 0;\n private _wheelAccumulatorY: number = 0;\n private _wheelRafId: number | null = null;\n private _scrollRafId: number | null = null;\n\n // Pointer move tracking for early exit\n private _lastPointerX: number = 0;\n private _lastPointerY: number = 0;\n\n // Tracks which directions fired during the current gesture (for Complete events)\n private _activatedDirections: Set<string> = new Set();\n\n // Smooth progress interpolation\n private _targetProgress: number = 0;\n private _currentProgress: number = 0;\n private _interpolationRafId: number | null = null;\n\n // Sequence control for each mode (playNext/playPrevious)\n private _siblings: Timeline[] | null = null;\n private _index: number = 0;\n // Shared state object for tracking last played index across siblings\n // All siblings in the same group share the same object reference\n private static _sequenceStates: WeakMap<Timeline[], { lastIndex: number }> = new WeakMap();\n\n // Bound event handlers (for cleanup)\n private _boundHandlers: {\n pointerDown?: (e: PointerEvent) => void;\n pointerMove?: (e: PointerEvent) => void;\n pointerUp?: (e: PointerEvent) => void;\n touchStart?: (e: TouchEvent) => void;\n touchMove?: (e: TouchEvent) => void;\n touchEnd?: (e: TouchEvent) => void;\n wheel?: (e: WheelEvent) => void;\n scroll?: () => void;\n } = {};\n\n // Hover frozen-rect state — bounds are snapshotted at rest so that\n // translateY animations don't cause infinite mouseenter/mouseleave jitter.\n private _frozenHoverRect: DOMRect | null = null;\n private _gestureHoverActive: boolean = false;\n private _boundHoverMoveHandler: ((e: MouseEvent) => void) | null = null;\n\n constructor(timeline: Timeline, config: GestureConfig) {\n super(timeline, config);\n\n // Cache config values for hot path performance\n this._tolerance = config.tolerance ?? 1;\n this._dragMinimum = config.dragMinimum ?? 10;\n this._dragMinimumSquared = this._dragMinimum * this._dragMinimum;\n this._wheelSpeed = config.wheelSpeed ?? 1;\n this._scrollSpeed = config.scrollSpeed ?? 1;\n this._stopDelay = config.stopDelay ?? 150;\n this._preventDefault = config.preventDefault ?? false;\n this._lockAxis = config.lockAxis ?? false;\n this._animationStep = config.animationStep ?? 0.1;\n this._smooth = Math.max(0, Math.min(1, config.smooth ?? 0));\n\n // Set up sequence control for each mode\n const typedConfig = config as GestureConfigTyped;\n if (typedConfig._siblings && typedConfig._index !== undefined) {\n this._siblings = typedConfig._siblings;\n this._index = typedConfig._index;\n\n // Initialize shared sequence state if not exists\n if (!GestureTrigger._sequenceStates.has(this._siblings)) {\n GestureTrigger._sequenceStates.set(this._siblings, { lastIndex: 0 });\n }\n }\n }\n\n /**\n * Resolve target and attach gesture listeners once DOM is ready.\n */\n protected _onEnable(): void {\n // Resolve target (falls back to window if not configured or not found)\n const resolved = this._resolveElement(this._config.target, window);\n this._target = resolved ?? window;\n\n // Attach listeners based on enabled types\n const types = this._config.types;\n\n if (types.includes('pointer')) {\n this._addPointerListeners();\n }\n\n if (types.includes('touch')) {\n this._addTouchListeners();\n }\n\n if (types.includes('wheel')) {\n this._addWheelListener();\n }\n\n if (types.includes('scroll')) {\n this._addScrollListener();\n }\n\n // Always add hover listeners if Hover or HoverEnd events exist\n if (this._config.events.Hover || this._config.events.HoverEnd) {\n this._addHoverListeners();\n }\n }\n\n /**\n * Cancel all pending timers/RAF and remove all event listeners.\n */\n protected _onDisable(): void {\n // Clear stop timeout\n if (this._stopTimeout) {\n clearTimeout(this._stopTimeout);\n this._stopTimeout = null;\n }\n\n // Cancel any pending RAF\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n if (this._wheelRafId !== null) {\n cancelAnimationFrame(this._wheelRafId);\n this._wheelRafId = null;\n }\n if (this._scrollRafId !== null) {\n cancelAnimationFrame(this._scrollRafId);\n this._scrollRafId = null;\n }\n if (this._interpolationRafId !== null) {\n cancelAnimationFrame(this._interpolationRafId);\n this._interpolationRafId = null;\n }\n this._rafScheduled = false;\n this._pendingActions.clear();\n\n // Remove all listeners\n const target = this._target;\n\n if (this._boundHandlers.pointerDown) {\n target.removeEventListener('pointerdown', this._boundHandlers.pointerDown as EventListener);\n window.removeEventListener('pointermove', this._boundHandlers.pointerMove as EventListener);\n window.removeEventListener('pointerup', this._boundHandlers.pointerUp as EventListener);\n }\n\n if (this._boundHandlers.touchStart) {\n target.removeEventListener('touchstart', this._boundHandlers.touchStart as EventListener);\n window.removeEventListener('touchmove', this._boundHandlers.touchMove as EventListener);\n window.removeEventListener('touchend', this._boundHandlers.touchEnd as EventListener);\n }\n\n if (this._boundHandlers.wheel) {\n target.removeEventListener('wheel', this._boundHandlers.wheel as EventListener);\n }\n\n if (this._boundHandlers.scroll) {\n const scrollTarget = this._target === window ? window : this._target;\n scrollTarget.removeEventListener('scroll', this._boundHandlers.scroll);\n }\n\n // Remove hover frozen-rect mousemove handler\n if (this._boundHoverMoveHandler) {\n window.removeEventListener('mousemove', this._boundHoverMoveHandler);\n this._boundHoverMoveHandler = null;\n }\n this._frozenHoverRect = null;\n this._gestureHoverActive = false;\n\n this._boundHandlers = {};\n }\n\n // ============================================\n // Pointer Events\n // ============================================\n\n private _addPointerListeners(): void {\n this._boundHandlers.pointerDown = this._handlePointerDown.bind(this);\n this._boundHandlers.pointerMove = this._handlePointerMove.bind(this);\n this._boundHandlers.pointerUp = this._handlePointerUp.bind(this);\n\n // Use passive listeners when preventDefault is not needed\n const passiveOption = { passive: !this._preventDefault };\n\n this._target.addEventListener(\n 'pointerdown',\n this._boundHandlers.pointerDown as EventListener,\n passiveOption\n );\n // Move and up on window to catch events outside target\n window.addEventListener(\n 'pointermove',\n this._boundHandlers.pointerMove as EventListener,\n passiveOption\n );\n window.addEventListener('pointerup', this._boundHandlers.pointerUp as EventListener);\n }\n\n private _handlePointerDown(e: PointerEvent): void {\n // Skip touch-originated pointer events if touch listeners are also enabled\n // to prevent double-firing on touch devices\n if (e.pointerType === 'touch' && this._config.types.includes('touch')) {\n return;\n }\n\n if (this._preventDefault) {\n e.preventDefault();\n }\n\n this._handleInputStart(e.clientX, e.clientY);\n }\n\n private _handlePointerMove(e: PointerEvent): void {\n // Skip touch-originated pointer events if touch listeners are also enabled\n if (e.pointerType === 'touch' && this._config.types.includes('touch')) {\n return;\n }\n\n if (this._handleInputMove(e.clientX, e.clientY)) {\n if (this._preventDefault) {\n e.preventDefault();\n }\n }\n }\n\n private _handlePointerUp(e: PointerEvent): void {\n // Skip touch-originated pointer events if touch listeners are also enabled\n if (e.pointerType === 'touch' && this._config.types.includes('touch')) {\n return;\n }\n\n this._handleInputEnd();\n }\n\n // ============================================\n // Touch Events\n // ============================================\n\n private _addTouchListeners(): void {\n this._boundHandlers.touchStart = this._handleTouchStart.bind(this);\n this._boundHandlers.touchMove = this._handleTouchMove.bind(this);\n this._boundHandlers.touchEnd = this._handleTouchEnd.bind(this);\n\n const passiveOption = { passive: !this._preventDefault };\n\n this._target.addEventListener(\n 'touchstart',\n this._boundHandlers.touchStart as EventListener,\n passiveOption\n );\n window.addEventListener(\n 'touchmove',\n this._boundHandlers.touchMove as EventListener,\n passiveOption\n );\n window.addEventListener('touchend', this._boundHandlers.touchEnd as EventListener);\n }\n\n private _handleTouchStart(e: TouchEvent): void {\n if (this._preventDefault) {\n e.preventDefault();\n }\n\n const touch = e.touches[0];\n if (!touch) return;\n\n this._handleInputStart(touch.clientX, touch.clientY);\n }\n\n private _handleTouchMove(e: TouchEvent): void {\n const touch = e.touches[0];\n if (!touch) return;\n\n if (this._handleInputMove(touch.clientX, touch.clientY)) {\n if (this._preventDefault) {\n e.preventDefault();\n }\n }\n }\n\n private _handleTouchEnd(_e: TouchEvent): void {\n this._handleInputEnd();\n }\n\n // ============================================\n // Shared Input Handlers (pointer/touch core logic)\n // ============================================\n\n /**\n * Shared start logic for pointer and touch input.\n */\n private _handleInputStart(x: number, y: number): void {\n this._fireEvent('PressInit');\n\n this._startX = x;\n this._startY = y;\n this._lastPointerX = x;\n this._lastPointerY = y;\n this._deltaX = 0;\n this._deltaY = 0;\n this._isPressed = true;\n this._isDragging = false;\n this._lockedAxis = null;\n\n this._fireEvent('Press');\n }\n\n /**\n * Shared move logic for pointer and touch input.\n * Returns true if movement was processed (caller should preventDefault if needed).\n */\n private _handleInputMove(x: number, y: number): boolean {\n if (!this._isPressed) return false;\n\n // Early exit for minimal movement (< 1px) to reduce processing\n const movementX = x - this._lastPointerX;\n const movementY = y - this._lastPointerY;\n if (Math.abs(movementX) < 1 && Math.abs(movementY) < 1) {\n return false;\n }\n this._lastPointerX = x;\n this._lastPointerY = y;\n\n const newDeltaX = x - this._startX;\n const newDeltaY = y - this._startY;\n\n this._updateDeltas(newDeltaX, newDeltaY);\n this._checkDragStart();\n this._checkDirectionAndFire(false);\n this._checkToggle(newDeltaX, newDeltaY);\n this._scheduleStopCheck();\n\n if (this._isDragging) {\n this._fireEvent('Drag');\n }\n\n return true;\n }\n\n /**\n * Shared end logic for pointer and touch input.\n */\n private _handleInputEnd(): void {\n if (!this._isPressed) return;\n\n if (this._isDragging) {\n this._fireEvent('DragEnd');\n }\n\n this._fireEvent('Release');\n this._fireCompleteCallbacks();\n this._fireEvent('Stop');\n\n this._resetGestureState();\n }\n\n // ============================================\n // Wheel Events (with RAF throttling)\n // ============================================\n\n private _addWheelListener(): void {\n this._boundHandlers.wheel = this._handleWheel.bind(this);\n this._target.addEventListener('wheel', this._boundHandlers.wheel as EventListener, {\n passive: !this._preventDefault,\n });\n }\n\n private _handleWheel(e: WheelEvent): void {\n if (this._preventDefault) {\n e.preventDefault();\n }\n\n // Accumulate wheel deltas for RAF processing\n this._wheelAccumulatorX += e.deltaX * this._wheelSpeed;\n this._wheelAccumulatorY += e.deltaY * this._wheelSpeed;\n\n // Throttle processing to RAF\n if (this._wheelRafId !== null) return;\n\n this._wheelRafId = requestAnimationFrame(() => {\n this._wheelRafId = null;\n\n // Use accumulated deltas\n this._deltaX = this._wheelAccumulatorX;\n this._deltaY = this._wheelAccumulatorY;\n this._wheelAccumulatorX = 0;\n this._wheelAccumulatorY = 0;\n\n this._checkDirectionAndFire(true);\n this._checkToggle(this._deltaX, this._deltaY);\n this._scheduleStopCheck();\n\n // Reset deltas after processing\n this._deltaX = 0;\n this._deltaY = 0;\n });\n }\n\n // ============================================\n // Scroll Events (with RAF throttling)\n // ============================================\n\n private _addScrollListener(): void {\n this._boundHandlers.scroll = this._handleScroll.bind(this);\n\n // Initialize scroll position\n if (this._target === window) {\n this._lastScrollX = window.scrollX;\n this._lastScrollY = window.scrollY;\n } else {\n const element = this._target as Element;\n this._lastScrollX = element.scrollLeft;\n this._lastScrollY = element.scrollTop;\n }\n\n this._target.addEventListener('scroll', this._boundHandlers.scroll, { passive: true });\n }\n\n private _handleScroll(): void {\n // Throttle processing to RAF\n if (this._scrollRafId !== null) return;\n\n this._scrollRafId = requestAnimationFrame(() => {\n this._scrollRafId = null;\n this._processScroll();\n });\n }\n\n private _processScroll(): void {\n let currentScrollX: number;\n let currentScrollY: number;\n\n if (this._target === window) {\n currentScrollX = window.scrollX;\n currentScrollY = window.scrollY;\n } else {\n const element = this._target as Element;\n currentScrollX = element.scrollLeft;\n currentScrollY = element.scrollTop;\n }\n\n const deltaX = (currentScrollX - this._lastScrollX) * this._scrollSpeed;\n const deltaY = (currentScrollY - this._lastScrollY) * this._scrollSpeed;\n\n this._lastScrollX = currentScrollX;\n this._lastScrollY = currentScrollY;\n\n // Use scroll deltas directly (instant like wheel)\n this._deltaX = deltaX;\n this._deltaY = deltaY;\n\n this._checkDirectionAndFire(true);\n this._checkToggle(deltaX, deltaY);\n this._scheduleStopCheck();\n\n // Reset deltas\n this._deltaX = 0;\n this._deltaY = 0;\n }\n\n // ============================================\n // Hover Events\n // ============================================\n\n private _addHoverListeners(): void {\n // Hover only makes sense on elements, not window\n if (this._target === window) return;\n\n const element = this._target as Element;\n\n // Snapshot element bounds at rest position using getLayoutRect.\n // This prevents jitter when animations move the element away from the cursor.\n this._frozenHoverRect = getLayoutRect(element);\n\n this._boundHoverMoveHandler = (e: MouseEvent) => {\n if (!this._frozenHoverRect) return;\n const rect = this._frozenHoverRect;\n const isOver =\n e.clientX >= rect.left && e.clientX <= rect.right &&\n e.clientY >= rect.top && e.clientY <= rect.bottom;\n\n if (isOver && !this._gestureHoverActive) {\n this._gestureHoverActive = true;\n this._fireEvent('Hover');\n } else if (!isOver && this._gestureHoverActive) {\n this._gestureHoverActive = false;\n this._fireEvent('HoverEnd');\n }\n };\n\n window.addEventListener('mousemove', this._boundHoverMoveHandler);\n }\n\n /**\n * Re-snapshot frozen hover rect after layout changes (e.g. window resize).\n * Called by TriggerManager on resize.\n */\n refresh(): void {\n if (this._target === window || !this._frozenHoverRect) return;\n this._frozenHoverRect = getLayoutRect(this._target as Element);\n }\n\n // ============================================\n // Direction Detection\n // ============================================\n\n private _updateDeltas(newDeltaX: number, newDeltaY: number): void {\n // Apply axis locking\n if (this._lockAxis && this._lockedAxis) {\n if (this._lockedAxis === 'x') {\n this._deltaX = newDeltaX;\n this._deltaY = 0;\n } else {\n this._deltaX = 0;\n this._deltaY = newDeltaY;\n }\n } else {\n this._deltaX = newDeltaX;\n this._deltaY = newDeltaY;\n }\n\n // Determine axis lock if enabled and not yet locked\n if (this._lockAxis && !this._lockedAxis) {\n const tolerance = this._tolerance;\n if (Math.abs(this._deltaX) > tolerance || Math.abs(this._deltaY) > tolerance) {\n this._lockedAxis = Math.abs(this._deltaX) > Math.abs(this._deltaY) ? 'x' : 'y';\n }\n }\n }\n\n /**\n * Check and fire directional callbacks.\n * @param instant - If true (wheel/scroll), skips axis-lock checks and direction tracking.\n * If false (pointer/touch), respects axis locking and tracks activated directions.\n */\n private _checkDirectionAndFire(instant: boolean): void {\n const tolerance = this._tolerance;\n\n // Check Y axis (up/down)\n // For continuous gestures (pointer/touch), respect axis locking\n if (instant || !this._lockedAxis || this._lockedAxis === 'y') {\n if (this._deltaY < -tolerance) {\n this._fireEvent('Up');\n if (!instant) this._activatedDirections.add('Up');\n }\n if (this._deltaY > tolerance) {\n this._fireEvent('Down');\n if (!instant) this._activatedDirections.add('Down');\n }\n }\n\n // Check X axis (left/right)\n if (instant || !this._lockedAxis || this._lockedAxis === 'x') {\n if (this._deltaX < -tolerance) {\n this._fireEvent('Left');\n if (!instant) this._activatedDirections.add('Left');\n }\n if (this._deltaX > tolerance) {\n this._fireEvent('Right');\n if (!instant) this._activatedDirections.add('Right');\n }\n }\n\n // onChange fires on any movement past tolerance\n if (Math.abs(this._deltaX) > tolerance || Math.abs(this._deltaY) > tolerance) {\n this._fireEvent('Change');\n\n if (Math.abs(this._deltaX) > tolerance) {\n this._fireEvent('ChangeX');\n }\n if (Math.abs(this._deltaY) > tolerance) {\n this._fireEvent('ChangeY');\n }\n }\n }\n\n /**\n * Check for direction toggle (reversal)\n */\n private _checkToggle(deltaX: number, deltaY: number): void {\n const tolerance = this._tolerance;\n\n // Only check ToggleX when X movement exceeds tolerance\n if (Math.abs(deltaX) >= tolerance) {\n const currentDirX = deltaX > 0 ? 1 : -1;\n if (this._lastDirectionX !== null && currentDirX !== this._lastDirectionX) {\n this._fireEvent('ToggleX');\n }\n this._lastDirectionX = currentDirX;\n }\n\n // Only check ToggleY when Y movement exceeds tolerance\n if (Math.abs(deltaY) >= tolerance) {\n const currentDirY = deltaY > 0 ? 1 : -1;\n if (this._lastDirectionY !== null && currentDirY !== this._lastDirectionY) {\n this._fireEvent('ToggleY');\n }\n this._lastDirectionY = currentDirY;\n }\n }\n\n private _checkDragStart(): void {\n if (this._isDragging) return;\n\n // Use squared distance comparison to avoid sqrt (faster)\n const distanceSquared = this._deltaX * this._deltaX + this._deltaY * this._deltaY;\n\n if (distanceSquared > this._dragMinimumSquared) {\n this._isDragging = true;\n }\n }\n\n // ============================================\n // Stop Detection\n // ============================================\n\n private _scheduleStopCheck(): void {\n this._isMoving = true;\n this._lastActivityTime = performance.now();\n\n // Only create a new timeout if one isn't already running\n // This avoids constant setTimeout/clearTimeout churn on high-frequency events\n if (this._stopTimeout) {\n return;\n }\n\n const checkStop = () => {\n const elapsed = performance.now() - this._lastActivityTime;\n if (elapsed >= this._stopDelay) {\n // Enough time has passed since last activity\n this._stopTimeout = null;\n if (this._isMoving) {\n this._isMoving = false;\n // Reset direction tracking for next gesture session\n this._lastDirectionX = null;\n this._lastDirectionY = null;\n // Only fire onStop for wheel/scroll (continuous pointer gestures end with pointerup)\n if (!this._isPressed) {\n this._fireEvent('Stop');\n }\n }\n } else {\n // Still active, schedule another check for remaining time\n this._stopTimeout = setTimeout(checkStop, this._stopDelay - elapsed);\n }\n };\n\n this._stopTimeout = setTimeout(checkStop, this._stopDelay);\n }\n\n // ============================================\n // Action Execution (with RAF batching)\n // ============================================\n\n private _fireEvent(event: GestureEvent): void {\n const action = this._config.events[event];\n if (action) {\n this._pendingActions.set(action, event);\n this._scheduleRAF();\n }\n }\n\n private _scheduleRAF(): void {\n if (this._rafScheduled) return;\n this._rafScheduled = true;\n\n this._rafId = requestAnimationFrame(() => {\n this._rafScheduled = false;\n this._rafId = null;\n\n // Execute all pending actions\n for (const [action, event] of this._pendingActions) {\n this._executeAction(action, event);\n }\n this._pendingActions.clear();\n });\n }\n\n private _getStepForEvent(event?: string): number {\n if (typeof this._animationStep === 'number') {\n return this._animationStep;\n }\n if (event && this._animationStep[event] !== undefined) {\n return this._animationStep[event]!;\n }\n const values = Object.values(this._animationStep);\n return values.length > 0 ? values[0]! : 0.1;\n }\n\n private _executeAction(action: GestureAction, event?: GestureEvent): void {\n switch (action) {\n // Common actions delegated to the shared executeTimelineAction utility\n case 'play':\n case 'pause':\n case 'reverse':\n case 'restart':\n case 'reset': // executeTimelineAction order: pause() then progress(0) — canonical\n case 'complete':\n executeTimelineAction(action as TimelineAction, this._timeline);\n break;\n\n // Gesture-specific actions handled locally\n case 'toggle':\n if (this._timeline.isActive()) {\n this._timeline.pause();\n } else {\n this._timeline.play();\n }\n break;\n case 'kill':\n this._timeline.kill();\n break;\n case 'playReverse': {\n const progress = this._timeline.progress();\n if (progress >= 1) {\n this._timeline.reverse();\n } else if (progress <= 0) {\n this._timeline.play();\n } else if (!this._timeline.isActive()) {\n // Only toggle direction when paused/stopped — not while actively playing.\n // This prevents jittery play/reverse toggling on every frame during\n // continuous gesture events (scroll, wheel).\n if (this._timeline.reversed()) {\n this._timeline.play();\n } else {\n this._timeline.reverse();\n }\n }\n // If active and mid-animation, let the current direction continue.\n break;\n }\n case 'progressUp':\n this._interpolateProgress(this._getStepForEvent(event));\n break;\n case 'progressDown':\n this._interpolateProgress(-this._getStepForEvent(event));\n break;\n case 'playNext':\n this._playSequenceItem(1);\n break;\n case 'playPrevious':\n this._playSequenceItem(-1);\n break;\n }\n }\n\n // ============================================\n // Sequence Control (playNext/playPrevious)\n // ============================================\n\n /**\n * Play next or previous item in sequence (for each mode)\n * @param direction 1 for next, -1 for previous\n */\n private _playSequenceItem(direction: 1 | -1): void {\n // Only works in each mode with siblings\n if (!this._siblings || this._siblings.length === 0) {\n return;\n }\n\n const state = GestureTrigger._sequenceStates.get(this._siblings);\n if (!state) return;\n\n const count = this._siblings.length;\n const currentIndex = state.lastIndex;\n\n // Calculate next index with wrapping\n const nextIndex = (currentIndex + direction + count) % count;\n\n // Reverse/reset the current item\n const currentTimeline = this._siblings[currentIndex];\n if (currentTimeline && (currentTimeline.progress() as number) > 0) {\n currentTimeline.reverse();\n }\n\n // Play the next item\n const nextTimeline = this._siblings[nextIndex];\n if (nextTimeline) {\n nextTimeline.restart();\n }\n\n // Update shared state\n state.lastIndex = nextIndex;\n }\n\n // ============================================\n // Smooth Progress Interpolation\n // ============================================\n\n /**\n * Smoothly interpolate progress by the given delta.\n * Uses lerp for continuous scrolling (responsive) with natural ease-out when stopping.\n */\n private _interpolateProgress(delta: number): void {\n const timelineProgress = this._timeline.progress() as number;\n\n // Sync from timeline if changed externally (drift detection)\n // This handles cases where timeline.progress() was set by external code\n const drift = Math.abs(timelineProgress - this._currentProgress);\n if (drift > 0.01) {\n this._currentProgress = timelineProgress;\n this._targetProgress = timelineProgress;\n }\n\n // Also sync on first call (when loop not running)\n if (this._interpolationRafId === null) {\n this._currentProgress = timelineProgress;\n this._targetProgress = timelineProgress;\n }\n\n // Update target progress (clamped)\n this._targetProgress = Math.max(0, Math.min(1, this._targetProgress + delta));\n\n // If smooth is 0, apply instantly\n if (this._smooth === 0) {\n this._currentProgress = this._targetProgress;\n this._timeline.progress(this._currentProgress);\n return;\n }\n\n // Start interpolation loop if not running\n if (this._interpolationRafId === null) {\n this._startInterpolationLoop();\n }\n }\n\n private _startInterpolationLoop(): void {\n if (this._interpolationRafId !== null) return;\n\n // Lerp factor: higher = snappier response\n // smooth 0 = instant, smooth 0.5 = 0.15, smooth 1 = 0.08\n const lerpFactor = 0.08 + (1 - this._smooth) * 0.12;\n\n const loop = () => {\n const diff = this._targetProgress - this._currentProgress;\n\n // Stop when close enough\n if (Math.abs(diff) < 0.0001) {\n this._currentProgress = this._targetProgress;\n this._timeline.progress(this._currentProgress);\n this._interpolationRafId = null;\n return;\n }\n\n // Lerp toward target - naturally smooth during continuous input,\n // naturally eases out when input stops\n this._currentProgress += diff * lerpFactor;\n this._timeline.progress(this._currentProgress);\n\n // Continue loop\n this._interpolationRafId = requestAnimationFrame(loop);\n };\n\n this._interpolationRafId = requestAnimationFrame(loop);\n }\n\n // ============================================\n // Complete Callbacks (direction gesture end)\n // ============================================\n\n /**\n * Fire directional Complete callbacks for any directions that were activated\n * during this gesture, then clear the tracking set.\n */\n private _fireCompleteCallbacks(): void {\n if (this._activatedDirections.has('Up')) this._fireEvent('UpComplete');\n if (this._activatedDirections.has('Down')) this._fireEvent('DownComplete');\n if (this._activatedDirections.has('Left')) this._fireEvent('LeftComplete');\n if (this._activatedDirections.has('Right')) this._fireEvent('RightComplete');\n this._activatedDirections.clear();\n }\n\n // ============================================\n // State Management\n // ============================================\n\n private _resetGestureState(): void {\n this._isPressed = false;\n this._isDragging = false;\n this._deltaX = 0;\n this._deltaY = 0;\n this._lockedAxis = null;\n this._lastDirectionX = null;\n this._lastDirectionY = null;\n this._activatedDirections.clear();\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('gesture', GestureTrigger);\n",
|
|
25
|
-
"/**\n * CSSRenderer - Render CSS properties to DOM elements\n *\n * Handles:\n * - Transform properties (x, y, rotate, scale, etc.)\n * - Standard CSS properties (opacity, backgroundColor, etc.)\n * - Unit handling (px, %, deg, etc.)\n * - Auto-batching: queues during animation tick, writes immediately otherwise\n *\n * The render system automatically detects context:\n * - Inside Ticker tick → queue for batch flush at end of frame (performance)\n * - Outside tick → write immediately to DOM (responsiveness for seek/progress/etc.)\n */\n\nimport { setTransformValue, buildTransformString } from './TransformCache';\nimport { queueTransform, queueStyle } from './RenderBatch';\nimport { isTransformProp, isCSSVariable, camelToKebab } from '../utils/PropertyParser';\nimport { isInTick } from '../core/Ticker';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport type { ParsedFilterFunction } from '../utils/FilterParser';\n\n// Cached reference for hot-path performance\nlet _filter: typeof SDKRegistry.filter = null;\n\n/**\n * Set a CSS property value on an element\n * Automatically batches during animation tick, writes immediately otherwise\n */\nexport function setCSSProperty(\n element: Element,\n property: string,\n value: number,\n unit: string\n): void {\n const htmlElement = element as HTMLElement;\n\n // Check if element has style property (for test environments)\n if (!htmlElement.style) {\n return;\n }\n\n // Check if we're inside a tick - determines batch vs immediate write\n const inTick = isInTick();\n\n if (isTransformProp(property)) {\n // Update transform cache with value and unit (sets dirty flag)\n setTransformValue(element, property, value, unit);\n\n if (inTick) {\n // Inside tick: queue for batch flush at end of frame\n queueTransform(element);\n } else {\n // Outside tick: write immediately for responsive feedback\n htmlElement.style.transform = buildTransformString(element);\n }\n } else {\n // Regular CSS property or CSS variable\n const cssValue = unit ? `${value}${unit}` : `${value}`;\n\n if (inTick) {\n // Inside tick: queue for batch flush\n queueStyle(element, property, cssValue);\n } else {\n // Outside tick: write immediately\n try {\n if (isCSSVariable(property)) {\n // CSS variables use setProperty with raw name\n htmlElement.style.setProperty(property, cssValue);\n } else if (property in htmlElement.style) {\n // Dynamic property access requires index signature cast\n (htmlElement.style as CSSStyleDeclaration & Record<string, string>)[property] = cssValue;\n } else {\n htmlElement.style.setProperty(camelToKebab(property), cssValue);\n }\n } catch {\n // Silently fail for invalid properties\n }\n }\n }\n}\n\n/**\n * Set a CSS color property value on an element\n * Takes RGBA values directly for performance (avoids string formatting in hot path)\n */\nexport function setCSSColorProperty(\n element: Element,\n property: string,\n r: number,\n g: number,\n b: number,\n a: number\n): void {\n const htmlElement = element as HTMLElement;\n\n if (!htmlElement.style) {\n return;\n }\n\n // Format as rgba() - use rgb() if fully opaque for cleaner output\n const cssValue = a === 1\n ? `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`\n : `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;\n\n const inTick = isInTick();\n\n if (inTick) {\n queueStyle(element, property, cssValue);\n } else {\n try {\n if (isCSSVariable(property)) {\n // CSS variables use setProperty with raw name\n htmlElement.style.setProperty(property, cssValue);\n } else if (property in htmlElement.style) {\n (htmlElement.style as CSSStyleDeclaration & Record<string, string>)[property] = cssValue;\n } else {\n htmlElement.style.setProperty(camelToKebab(property), cssValue);\n }\n } catch {\n // Silently fail for invalid properties\n }\n }\n}\n\n/**\n * Get computed value of a CSS property\n */\nexport function getCSSProperty(element: Element, property: string): string {\n const computed = window.getComputedStyle(element);\n const cssProp = camelToKebab(property);\n return computed.getPropertyValue(cssProp);\n}\n\n/**\n * Set a CSS filter property value on an element\n * Interpolates between start and end filter arrays at given progress\n */\nexport function setCSSFilterProperty(\n element: Element,\n startFilters: ParsedFilterFunction[],\n endFilters: ParsedFilterFunction[],\n progress: number\n): void {\n const htmlElement = element as HTMLElement;\n\n if (!htmlElement.style) {\n return;\n }\n\n // Interpolate filter values using SDKRegistry (cached for performance)\n if (_filter || (_filter = SDKRegistry.filter)) {\n const interpolated = _filter.interpolateFilters(startFilters, endFilters, progress);\n const cssValue = _filter.filterToString(interpolated);\n\n const inTick = isInTick();\n\n if (inTick) {\n queueStyle(element, 'filter', cssValue);\n } else {\n htmlElement.style.filter = cssValue;\n }\n }\n}\n",
|
|
25
|
+
"/**\n * CSSRenderer - Render CSS properties to DOM elements\n *\n * Handles:\n * - Transform properties (x, y, rotate, scale, etc.)\n * - Standard CSS properties (opacity, backgroundColor, etc.)\n * - Unit handling (px, %, deg, etc.)\n * - Auto-batching: queues during animation tick, writes immediately otherwise\n *\n * The render system automatically detects context:\n * - Inside Ticker tick → queue for batch flush at end of frame (performance)\n * - Outside tick → write immediately to DOM (responsiveness for seek/progress/etc.)\n */\n\nimport { setTransformValue, buildTransformString } from './TransformCache';\nimport { queueTransform, queueStyle } from './RenderBatch';\nimport { isTransformProp, isCSSVariable, camelToKebab } from '../utils/PropertyParser';\nimport { isInTick } from '../core/Ticker';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport type { ParsedFilterFunction } from '../utils/FilterParser';\nimport type { ParsedClipPath } from '../utils/ClipPathParser';\n\n// Cached reference for hot-path performance\nlet _filter: typeof SDKRegistry.filter = null;\nlet _clipPath: typeof SDKRegistry.clipPath = null;\n\n/**\n * Set a CSS property value on an element\n * Automatically batches during animation tick, writes immediately otherwise\n */\nexport function setCSSProperty(\n element: Element,\n property: string,\n value: number,\n unit: string\n): void {\n const htmlElement = element as HTMLElement;\n\n // Check if element has style property (for test environments)\n if (!htmlElement.style) {\n return;\n }\n\n // Check if we're inside a tick - determines batch vs immediate write\n const inTick = isInTick();\n\n if (isTransformProp(property)) {\n // Update transform cache with value and unit (sets dirty flag)\n setTransformValue(element, property, value, unit);\n\n if (inTick) {\n // Inside tick: queue for batch flush at end of frame\n queueTransform(element);\n } else {\n // Outside tick: write immediately for responsive feedback\n htmlElement.style.transform = buildTransformString(element);\n }\n } else {\n // Regular CSS property or CSS variable\n const cssValue = unit ? `${value}${unit}` : `${value}`;\n\n if (inTick) {\n // Inside tick: queue for batch flush\n queueStyle(element, property, cssValue);\n } else {\n // Outside tick: write immediately\n try {\n if (isCSSVariable(property)) {\n // CSS variables use setProperty with raw name\n htmlElement.style.setProperty(property, cssValue);\n } else if (property in htmlElement.style) {\n // Dynamic property access requires index signature cast\n (htmlElement.style as CSSStyleDeclaration & Record<string, string>)[property] = cssValue;\n } else {\n htmlElement.style.setProperty(camelToKebab(property), cssValue);\n }\n } catch {\n // Silently fail for invalid properties\n }\n }\n }\n}\n\n/**\n * Set a CSS color property value on an element\n * Takes RGBA values directly for performance (avoids string formatting in hot path)\n */\nexport function setCSSColorProperty(\n element: Element,\n property: string,\n r: number,\n g: number,\n b: number,\n a: number\n): void {\n const htmlElement = element as HTMLElement;\n\n if (!htmlElement.style) {\n return;\n }\n\n // Format as rgba() - use rgb() if fully opaque for cleaner output\n const cssValue = a === 1\n ? `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`\n : `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;\n\n const inTick = isInTick();\n\n if (inTick) {\n queueStyle(element, property, cssValue);\n } else {\n try {\n if (isCSSVariable(property)) {\n // CSS variables use setProperty with raw name\n htmlElement.style.setProperty(property, cssValue);\n } else if (property in htmlElement.style) {\n (htmlElement.style as CSSStyleDeclaration & Record<string, string>)[property] = cssValue;\n } else {\n htmlElement.style.setProperty(camelToKebab(property), cssValue);\n }\n } catch {\n // Silently fail for invalid properties\n }\n }\n}\n\n/**\n * Get computed value of a CSS property\n */\nexport function getCSSProperty(element: Element, property: string): string {\n const computed = window.getComputedStyle(element);\n const cssProp = camelToKebab(property);\n return computed.getPropertyValue(cssProp);\n}\n\n/**\n * Set a CSS filter property value on an element\n * Interpolates between start and end filter arrays at given progress\n */\nexport function setCSSFilterProperty(\n element: Element,\n startFilters: ParsedFilterFunction[],\n endFilters: ParsedFilterFunction[],\n progress: number\n): void {\n const htmlElement = element as HTMLElement;\n\n if (!htmlElement.style) {\n return;\n }\n\n // Interpolate filter values using SDKRegistry (cached for performance)\n if (_filter || (_filter = SDKRegistry.filter)) {\n const interpolated = _filter.interpolateFilters(startFilters, endFilters, progress);\n const cssValue = _filter.filterToString(interpolated);\n\n const inTick = isInTick();\n\n if (inTick) {\n queueStyle(element, 'filter', cssValue);\n } else {\n htmlElement.style.filter = cssValue;\n }\n }\n}\n\n/**\n * Set a CSS clip-path property by interpolating two parsed shape functions.\n *\n * Mirrors `setCSSFilterProperty`: looks up the (optional) ClipPathParser\n * through SDKRegistry, interpolates the components at the given progress,\n * serializes the result to a CSS string, then either queues the write or\n * applies it immediately depending on tick context.\n */\nexport function setCSSClipPathProperty(\n element: Element,\n property: string,\n startClipPath: ParsedClipPath,\n endClipPath: ParsedClipPath,\n progress: number,\n): void {\n const htmlElement = element as HTMLElement;\n if (!htmlElement.style) return;\n\n if (_clipPath || (_clipPath = SDKRegistry.clipPath)) {\n const interpolated = _clipPath.interpolateClipPaths(startClipPath, endClipPath, progress);\n const cssValue = _clipPath.clipPathToString(interpolated);\n\n // Browsers expose clip-path under both `clipPath` (camel) and the kebab\n // form via setProperty. We always set both so older Safari (which still\n // requires `-webkit-clip-path`) continues to render.\n const inTick = isInTick();\n if (inTick) {\n queueStyle(element, 'clip-path', cssValue);\n queueStyle(element, '-webkit-clip-path', cssValue);\n } else {\n htmlElement.style.setProperty('clip-path', cssValue);\n htmlElement.style.setProperty('-webkit-clip-path', cssValue);\n }\n }\n}\n",
|
|
26
26
|
"/**\n * PathRenderer - Encapsulates motion path rendering logic\n * \n * Extracted from PropTween to improve code organization.\n * PropTween still holds the data (for pooling), but rendering is delegated here.\n */\n\nimport { setTransformValue, buildTransformString } from './TransformCache';\nimport { queueTransform } from './RenderBatch';\nimport { isInTick } from '../core/Ticker';\nimport type { PathPoint } from '../utils/PathParser';\n\ninterface PathRenderState {\n pathLUT: PathPoint[];\n alignOffset: { x: number; y: number };\n pathOffset: { x: number; y: number };\n pathRotate: boolean;\n}\n\n/**\n * Render path position at given progress\n */\nexport function renderPath(\n target: Element,\n progress: number,\n state: PathRenderState\n): void {\n const { pathLUT, alignOffset, pathOffset, pathRotate } = state;\n\n if (!pathLUT || pathLUT.length === 0) return;\n\n // Get point from LUT with linear interpolation between samples\n const lutIndex = progress * (pathLUT.length - 1);\n const lowIndex = Math.floor(lutIndex);\n const highIndex = Math.min(lowIndex + 1, pathLUT.length - 1);\n const t = lutIndex - lowIndex;\n\n const p1 = pathLUT[lowIndex];\n const p2 = pathLUT[highIndex];\n\n // Linearly interpolate between the two nearest samples\n const pathX = p1.x + (p2.x - p1.x) * t;\n const pathY = p1.y + (p2.y - p1.y) * t;\n\n // Final position = pathOffset + pathPosition - alignOffset\n // pathOffset positions the path relative to align element\n // alignOffset adjusts for element's origin point\n const x = (pathOffset?.x ?? 0) + pathX - (alignOffset?.x ?? 0);\n const y = (pathOffset?.y ?? 0) + pathY - (alignOffset?.y ?? 0);\n\n // Set transform values\n setTransformValue(target, 'x', x, 'px');\n setTransformValue(target, 'y', y, 'px');\n\n if (pathRotate) {\n const angle = interpolateAngle(p1.angle, p2.angle, t);\n setTransformValue(target, 'rotate', angle, 'deg');\n }\n\n // Build and apply transform string\n if (isInTick()) {\n queueTransform(target);\n } else {\n (target as HTMLElement).style.transform = buildTransformString(target);\n }\n}\n\n/**\n * Interpolate angle with proper wraparound handling\n * Always takes the shorter rotation path (max 180° difference)\n */\nfunction interpolateAngle(angle1: number, angle2: number, t: number): number {\n let angleDiff = angle2 - angle1;\n\n // Normalize to shortest rotation path (±180°)\n if (angleDiff > 180) angleDiff -= 360;\n if (angleDiff < -180) angleDiff += 360;\n\n return angle1 + angleDiff * t;\n}\n",
|
|
27
|
-
"/**\n * PropTween - Individual property interpolation unit\n *\n * Linked list node for animating a single property.\n * Pooled for performance.\n * Supports scalar values, color (RGBA), and filter interpolation.\n */\n\nimport { setCSSProperty, setCSSColorProperty, setCSSFilterProperty } from '../render/CSSRenderer';\nimport { clearTransformCache } from '../render/TransformCache';\nimport type { AnimationTarget } from '../types';\nimport type { ParsedFilterFunction } from '../utils/FilterParser';\nimport type { ParsedDrawSVG } from '../utils/DrawSVGParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { getPointAtProgress, type PathPoint } from '../utils/PathParser';\nimport { renderPath } from '../render/PathRenderer';\n\n// Cached reference for hot-path performance\nlet _drawSVG: typeof SDKRegistry.drawSVG = null;\n\n// Minimum samples for path LUT (ensures smooth animation)\nconst MIN_PATH_SAMPLES = 100;\n// Maximum samples to prevent memory issues with very long paths\nconst MAX_PATH_SAMPLES = 500;\n// Sample density: approximately 1 sample per 2 pixels of path length\nconst PATH_SAMPLE_DENSITY = 0.5;\n\n// LUT array pool for memory efficiency\nconst lutPool: PathPoint[][] = [];\nconst MAX_POOL_SIZE = 50;\n\nfunction acquireLUT(size: number): PathPoint[] {\n // Try to find a pooled array of similar size\n for (let i = 0; i < lutPool.length; i++) {\n if (lutPool[i].length >= size) {\n const lut = lutPool.splice(i, 1)[0];\n // IMPORTANT: Always set length to requested size to avoid reading stale data\n lut.length = size;\n return lut;\n }\n }\n return new Array(size);\n}\n\nfunction releaseLUT(lut: PathPoint[]): void {\n if (lutPool.length < MAX_POOL_SIZE) {\n lutPool.push(lut);\n }\n}\n\ntype PropTweenValueType = 'scalar' | 'color' | 'filter' | 'drawSVG' | 'path';\n\nexport class PropTween {\n target: AnimationTarget | null = null;\n property: string = '';\n startValue: number = 0;\n endValue: number = 0;\n change: number = 0;\n unit: string = '';\n next: PropTween | null = null;\n\n // ─────────────────────────────────────────────────────────────\n // Type discriminator - determines which property group is active\n // ─────────────────────────────────────────────────────────────\n valueType: PropTweenValueType = 'scalar';\n\n // ─────────────────────────────────────────────────────────────\n // COLOR properties (valueType === 'color')\n // ─────────────────────────────────────────────────────────────\n startColor: Float32Array | null = null;\n endColor: Float32Array | null = null;\n changeColor: Float32Array = new Float32Array(4);\n\n // ─────────────────────────────────────────────────────────────\n // FILTER properties (valueType === 'filter')\n // ─────────────────────────────────────────────────────────────\n startFilters: ParsedFilterFunction[] | null = null;\n endFilters: ParsedFilterFunction[] | null = null;\n\n // ─────────────────────────────────────────────────────────────\n // DRAWSVG properties (valueType === 'drawSVG')\n // ─────────────────────────────────────────────────────────────\n startDraw: ParsedDrawSVG | null = null;\n endDraw: ParsedDrawSVG | null = null;\n pathLength: number = 0; // SVG stroke length for dasharray calc\n\n // ─────────────────────────────────────────────────────────────\n // MOTION PATH properties (valueType === 'path')\n // Pre-samples path into LUT for O(1) rendering per frame\n // ─────────────────────────────────────────────────────────────\n pathData: string | null = null; // Raw SVG path d attribute\n motionPathLength: number = 0; // Total path length in px\n startProgress: number = 0; // Start position on path (0-1)\n endProgress: number = 0; // End position on path (0-1)\n pathRotate: boolean = false; // Auto-rotate along tangent\n alignOffset: { x: number; y: number } | null = null; // Element origin offset\n pathOffset: { x: number; y: number } | null = null; // Align element offset\n pathLUT: PathPoint[] | null = null; // Pre-sampled lookup table\n\n // Cached target type check (avoids instanceof in hot path)\n private _isElement: boolean = false;\n\n /**\n * Initialize the PropTween with scalar values\n */\n init(\n target: AnimationTarget,\n property: string,\n startValue: number,\n endValue: number,\n unit: string = ''\n ): this {\n this.target = target;\n this.property = property;\n this.startValue = startValue;\n this.endValue = endValue;\n this.change = endValue - startValue;\n this.unit = unit;\n this.valueType = 'scalar';\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with color values\n */\n initColor(\n target: AnimationTarget,\n property: string,\n startColor: Float32Array,\n endColor: Float32Array\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'color';\n this.startColor = startColor;\n this.endColor = endColor;\n this.changeColor[0] = endColor[0] - startColor[0];\n this.changeColor[1] = endColor[1] - startColor[1];\n this.changeColor[2] = endColor[2] - startColor[2];\n this.changeColor[3] = endColor[3] - startColor[3];\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with filter values\n */\n initFilter(\n target: AnimationTarget,\n property: string,\n startFilters: ParsedFilterFunction[],\n endFilters: ParsedFilterFunction[]\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'filter';\n this.startFilters = startFilters;\n this.endFilters = endFilters;\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with drawSVG values\n */\n initDrawSVG(\n target: AnimationTarget,\n property: string,\n startDraw: ParsedDrawSVG,\n endDraw: ParsedDrawSVG,\n pathLength: number\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'drawSVG';\n this.startDraw = startDraw;\n this.endDraw = endDraw;\n this.pathLength = pathLength;\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with path (motion path) values\n * Pre-samples the path into a lookup table for O(1) rendering\n */\n initPath(\n target: AnimationTarget,\n property: string,\n pathData: string,\n pathLength: number,\n startProgress: number,\n endProgress: number,\n rotate: boolean,\n alignOffset: { x: number; y: number },\n pathOffset: { x: number; y: number }\n ): this {\n // Clear transform cache AND inline style to ensure clean state for this animation\n // This prevents stale rotation values from previous path animations being\n // read back during hydration when the cache is recreated\n if (target instanceof Element) {\n clearTransformCache(target, true);\n }\n\n this.target = target;\n this.property = property;\n this.valueType = 'path';\n this.pathData = pathData;\n this.motionPathLength = pathLength;\n this.startProgress = startProgress;\n this.endProgress = endProgress;\n this.pathRotate = rotate;\n this.alignOffset = alignOffset;\n this.pathOffset = pathOffset;\n this._isElement = target instanceof Element;\n\n // Pre-sample path into lookup table for O(1) rendering\n // Calculate number of samples based on path length and animation range\n const progressRange = Math.abs(endProgress - startProgress);\n const effectiveLength = pathLength * progressRange;\n const sampleCount = Math.min(\n MAX_PATH_SAMPLES,\n Math.max(MIN_PATH_SAMPLES, Math.ceil(effectiveLength * PATH_SAMPLE_DENSITY))\n );\n\n // Acquire LUT from pool\n this.pathLUT = acquireLUT(sampleCount + 1);\n for (let i = 0; i <= sampleCount; i++) {\n const t = i / sampleCount;\n const pathProgress = startProgress + (endProgress - startProgress) * t;\n this.pathLUT[i] = getPointAtProgress(pathData, pathProgress, rotate);\n }\n\n return this;\n }\n\n /**\n * Render the property at given progress (0-1)\n */\n render(progress: number): void {\n if (!this.target) return;\n\n if (this.valueType === 'color' && this.startColor) {\n // Interpolate RGBA channels\n const r = this.startColor[0] + this.changeColor[0] * progress;\n const g = this.startColor[1] + this.changeColor[1] * progress;\n const b = this.startColor[2] + this.changeColor[2] * progress;\n const a = this.startColor[3] + this.changeColor[3] * progress;\n\n if (this._isElement) {\n setCSSColorProperty(this.target as Element, this.property, r, g, b, a);\n }\n } else if (this.valueType === 'filter' && this.startFilters && this.endFilters) {\n // Interpolate filter functions\n if (this._isElement) {\n setCSSFilterProperty(this.target as Element, this.startFilters, this.endFilters, progress);\n }\n } else if (this.valueType === 'drawSVG' && this.startDraw && this.endDraw) {\n // Interpolate drawSVG start and end positions\n if (this._isElement && this.pathLength > 0) {\n const currentStart = this.startDraw.start + (this.endDraw.start - this.startDraw.start) * progress;\n const currentEnd = this.startDraw.end + (this.endDraw.end - this.startDraw.end) * progress;\n if (_drawSVG || (_drawSVG = SDKRegistry.drawSVG)) {\n _drawSVG.applyDrawSVG(this.target as Element, currentStart, currentEnd, this.pathLength);\n }\n }\n } else if (this.valueType === 'path' && this.pathLUT && this.pathLUT.length > 0) {\n // Delegate to PathRenderer for cleaner code organization\n if (this._isElement) {\n renderPath(this.target as Element, progress, {\n pathLUT: this.pathLUT,\n alignOffset: this.alignOffset ?? { x: 0, y: 0 },\n pathOffset: this.pathOffset ?? { x: 0, y: 0 },\n pathRotate: this.pathRotate,\n });\n }\n } else {\n // Scalar interpolation\n const value = this.startValue + this.change * progress;\n\n if (this._isElement) {\n setCSSProperty(this.target as Element, this.property, value, this.unit);\n } else {\n // Plain object target (for testing)\n (this.target as Record<string, number>)[this.property] = value;\n }\n }\n }\n\n /**\n * Reset for object pooling\n */\n reset(): void {\n this.target = null;\n this.property = '';\n this.startValue = 0;\n this.endValue = 0;\n this.change = 0;\n this.unit = '';\n this.next = null;\n this.valueType = 'scalar';\n this.startColor = null;\n this.endColor = null;\n this.startFilters = null;\n this.endFilters = null;\n this.startDraw = null;\n this.endDraw = null;\n this.pathLength = 0;\n this.pathData = null;\n this.motionPathLength = 0;\n this.startProgress = 0;\n this.endProgress = 0;\n this.pathRotate = false;\n this.alignOffset = null;\n this.pathOffset = null;\n // Release LUT back to pool\n if (this.pathLUT) {\n releaseLUT(this.pathLUT);\n }\n this.pathLUT = null;\n this._isElement = false;\n }\n}\n",
|
|
27
|
+
"/**\n * PropTween - Individual property interpolation unit\n *\n * Linked list node for animating a single property.\n * Pooled for performance.\n * Supports scalar values, color (RGBA), and filter interpolation.\n */\n\nimport { setCSSProperty, setCSSColorProperty, setCSSFilterProperty, setCSSClipPathProperty } from '../render/CSSRenderer';\nimport { clearTransformCache } from '../render/TransformCache';\nimport type { AnimationTarget } from '../types';\nimport type { ParsedFilterFunction } from '../utils/FilterParser';\nimport type { ParsedDrawSVG } from '../utils/DrawSVGParser';\nimport type { ParsedClipPath } from '../utils/ClipPathParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { getPointAtProgress, type PathPoint } from '../utils/PathParser';\nimport { renderPath } from '../render/PathRenderer';\n\n// Cached reference for hot-path performance\nlet _drawSVG: typeof SDKRegistry.drawSVG = null;\n\n// Minimum samples for path LUT (ensures smooth animation)\nconst MIN_PATH_SAMPLES = 100;\n// Maximum samples to prevent memory issues with very long paths\nconst MAX_PATH_SAMPLES = 500;\n// Sample density: approximately 1 sample per 2 pixels of path length\nconst PATH_SAMPLE_DENSITY = 0.5;\n\n// LUT array pool for memory efficiency\nconst lutPool: PathPoint[][] = [];\nconst MAX_POOL_SIZE = 50;\n\nfunction acquireLUT(size: number): PathPoint[] {\n // Try to find a pooled array of similar size\n for (let i = 0; i < lutPool.length; i++) {\n if (lutPool[i].length >= size) {\n const lut = lutPool.splice(i, 1)[0];\n // IMPORTANT: Always set length to requested size to avoid reading stale data\n lut.length = size;\n return lut;\n }\n }\n return new Array(size);\n}\n\nfunction releaseLUT(lut: PathPoint[]): void {\n if (lutPool.length < MAX_POOL_SIZE) {\n lutPool.push(lut);\n }\n}\n\ntype PropTweenValueType = 'scalar' | 'color' | 'filter' | 'drawSVG' | 'path' | 'clipPath';\n\nexport class PropTween {\n target: AnimationTarget | null = null;\n property: string = '';\n startValue: number = 0;\n endValue: number = 0;\n change: number = 0;\n unit: string = '';\n next: PropTween | null = null;\n\n // ─────────────────────────────────────────────────────────────\n // Type discriminator - determines which property group is active\n // ─────────────────────────────────────────────────────────────\n valueType: PropTweenValueType = 'scalar';\n\n // ─────────────────────────────────────────────────────────────\n // COLOR properties (valueType === 'color')\n // ─────────────────────────────────────────────────────────────\n startColor: Float32Array | null = null;\n endColor: Float32Array | null = null;\n changeColor: Float32Array = new Float32Array(4);\n\n // ─────────────────────────────────────────────────────────────\n // FILTER properties (valueType === 'filter')\n // ─────────────────────────────────────────────────────────────\n startFilters: ParsedFilterFunction[] | null = null;\n endFilters: ParsedFilterFunction[] | null = null;\n\n // ─────────────────────────────────────────────────────────────\n // DRAWSVG properties (valueType === 'drawSVG')\n // ─────────────────────────────────────────────────────────────\n startDraw: ParsedDrawSVG | null = null;\n endDraw: ParsedDrawSVG | null = null;\n pathLength: number = 0; // SVG stroke length for dasharray calc\n\n // ─────────────────────────────────────────────────────────────\n // CLIP-PATH properties (valueType === 'clipPath')\n // ─────────────────────────────────────────────────────────────\n startClipPath: ParsedClipPath | null = null;\n endClipPath: ParsedClipPath | null = null;\n\n // ─────────────────────────────────────────────────────────────\n // MOTION PATH properties (valueType === 'path')\n // Pre-samples path into LUT for O(1) rendering per frame\n // ─────────────────────────────────────────────────────────────\n pathData: string | null = null; // Raw SVG path d attribute\n motionPathLength: number = 0; // Total path length in px\n startProgress: number = 0; // Start position on path (0-1)\n endProgress: number = 0; // End position on path (0-1)\n pathRotate: boolean = false; // Auto-rotate along tangent\n alignOffset: { x: number; y: number } | null = null; // Element origin offset\n pathOffset: { x: number; y: number } | null = null; // Align element offset\n pathLUT: PathPoint[] | null = null; // Pre-sampled lookup table\n\n // Cached target type check (avoids instanceof in hot path)\n private _isElement: boolean = false;\n\n /**\n * Initialize the PropTween with scalar values\n */\n init(\n target: AnimationTarget,\n property: string,\n startValue: number,\n endValue: number,\n unit: string = ''\n ): this {\n this.target = target;\n this.property = property;\n this.startValue = startValue;\n this.endValue = endValue;\n this.change = endValue - startValue;\n this.unit = unit;\n this.valueType = 'scalar';\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with color values\n */\n initColor(\n target: AnimationTarget,\n property: string,\n startColor: Float32Array,\n endColor: Float32Array\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'color';\n this.startColor = startColor;\n this.endColor = endColor;\n this.changeColor[0] = endColor[0] - startColor[0];\n this.changeColor[1] = endColor[1] - startColor[1];\n this.changeColor[2] = endColor[2] - startColor[2];\n this.changeColor[3] = endColor[3] - startColor[3];\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with filter values\n */\n initFilter(\n target: AnimationTarget,\n property: string,\n startFilters: ParsedFilterFunction[],\n endFilters: ParsedFilterFunction[]\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'filter';\n this.startFilters = startFilters;\n this.endFilters = endFilters;\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with clip-path values\n */\n initClipPath(\n target: AnimationTarget,\n property: string,\n startClipPath: ParsedClipPath,\n endClipPath: ParsedClipPath\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'clipPath';\n this.startClipPath = startClipPath;\n this.endClipPath = endClipPath;\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with drawSVG values\n */\n initDrawSVG(\n target: AnimationTarget,\n property: string,\n startDraw: ParsedDrawSVG,\n endDraw: ParsedDrawSVG,\n pathLength: number\n ): this {\n this.target = target;\n this.property = property;\n this.valueType = 'drawSVG';\n this.startDraw = startDraw;\n this.endDraw = endDraw;\n this.pathLength = pathLength;\n this._isElement = target instanceof Element;\n return this;\n }\n\n /**\n * Initialize the PropTween with path (motion path) values\n * Pre-samples the path into a lookup table for O(1) rendering\n */\n initPath(\n target: AnimationTarget,\n property: string,\n pathData: string,\n pathLength: number,\n startProgress: number,\n endProgress: number,\n rotate: boolean,\n alignOffset: { x: number; y: number },\n pathOffset: { x: number; y: number }\n ): this {\n // Clear transform cache AND inline style to ensure clean state for this animation\n // This prevents stale rotation values from previous path animations being\n // read back during hydration when the cache is recreated\n if (target instanceof Element) {\n clearTransformCache(target, true);\n }\n\n this.target = target;\n this.property = property;\n this.valueType = 'path';\n this.pathData = pathData;\n this.motionPathLength = pathLength;\n this.startProgress = startProgress;\n this.endProgress = endProgress;\n this.pathRotate = rotate;\n this.alignOffset = alignOffset;\n this.pathOffset = pathOffset;\n this._isElement = target instanceof Element;\n\n // Pre-sample path into lookup table for O(1) rendering\n // Calculate number of samples based on path length and animation range\n const progressRange = Math.abs(endProgress - startProgress);\n const effectiveLength = pathLength * progressRange;\n const sampleCount = Math.min(\n MAX_PATH_SAMPLES,\n Math.max(MIN_PATH_SAMPLES, Math.ceil(effectiveLength * PATH_SAMPLE_DENSITY))\n );\n\n // Acquire LUT from pool\n this.pathLUT = acquireLUT(sampleCount + 1);\n for (let i = 0; i <= sampleCount; i++) {\n const t = i / sampleCount;\n const pathProgress = startProgress + (endProgress - startProgress) * t;\n this.pathLUT[i] = getPointAtProgress(pathData, pathProgress, rotate);\n }\n\n return this;\n }\n\n /**\n * Render the property at given progress (0-1)\n */\n render(progress: number): void {\n if (!this.target) return;\n\n if (this.valueType === 'color' && this.startColor) {\n // Interpolate RGBA channels\n const r = this.startColor[0] + this.changeColor[0] * progress;\n const g = this.startColor[1] + this.changeColor[1] * progress;\n const b = this.startColor[2] + this.changeColor[2] * progress;\n const a = this.startColor[3] + this.changeColor[3] * progress;\n\n if (this._isElement) {\n setCSSColorProperty(this.target as Element, this.property, r, g, b, a);\n }\n } else if (this.valueType === 'filter' && this.startFilters && this.endFilters) {\n // Interpolate filter functions\n if (this._isElement) {\n setCSSFilterProperty(this.target as Element, this.startFilters, this.endFilters, progress);\n }\n } else if (this.valueType === 'clipPath' && this.startClipPath && this.endClipPath) {\n // Interpolate clip-path shape functions\n if (this._isElement) {\n setCSSClipPathProperty(this.target as Element, this.property, this.startClipPath, this.endClipPath, progress);\n }\n } else if (this.valueType === 'drawSVG' && this.startDraw && this.endDraw) {\n // Interpolate drawSVG start and end positions\n if (this._isElement && this.pathLength > 0) {\n const currentStart = this.startDraw.start + (this.endDraw.start - this.startDraw.start) * progress;\n const currentEnd = this.startDraw.end + (this.endDraw.end - this.startDraw.end) * progress;\n if (_drawSVG || (_drawSVG = SDKRegistry.drawSVG)) {\n _drawSVG.applyDrawSVG(this.target as Element, currentStart, currentEnd, this.pathLength);\n }\n }\n } else if (this.valueType === 'path' && this.pathLUT && this.pathLUT.length > 0) {\n // Delegate to PathRenderer for cleaner code organization\n if (this._isElement) {\n renderPath(this.target as Element, progress, {\n pathLUT: this.pathLUT,\n alignOffset: this.alignOffset ?? { x: 0, y: 0 },\n pathOffset: this.pathOffset ?? { x: 0, y: 0 },\n pathRotate: this.pathRotate,\n });\n }\n } else {\n // Scalar interpolation\n const value = this.startValue + this.change * progress;\n\n if (this._isElement) {\n setCSSProperty(this.target as Element, this.property, value, this.unit);\n } else {\n // Plain object target (for testing)\n (this.target as Record<string, number>)[this.property] = value;\n }\n }\n }\n\n /**\n * Reset for object pooling\n */\n reset(): void {\n this.target = null;\n this.property = '';\n this.startValue = 0;\n this.endValue = 0;\n this.change = 0;\n this.unit = '';\n this.next = null;\n this.valueType = 'scalar';\n this.startColor = null;\n this.endColor = null;\n this.startFilters = null;\n this.endFilters = null;\n this.startDraw = null;\n this.endDraw = null;\n this.pathLength = 0;\n this.startClipPath = null;\n this.endClipPath = null;\n this.pathData = null;\n this.motionPathLength = 0;\n this.startProgress = 0;\n this.endProgress = 0;\n this.pathRotate = false;\n this.alignOffset = null;\n this.pathOffset = null;\n // Release LUT back to pool\n if (this.pathLUT) {\n releaseLUT(this.pathLUT);\n }\n this.pathLUT = null;\n this._isElement = false;\n }\n}\n",
|
|
28
28
|
"/**\n * ObjectPool<T> - Generic object pool for reusable instances\n *\n * Reuses objects that implement a `reset()` method to minimize\n * garbage collection overhead.\n */\n\n/** Constraint: pooled objects must be resettable */\nexport interface Poolable {\n reset(): void;\n}\n\nexport class ObjectPool<T extends Poolable> {\n private pool: T[] = [];\n private totalCreated: number = 0;\n private readonly factory: () => T;\n private readonly maxSize: number | undefined;\n\n constructor(factory: () => T, maxSize?: number) {\n this.factory = factory;\n this.maxSize = maxSize;\n }\n\n /**\n * Acquire an object from the pool or create a new one\n */\n acquire(): T {\n if (this.pool.length > 0) {\n return this.pool.pop()!;\n }\n this.totalCreated++;\n return this.factory();\n }\n\n /**\n * Release an object back to the pool\n */\n release(obj: T): void {\n obj.reset();\n if (this.maxSize === undefined || this.pool.length < this.maxSize) {\n this.pool.push(obj);\n }\n // else: pool is at capacity — let GC claim it\n }\n\n /**\n * Get count of pooled objects\n */\n getPoolSize(): number {\n return this.pool.length;\n }\n\n /**\n * Get total objects created (for stats)\n */\n getTotalCreated(): number {\n return this.totalCreated;\n }\n\n /**\n * Clear the pool (for testing/debugging)\n */\n clear(): void {\n this.pool = [];\n }\n}\n",
|
|
29
29
|
"/**\n * PropTweenPool - Singleton object pool for PropTween instances\n *\n * Reuses PropTween objects to minimize garbage collection overhead\n */\n\nimport { PropTween } from '../core/PropTween';\nimport { ObjectPool } from './ObjectPool';\n\nconst pool = new ObjectPool<PropTween>(() => new PropTween(), 500);\n\nexport const PropTweenPool = {\n acquire: () => pool.acquire(),\n release: (propTween: PropTween) => pool.release(propTween),\n getPoolSize: () => pool.getPoolSize(),\n getTotalCreated: () => pool.getTotalCreated(),\n clear: () => pool.clear(),\n};\n\nexport type PropTweenPool = typeof PropTweenPool;\n",
|
|
30
|
-
"/**\n * AnimationBuilder - Config-based animation creator\n * \n * Creates animations from configuration objects.\n * Used internally by Motion() function.\n */\n\nimport type { AnimationVars, AnimationTarget, TargetInput, StaggerVars, RepeatConfig, SplitType, AnimationConfig, FitConfig } from '../types';\nimport type { InternalAnimationConfig } from './Animation';\n\n/**\n * Internal builder configuration for cloning (not exported from SDK)\n */\ninterface BuilderConfig {\n fromVars?: AnimationVars;\n toVars?: AnimationVars;\n duration: number;\n delay: number;\n ease: string;\n axis?: 'x' | 'y';\n fit?: FitConfig;\n split?: SplitType;\n mask?: boolean;\n stagger?: number | StaggerVars;\n repeat?: number;\n repeatDelay?: number;\n yoyo?: boolean;\n onStart?: () => void;\n onUpdate?: (progress: number) => void;\n onComplete?: () => void;\n onRepeat?: (repeatCount: number) => void;\n onReverseComplete?: () => void;\n}\nimport { resolveTargets } from '../utils/TargetResolver';\nimport { Animation } from './Animation';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { clearTransformCache } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { isColorProp, isFilterProp, getCurrentValue, isCSSVariable, parseValue, getDefaultUnit } from '../utils/PropertyParser';\n\n/**\n * Default \"natural\" values for animatable properties.\n * Used when from has properties that to doesn't specify.\n */\nconst PROPERTY_DEFAULTS: Record<string, number> = {\n x: 0, y: 0, z: 0,\n rotate: 0, rotateX: 0, rotateY: 0, rotateZ: 0,\n scale: 1, scaleX: 1, scaleY: 1, scaleZ: 1,\n skewX: 0, skewY: 0,\n opacity: 1,\n};\n\nexport class AnimationBuilder {\n /**\n * Callback to create animations via Engine (set by Engine to avoid circular dependency)\n * Engine→Timeline→AnimationBuilder→Engine cycle is broken by this callback.\n */\n private static _createAnimationCallback: ((\n targets: AnimationTarget[],\n vars: AnimationVars,\n duration: number,\n delay: number,\n ease: string,\n isFrom: boolean,\n fromVars?: AnimationVars,\n config?: InternalAnimationConfig\n ) => Animation | Animation[]) | null = null;\n\n static setCreateAnimationCallback(callback: typeof AnimationBuilder._createAnimationCallback): void {\n AnimationBuilder._createAnimationCallback = callback;\n }\n\n private _targets: AnimationTarget[];\n private _originalTargets: AnimationTarget[];\n private _fromVars?: AnimationVars;\n private _toVars?: AnimationVars;\n private _duration: number = 0.5;\n private _delay: number = 0;\n private _ease: string = 'power1.out';\n private _animation: Animation | null = null;\n private _animations: Animation[] = [];\n private _built: boolean = false;\n // Configuration\n private _splitType?: SplitType;\n private _mask?: boolean;\n private _repeat?: number;\n private _repeatDelay?: number;\n private _yoyo?: boolean;\n private _stagger?: number | StaggerVars;\n private _axis?: 'x' | 'y';\n private _fitConfig?: FitConfig;\n // Lifecycle callbacks\n private _onStart?: () => void;\n private _onUpdate?: (progress: number) => void;\n private _onComplete?: () => void;\n private _onRepeat?: (repeatCount: number) => void;\n private _onReverseComplete?: () => void;\n\n // When true, Engine skips the immediate render(0) for FROM animations.\n // Set by Timeline._addBuilder so it can capture initial values first.\n private _skipInitialFromRender = false;\n\n // For pooling/rebuild\n private _plainObjectInitialValues: Map<AnimationTarget, Record<string, number>> = new Map();\n private _expectedAnimationIds: number[] = [];\n private _propsToAnimate: string[] = [];\n\n /**\n * Create an animation builder\n * @param targets - Elements or objects to animate\n * @param config - Animation configuration\n */\n constructor(targets: TargetInput, config?: AnimationConfig) {\n this._targets = resolveTargets(targets);\n this._originalTargets = this._targets;\n\n // Silently accept empty targets — matches GSAP's nullTargetWarn:false behavior.\n // The animation still participates in timelines (duration, callbacks) as a no-op.\n\n if (config) {\n this._applyAnimationConfig(config);\n }\n }\n\n /**\n * Apply animation config\n */\n private _applyAnimationConfig(config: AnimationConfig): void {\n if (config.from) this._fromVars = config.from;\n if (config.to) this._toVars = config.to;\n\n if (config.duration !== undefined) {\n if (typeof config.duration !== 'number' || !isFinite(config.duration) || config.duration < 0) {\n console.warn(`[Motion] Invalid duration: ${config.duration}. Must be a finite number >= 0. Using default.`);\n } else {\n this._duration = config.duration;\n }\n }\n\n if (config.delay !== undefined) {\n if (typeof config.delay !== 'number' || isNaN(config.delay)) {\n console.warn(`[Motion] Invalid delay: ${config.delay}. Must be a number. Using default 0.`);\n } else if (!isFinite(config.delay)) {\n console.warn(`[Motion] Invalid delay: ${config.delay}. Infinity is not a valid delay. Using default 0.`);\n } else {\n this._delay = config.delay;\n }\n }\n\n if (config.ease) this._ease = config.ease;\n if (config.split) this._splitType = config.split;\n if (config.mask) this._mask = config.mask;\n if (config.stagger !== undefined) this._stagger = config.stagger;\n if (config.axis) this._axis = config.axis;\n if (config.fit) this._fitConfig = config.fit;\n\n if (config.repeat) {\n if (typeof config.repeat === 'number') {\n this._repeat = config.repeat;\n } else {\n this._repeat = config.repeat.times;\n this._repeatDelay = config.repeat.delay;\n this._yoyo = config.repeat.yoyo;\n }\n }\n\n if (config.onStart) this._onStart = config.onStart;\n if (config.onUpdate) this._onUpdate = config.onUpdate;\n if (config.onComplete) this._onComplete = config.onComplete;\n if (config.onRepeat) this._onRepeat = config.onRepeat;\n if (config.onReverseComplete) this._onReverseComplete = config.onReverseComplete;\n }\n\n /**\n * Capture initial values from plain object targets\n */\n private _capturePlainObjectValues(props: string[]): void {\n for (const target of this._targets) {\n if (target instanceof Element) continue;\n const obj = target as Record<string, number>;\n const values: Record<string, number> = {};\n for (const prop of props) {\n if (prop in obj && typeof obj[prop] === 'number') {\n values[prop] = obj[prop];\n }\n }\n if (Object.keys(values).length > 0) {\n this._plainObjectInitialValues.set(target, values);\n }\n }\n }\n\n /**\n * Restore plain object targets to initial values\n */\n private _restorePlainObjectValues(): void {\n for (const [target, values] of this._plainObjectInitialValues) {\n const obj = target as Record<string, number>;\n for (const [prop, value] of Object.entries(values)) {\n obj[prop] = value;\n }\n }\n }\n\n private _ensureBuilt(): void {\n if (!this._built) this._build();\n }\n\n /**\n * Check if animations have been invalidated (pooled or reused)\n */\n private _isInvalidated(): boolean {\n if (this._animations.length === 0) return false;\n return this._animations.some((anim, i) => {\n const currentId = anim.getId();\n const expectedId = this._expectedAnimationIds[i];\n return currentId === 0 || currentId !== expectedId;\n });\n }\n\n /**\n * Rebuild if animations were pooled\n */\n rebuildIfPooled(): void {\n if (this._isInvalidated()) {\n this._restorePlainObjectValues();\n for (const target of this._targets) {\n if (target instanceof Element) {\n clearTransformCache(target);\n SDKRegistry.styleReset?.clearAnimationStylesForProps?.(target, this._propsToAnimate);\n }\n }\n this._resetBuildState();\n this._ensureBuilt();\n }\n }\n\n private _build(): void {\n if (this._built) return;\n if (this._targets.length === 0) return;\n // Allow spacer entries: if duration > 0 but no from/to/fit, still create\n // an animation so it occupies time in a timeline (e.g. a hold/pause).\n if (!this._fromVars && !this._toVars && !this._fitConfig && this._duration <= 0) return;\n\n if (this._fitConfig) {\n if (!SDKRegistry.fit) {\n console.warn('[Motion] FitResolver not loaded. Did you import the SDK?');\n return;\n }\n this._built = true;\n\n // Issue #10: Guard against uninitialized Engine before calling the callback\n if (!AnimationBuilder._createAnimationCallback) {\n throw new Error('AnimationBuilder: Engine not initialized. Call Engine.init() first.');\n }\n\n const internalConfig: InternalAnimationConfig = {};\n if (this._repeat !== undefined) internalConfig.repeat = this._repeat;\n if (this._repeatDelay !== undefined) internalConfig.repeatDelay = this._repeatDelay;\n if (this._yoyo) internalConfig.yoyo = this._yoyo;\n if (this._onStart) internalConfig.onStart = this._onStart;\n if (this._onUpdate) internalConfig.onUpdate = this._onUpdate;\n if (this._onComplete) internalConfig.onComplete = this._onComplete;\n if (this._onRepeat) internalConfig.onRepeat = this._onRepeat;\n if (this._onReverseComplete) internalConfig.onReverseComplete = this._onReverseComplete;\n // Issue #9: Forward stagger into Fit internalConfig so staggered Fit works\n if (this._stagger !== undefined) internalConfig.stagger = this._stagger;\n\n // Issue #8: Create one Animation per Element target so that\n // registerPendingSetup (a single-slot setter) is called exactly once\n // per Animation — avoids overwriting geometry captured for earlier targets (Fit).\n const animations: Animation[] = [];\n for (const target of this._targets) {\n if (!(target instanceof Element)) continue;\n const result = AnimationBuilder._createAnimationCallback(\n [target],\n {},\n this._duration,\n this._delay,\n this._ease,\n false,\n undefined,\n internalConfig\n );\n if (Array.isArray(result)) {\n animations.push(...result);\n } else {\n animations.push(result);\n }\n }\n\n const fitCfg = this._fitConfig;\n for (const anim of animations) {\n for (const target of anim.getTargets()) {\n if (target instanceof Element) {\n SDKRegistry.fit!.registerPendingSetup(anim, target, fitCfg, PropTweenPool);\n }\n }\n }\n\n this._animation = animations[0] ?? null;\n this._animations = animations;\n this._expectedAnimationIds = animations.map((a) => a.getId());\n return;\n }\n\n this._built = true;\n\n let vars: AnimationVars = {};\n let isFrom = false;\n let fromVarsToPass: AnimationVars | undefined;\n\n if (this._fromVars && this._toVars) {\n vars = { ...this._toVars };\n fromVarsToPass = { ...this._fromVars };\n isFrom = true;\n\n // Merge missing properties from 'from' into 'to' with defaults.\n // Properties in 'from' but not 'to' need a \"to\" value so the\n // animation returns to the element's natural state.\n // IMPORTANT: resolve computed values NOW, before clearAnimationStylesForProps\n // clears inline styles (which would make computed values return defaults).\n for (const key of Object.keys(fromVarsToPass)) {\n if (!(key in vars)) {\n if (key in PROPERTY_DEFAULTS) {\n (vars as Record<string, number>)[key] = PROPERTY_DEFAULTS[key];\n } else if (isColorProp(key)) {\n (vars as Record<string, string>)[key] = '';\n } else if (isFilterProp(key)) {\n (vars as Record<string, string>)[key] = '';\n } else {\n // Generic CSS property (e.g. backgroundSize, borderRadius, etc.):\n // Read the current computed value from the first DOM target to use\n // as the \"to\" endpoint. This must happen before clearAnimationStylesForProps.\n const fromRaw = fromVarsToPass[key];\n if (fromRaw === undefined || fromRaw === null) continue;\n const fromParsed = parseValue(fromRaw as string | number);\n const unit = fromParsed.unit || getDefaultUnit(key);\n let resolvedValue: number | undefined;\n\n for (const target of this._targets) {\n if (target instanceof Element) {\n if (isCSSVariable(key)) {\n // CSS variables: read via getComputedStyle getPropertyValue\n try {\n const computed = window.getComputedStyle(target);\n const val = computed.getPropertyValue(key).trim();\n if (val) resolvedValue = parseValue(val).value;\n } catch { /* ignore */ }\n } else {\n resolvedValue = getCurrentValue(target, key);\n }\n break; // Use first target's value (all targets share the same \"to\")\n }\n }\n\n // Build the \"to\" value string with the same unit as \"from\"\n const endValue = resolvedValue ?? 0;\n (vars as Record<string, string>)[key] = unit ? `${endValue}${unit}` : `${endValue}`;\n }\n }\n }\n } else if (this._fromVars) {\n vars = { ...this._fromVars };\n isFrom = true;\n } else if (this._toVars) {\n vars = { ...this._toVars };\n }\n\n // Handle text splitting (only if TextSplitter is loaded)\n if (this._splitType && SDKRegistry.textSplitter?.split) {\n const splitOptions = { mask: !!this._mask, consumer: this as object };\n const splitElements: Element[] = [];\n for (const target of this._originalTargets) {\n if (target instanceof Element) {\n const elements = SDKRegistry.textSplitter?.split(target, this._splitType, splitOptions);\n splitElements.push(...elements);\n }\n }\n if (splitElements.length > 0) {\n this._targets = splitElements;\n }\n }\n\n // Get properties to animate\n this._propsToAnimate = Object.keys(vars);\n if (fromVarsToPass) {\n for (const key of Object.keys(fromVarsToPass)) {\n if (!this._propsToAnimate.includes(key)) {\n this._propsToAnimate.push(key);\n }\n }\n }\n\n // Capture initial values for plain objects\n if (this._plainObjectInitialValues.size === 0) {\n this._capturePlainObjectValues(this._propsToAnimate);\n }\n\n // For FROM animations, clear cache and styles\n if (isFrom) {\n for (const target of this._targets) {\n if (target instanceof Element) {\n clearTransformCache(target);\n SDKRegistry.styleReset?.clearAnimationStylesForProps?.(target, this._propsToAnimate);\n }\n }\n }\n\n // Register animated properties for reset (only if StyleReset is loaded)\n if (SDKRegistry.styleReset?.registerAnimatedProps) {\n for (const target of this._targets) {\n if (target instanceof Element) {\n SDKRegistry.styleReset?.registerAnimatedProps(target, this._propsToAnimate);\n }\n }\n }\n\n // Build internal config for Engine\n const internalConfig = {\n repeat: this._repeat,\n repeatDelay: this._repeatDelay,\n yoyo: this._yoyo,\n stagger: this._stagger,\n onStart: this._onStart,\n onUpdate: this._onUpdate,\n onComplete: this._onComplete,\n onRepeat: this._onRepeat,\n onReverseComplete: this._onReverseComplete,\n _skipInitialRender: this._skipInitialFromRender || undefined,\n };\n\n if (!AnimationBuilder._createAnimationCallback) {\n throw new Error('AnimationBuilder: Engine not initialized. Call AnimationBuilder.setCreateAnimationCallback first.');\n }\n const result = AnimationBuilder._createAnimationCallback(\n this._targets,\n vars,\n this._duration,\n this._delay,\n this._ease,\n isFrom,\n fromVarsToPass,\n internalConfig\n );\n\n if (Array.isArray(result)) {\n this._animations = result;\n this._animation = result[0] || null;\n } else {\n this._animation = result;\n this._animations = [result];\n }\n\n this._expectedAnimationIds = this._animations.map(a => a.getId());\n }\n\n getAnimation(): Animation | null {\n this._ensureBuilt();\n return this._animation;\n }\n\n getAnimations(): Animation[] {\n this._ensureBuilt();\n return this._animations;\n }\n\n getTargets(): AnimationTarget[] {\n return this._targets;\n }\n\n /**\n * Get the original (pre-split) targets. When text splitting is used,\n * _targets holds the split fragments; _originalTargets holds the source elements.\n */\n getOriginalTargets(): AnimationTarget[] {\n return this._originalTargets;\n }\n\n /**\n * Whether this builder uses text splitting\n */\n hasSplit(): boolean {\n return !!this._splitType;\n }\n\n private _resetBuildState(): void {\n this._built = false;\n this._animations = [];\n this._animation = null;\n this._expectedAnimationIds = [];\n }\n\n /**\n * Get builder configuration for cloning (used by Timeline's each mode)\n */\n getConfig(): BuilderConfig {\n return {\n fromVars: this._fromVars ? { ...this._fromVars } : undefined,\n toVars: this._toVars ? { ...this._toVars } : undefined,\n duration: this._duration,\n delay: this._delay,\n ease: this._ease,\n axis: this._axis,\n fit: this._fitConfig ? { ...this._fitConfig } : undefined,\n split: this._splitType,\n mask: this._mask,\n stagger: this._stagger,\n repeat: this._repeat,\n repeatDelay: this._repeatDelay,\n yoyo: this._yoyo,\n onStart: this._onStart,\n onUpdate: this._onUpdate,\n onComplete: this._onComplete,\n onRepeat: this._onRepeat,\n onReverseComplete: this._onReverseComplete,\n };\n }\n\n /**\n * Set axis for mouse movement binding\n */\n setAxis(axis: 'x' | 'y'): void {\n this._axis = axis;\n }\n\n /**\n * Get axis binding\n */\n getAxis(): 'x' | 'y' | undefined {\n return this._axis;\n }\n\n /**\n * Tell Engine to skip the immediate FROM render (animation.render(0)).\n * Used by Timeline so it can capture initial values BEFORE FROM is applied.\n */\n setSkipInitialFromRender(skip: boolean): void {\n this._skipInitialFromRender = skip;\n }\n}\n",
|
|
30
|
+
"/**\n * AnimationBuilder - Config-based animation creator\n * \n * Creates animations from configuration objects.\n * Used internally by Motion() function.\n */\n\nimport type { AnimationVars, AnimationTarget, TargetInput, StaggerVars, RepeatConfig, SplitType, AnimationConfig, FitConfig, FlapConfig } from '../types';\nimport type { InternalAnimationConfig } from './Animation';\n\n/**\n * Internal builder configuration for cloning (not exported from SDK)\n */\ninterface BuilderConfig {\n fromVars?: AnimationVars;\n toVars?: AnimationVars;\n setVars?: AnimationVars;\n duration: number;\n delay: number;\n ease: string;\n axis?: 'x' | 'y';\n fit?: FitConfig;\n flap?: FlapConfig;\n split?: SplitType;\n mask?: boolean;\n stagger?: number | StaggerVars;\n repeat?: number;\n repeatDelay?: number;\n yoyo?: boolean;\n onStart?: () => void;\n onUpdate?: (progress: number) => void;\n onComplete?: () => void;\n onRepeat?: (repeatCount: number) => void;\n onReverseComplete?: () => void;\n}\nimport { resolveTargets } from '../utils/TargetResolver';\nimport { Animation } from './Animation';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { clearTransformCache } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { isColorProp, isFilterProp, isTransformProp, getCurrentValue, getCurrentValueInUnit, isCSSVariable, parseValue, getDefaultUnit, convertPxToUnit } from '../utils/PropertyParser';\nimport type { UserPropEntry } from '../utils/TextFlapper';\n\n/**\n * Default \"natural\" values for animatable properties.\n * Used when from has properties that to doesn't specify.\n */\nconst PROPERTY_DEFAULTS: Record<string, number> = {\n x: 0, y: 0, z: 0,\n rotate: 0, rotateX: 0, rotateY: 0, rotateZ: 0,\n scale: 1, scaleX: 1, scaleY: 1, scaleZ: 1,\n skewX: 0, skewY: 0,\n opacity: 1,\n};\n\nexport class AnimationBuilder {\n /**\n * Callback to create animations via Engine (set by Engine to avoid circular dependency)\n * Engine→Timeline→AnimationBuilder→Engine cycle is broken by this callback.\n */\n private static _createAnimationCallback: ((\n targets: AnimationTarget[],\n vars: AnimationVars,\n duration: number,\n delay: number,\n ease: string,\n isFrom: boolean,\n fromVars?: AnimationVars,\n config?: InternalAnimationConfig\n ) => Animation | Animation[]) | null = null;\n\n static setCreateAnimationCallback(callback: typeof AnimationBuilder._createAnimationCallback): void {\n AnimationBuilder._createAnimationCallback = callback;\n }\n\n private _targets: AnimationTarget[];\n private _originalTargets: AnimationTarget[];\n private _fromVars?: AnimationVars;\n private _toVars?: AnimationVars;\n private _setVars?: AnimationVars;\n private _duration: number = 0.5;\n private _delay: number = 0;\n private _ease: string = 'power1.out';\n private _animation: Animation | null = null;\n private _animations: Animation[] = [];\n private _built: boolean = false;\n // Configuration\n private _splitType?: SplitType;\n private _mask?: boolean;\n private _repeat?: number;\n private _repeatDelay?: number;\n private _yoyo?: boolean;\n private _stagger?: number | StaggerVars;\n private _axis?: 'x' | 'y';\n private _fitConfig?: FitConfig;\n private _flapConfig?: FlapConfig;\n // Lifecycle callbacks\n private _onStart?: () => void;\n private _onUpdate?: (progress: number) => void;\n private _onComplete?: () => void;\n private _onRepeat?: (repeatCount: number) => void;\n private _onReverseComplete?: () => void;\n\n // When true, Engine skips the immediate render(0) for FROM animations.\n // Set by Timeline._addBuilder so it can capture initial values first.\n private _skipInitialFromRender = false;\n\n // For pooling/rebuild\n private _plainObjectInitialValues: Map<AnimationTarget, Record<string, number>> = new Map();\n private _expectedAnimationIds: number[] = [];\n private _propsToAnimate: string[] = [];\n\n /**\n * Create an animation builder\n * @param targets - Elements or objects to animate\n * @param config - Animation configuration\n */\n constructor(targets: TargetInput, config?: AnimationConfig) {\n this._targets = resolveTargets(targets);\n this._originalTargets = this._targets;\n\n // Silently accept empty targets — matches GSAP's nullTargetWarn:false behavior.\n // The animation still participates in timelines (duration, callbacks) as a no-op.\n\n if (config) {\n this._applyAnimationConfig(config);\n }\n }\n\n /**\n * Apply animation config\n */\n private _applyAnimationConfig(config: AnimationConfig): void {\n if (config.from) this._fromVars = config.from;\n if (config.to) this._toVars = config.to;\n if (config.set) this._setVars = config.set;\n\n if (config.duration !== undefined) {\n if (typeof config.duration !== 'number' || !isFinite(config.duration) || config.duration < 0) {\n console.warn(`[Motion] Invalid duration: ${config.duration}. Must be a finite number >= 0. Using default.`);\n } else {\n this._duration = config.duration;\n }\n }\n\n if (config.delay !== undefined) {\n if (typeof config.delay !== 'number' || isNaN(config.delay)) {\n console.warn(`[Motion] Invalid delay: ${config.delay}. Must be a number. Using default 0.`);\n } else if (!isFinite(config.delay)) {\n console.warn(`[Motion] Invalid delay: ${config.delay}. Infinity is not a valid delay. Using default 0.`);\n } else {\n this._delay = config.delay;\n }\n }\n\n if (config.ease) this._ease = config.ease;\n if (config.split) this._splitType = config.split;\n if (config.mask) this._mask = config.mask;\n if (config.stagger !== undefined) this._stagger = config.stagger;\n if (config.axis) this._axis = config.axis;\n if (config.fit) this._fitConfig = config.fit;\n if (config.flap) {\n this._flapConfig = config.flap;\n // Auto-add split: 'chars' if not explicitly set\n if (!this._splitType) {\n this._splitType = 'chars';\n } else if (!this._splitType.includes('chars')) {\n // Upgrade to include chars (e.g., 'words' → 'chars,words')\n this._splitType = ('chars,' + this._splitType) as SplitType;\n }\n // Auto-add stagger if not explicitly set (each: 0 = simultaneous start)\n // User can override by adding their own Stagger config.\n if (this._stagger === undefined) {\n this._stagger = { each: 0, from: 'start' };\n }\n }\n\n if (config.repeat) {\n if (typeof config.repeat === 'number') {\n this._repeat = config.repeat;\n } else {\n this._repeat = config.repeat.times;\n this._repeatDelay = config.repeat.delay;\n this._yoyo = config.repeat.yoyo;\n }\n }\n\n if (config.onStart) this._onStart = config.onStart;\n if (config.onUpdate) this._onUpdate = config.onUpdate;\n if (config.onComplete) this._onComplete = config.onComplete;\n if (config.onRepeat) this._onRepeat = config.onRepeat;\n if (config.onReverseComplete) this._onReverseComplete = config.onReverseComplete;\n }\n\n /**\n * Capture initial values from plain object targets\n */\n private _capturePlainObjectValues(props: string[]): void {\n for (const target of this._targets) {\n if (target instanceof Element) continue;\n const obj = target as Record<string, number>;\n const values: Record<string, number> = {};\n for (const prop of props) {\n if (prop in obj && typeof obj[prop] === 'number') {\n values[prop] = obj[prop];\n }\n }\n if (Object.keys(values).length > 0) {\n this._plainObjectInitialValues.set(target, values);\n }\n }\n }\n\n /**\n * Restore plain object targets to initial values\n */\n private _restorePlainObjectValues(): void {\n for (const [target, values] of this._plainObjectInitialValues) {\n const obj = target as Record<string, number>;\n for (const [prop, value] of Object.entries(values)) {\n obj[prop] = value;\n }\n }\n }\n\n private _ensureBuilt(): void {\n if (!this._built) this._build();\n }\n\n /**\n * Check if animations have been invalidated (pooled or reused)\n */\n private _isInvalidated(): boolean {\n if (this._animations.length === 0) return false;\n return this._animations.some((anim, i) => {\n const currentId = anim.getId();\n const expectedId = this._expectedAnimationIds[i];\n return currentId === 0 || currentId !== expectedId;\n });\n }\n\n /**\n * Rebuild if animations were pooled\n */\n rebuildIfPooled(): void {\n if (this._isInvalidated()) {\n this._restorePlainObjectValues();\n for (const target of this._targets) {\n if (target instanceof Element) {\n clearTransformCache(target);\n SDKRegistry.styleReset?.clearAnimationStylesForProps?.(target, this._propsToAnimate);\n }\n }\n this._resetBuildState();\n this._ensureBuilt();\n }\n }\n\n private _build(): void {\n if (this._built) return;\n if (this._targets.length === 0) return;\n // Allow spacer entries: if duration > 0 but no from/to/fit, still create\n // an animation so it occupies time in a timeline (e.g. a hold/pause).\n if (!this._fromVars && !this._toVars && !this._setVars && !this._fitConfig && this._duration <= 0) return;\n\n if (this._fitConfig) {\n if (!SDKRegistry.fit) {\n console.warn('[Motion] FitResolver not loaded. Did you import the SDK?');\n return;\n }\n this._built = true;\n\n // Issue #10: Guard against uninitialized Engine before calling the callback\n if (!AnimationBuilder._createAnimationCallback) {\n throw new Error('AnimationBuilder: Engine not initialized. Call Engine.init() first.');\n }\n\n const internalConfig: InternalAnimationConfig = {};\n if (this._repeat !== undefined) internalConfig.repeat = this._repeat;\n if (this._repeatDelay !== undefined) internalConfig.repeatDelay = this._repeatDelay;\n if (this._yoyo) internalConfig.yoyo = this._yoyo;\n if (this._onStart) internalConfig.onStart = this._onStart;\n if (this._onUpdate) internalConfig.onUpdate = this._onUpdate;\n if (this._onComplete) internalConfig.onComplete = this._onComplete;\n if (this._onRepeat) internalConfig.onRepeat = this._onRepeat;\n if (this._onReverseComplete) internalConfig.onReverseComplete = this._onReverseComplete;\n // Issue #9: Forward stagger into Fit internalConfig so staggered Fit works\n if (this._stagger !== undefined) internalConfig.stagger = this._stagger;\n\n // Issue #8: Create one Animation per Element target so that\n // registerPendingSetup (a single-slot setter) is called exactly once\n // per Animation — avoids overwriting geometry captured for earlier targets (Fit).\n const animations: Animation[] = [];\n for (const target of this._targets) {\n if (!(target instanceof Element)) continue;\n const result = AnimationBuilder._createAnimationCallback(\n [target],\n {},\n this._duration,\n this._delay,\n this._ease,\n false,\n undefined,\n internalConfig\n );\n if (Array.isArray(result)) {\n animations.push(...result);\n } else {\n animations.push(result);\n }\n }\n\n const fitCfg = this._fitConfig;\n for (const anim of animations) {\n for (const target of anim.getTargets()) {\n if (target instanceof Element) {\n SDKRegistry.fit!.registerPendingSetup(anim, target, fitCfg, PropTweenPool);\n }\n }\n }\n\n this._animation = animations[0] ?? null;\n this._animations = animations;\n this._expectedAnimationIds = animations.map((a) => a.getId());\n return;\n }\n\n this._built = true;\n\n // When `set` is present alongside `to` but WITHOUT an explicit `from`,\n // promote the set values into `from` so the animation starts from the set\n // state and transitions to the `to` values. This matches the user\n // expectation: \"set opacity to 0, then animate to 0.5\" → from:0 → to:0.5.\n // If an explicit `from` is also present, `set` stays as a separate\n // zero-duration tween handled at the end of _build().\n if (this._setVars && Object.keys(this._setVars).length > 0 && this._toVars && !this._fromVars) {\n this._fromVars = { ...this._setVars };\n this._setVars = undefined; // consumed — don't create a separate tween\n }\n\n let vars: AnimationVars = {};\n let isFrom = false;\n let fromVarsToPass: AnimationVars | undefined;\n\n if (this._fromVars && this._toVars) {\n vars = { ...this._toVars };\n fromVarsToPass = { ...this._fromVars };\n isFrom = true;\n\n // Merge missing properties from 'from' into 'to' with defaults.\n // Properties in 'from' but not 'to' need a \"to\" value so the\n // animation returns to the element's natural state.\n // IMPORTANT: resolve computed values NOW, before clearAnimationStylesForProps\n // clears inline styles (which would make computed values return defaults).\n for (const key of Object.keys(fromVarsToPass)) {\n if (!(key in vars)) {\n if (key in PROPERTY_DEFAULTS) {\n (vars as Record<string, number>)[key] = PROPERTY_DEFAULTS[key];\n } else if (isColorProp(key)) {\n (vars as Record<string, string>)[key] = '';\n } else if (isFilterProp(key)) {\n (vars as Record<string, string>)[key] = '';\n } else {\n // Generic CSS property (e.g. backgroundSize, borderRadius, etc.):\n // Read the current computed value from the first DOM target to use\n // as the \"to\" endpoint. This must happen before clearAnimationStylesForProps.\n const fromRaw = fromVarsToPass[key];\n if (fromRaw === undefined || fromRaw === null) continue;\n const fromParsed = parseValue(fromRaw as string | number);\n const unit = fromParsed.unit || getDefaultUnit(key);\n let resolvedValue: number | undefined;\n\n for (const target of this._targets) {\n if (target instanceof Element) {\n if (isCSSVariable(key)) {\n // CSS variables: read via getComputedStyle getPropertyValue\n try {\n const computed = window.getComputedStyle(target);\n const val = computed.getPropertyValue(key).trim();\n if (val) resolvedValue = parseValue(val).value;\n } catch { /* ignore */ }\n } else {\n // getCurrentValue returns px for layout properties (width, height).\n // Convert to the animation's unit so e.g. 400px → 25% not 400%.\n resolvedValue = getCurrentValueInUnit(target, key, unit);\n }\n break; // Use first target's value (all targets share the same \"to\")\n }\n }\n\n // Build the \"to\" value string with the same unit as \"from\"\n const endValue = resolvedValue ?? 0;\n (vars as Record<string, string>)[key] = unit ? `${endValue}${unit}` : `${endValue}`;\n }\n }\n }\n } else if (this._fromVars) {\n vars = { ...this._fromVars };\n isFrom = true;\n } else if (this._toVars) {\n vars = { ...this._toVars };\n }\n\n // Handle text splitting (only if TextSplitter is loaded)\n if (this._splitType && SDKRegistry.textSplitter?.split) {\n const splitOptions = { mask: !!this._mask, consumer: this as object };\n const splitElements: Element[] = [];\n for (const target of this._originalTargets) {\n if (target instanceof Element) {\n const elements = SDKRegistry.textSplitter?.split(target, this._splitType, splitOptions);\n splitElements.push(...elements);\n }\n }\n if (splitElements.length > 0) {\n this._targets = splitElements;\n }\n }\n\n // Handle text flapper (after split produces char spans)\n // TextFlapper.prepare() returns per-char render drivers. We create a real\n // Animation per char via the Engine callback, so Timeline manages everything\n // (play/pause/seek/restart/kill) — no separate FlapController needed.\n // Flap animations are collected here and merged AFTER the main animation\n // creation below (which overwrites this._animations).\n let pendingFlapAnims: Animation[] | undefined;\n // Per-cycle user property entries built below, referenced by the stripping pass.\n let flapUserProps: UserPropEntry[] | undefined;\n if (this._flapConfig && SDKRegistry.textFlapper?.prepare && AnimationBuilder._createAnimationCallback) {\n // Build per-cycle user property entries when BOTH from and to are provided.\n // These are passed to prepare() so the render callback interpolates them\n // once per character-swap cycle instead of once over the full animation duration.\n // Properties that require complex parsing (color, filter, drawSVG, path,\n // clipPath, CSS variables) are not extractable and remain in the main animation.\n if (fromVarsToPass && Object.keys(fromVarsToPass).length > 0) {\n const entries: UserPropEntry[] = [];\n for (const key of Object.keys(fromVarsToPass)) {\n const fromRaw = fromVarsToPass[key];\n const toRaw = (vars as Record<string, unknown>)[key];\n if (fromRaw === undefined || toRaw === undefined) continue;\n // Skip props that cannot be represented as a single interpolated number\n if (isColorProp(key) || isFilterProp(key) || isCSSVariable(key)) continue;\n if (key === 'drawSVG' || key === 'path' || key === 'clip-path' || key === 'clipPath') continue;\n const fromParsed = parseValue(fromRaw as string | number);\n const toParsed = parseValue(toRaw as string | number);\n const unit = fromParsed.unit || toParsed.unit || getDefaultUnit(key);\n entries.push({\n prop: key,\n from: fromParsed.value,\n to: toParsed.value,\n unit,\n isTransform: isTransformProp(key),\n });\n }\n if (entries.length > 0) flapUserProps = entries;\n }\n\n const charElements = this._targets.filter(\n (t): t is HTMLElement => t instanceof HTMLElement\n );\n if (charElements.length > 0) {\n // Infer continuous mode from the animation's repeat config:\n // when repeat > 0 (or -1), chars should cycle without landing on target\n const isContinuous = (this._repeat !== undefined && this._repeat !== 0);\n\n const drivers = SDKRegistry.textFlapper.prepare(charElements, this._flapConfig, isContinuous, flapUserProps);\n // Resolve stagger delays in seconds (engine expects seconds for delay)\n const staggerDelays = this._stagger && SDKRegistry.stagger\n ? SDKRegistry.stagger.resolve(charElements, this._stagger)\n : undefined;\n\n pendingFlapAnims = [];\n let driverStaggerIdx = 0;\n for (let i = 0; i < charElements.length; i++) {\n const raw = charElements[i].textContent ?? '';\n const ws = raw === '' || raw === ' ' || raw === '\\u00a0' || raw === '\\n' || raw === '\\t';\n if (ws) continue;\n if (driverStaggerIdx >= drivers.length) break;\n const driver = drivers[driverStaggerIdx];\n const delay = staggerDelays ? staggerDelays[i] : 0;\n\n const flapAnim = AnimationBuilder._createAnimationCallback(\n [driver.el],\n {}, // no CSS vars — render callback handles visual\n this._duration,\n delay,\n this._ease ?? 'power1.out',\n false,\n undefined,\n {\n onUpdate: driver.render,\n onComplete: driver.finalize,\n // For continuous mode, use a very large finite repeat so the\n // animation cycles effectively forever without making the\n // timeline duration Infinity (which breaks scrub/progress).\n repeat: isContinuous\n ? Math.ceil(60 / this._duration) // ~60 seconds of cycling\n : 0,\n _skipInitialRender: this._skipInitialFromRender || undefined,\n }\n );\n const flapAnims = Array.isArray(flapAnim) ? flapAnim : [flapAnim];\n pendingFlapAnims.push(...flapAnims);\n driverStaggerIdx++;\n }\n }\n }\n\n // When flap is active, strip CSS properties that are now handled by the\n // per-cycle user-prop interpolation or that conflict with the flap render.\n //\n // Case 1 — user props extracted (both from and to provided):\n // All extracted props are stripped from the main animation vars because\n // the flap render callback now interpolates them per cycle.\n //\n // Case 2 — fade / blur type native conflict:\n // fade writes directly to el.style.opacity; blur writes to el.style.filter.\n // Strip those specific properties to prevent the main animation from\n // overwriting the flap visual each frame.\n //\n // flip / scale / slide / board route their transform writes through\n // TransformCache so the engine can freely tween other transform properties\n // in parallel — no stripping needed beyond Case 1 above.\n if (pendingFlapAnims?.length && this._flapConfig) {\n const flapType = this._flapConfig.type ?? 'flip';\n const userPropKeys = new Set(flapUserProps?.map(p => p.prop) ?? []);\n\n const shouldStrip = (prop: string): boolean => {\n // Props handed to the flap render callback — strip to avoid double-driving.\n if (userPropKeys.has(prop)) return true;\n // Native conflict props for fade/blur types.\n switch (flapType) {\n case 'fade': return prop === 'opacity';\n case 'blur': return isFilterProp(prop);\n default: return false;\n }\n };\n\n const strippedProps: string[] = [];\n for (const key of Object.keys(vars)) {\n if (shouldStrip(key)) {\n strippedProps.push(key);\n delete (vars as Record<string, unknown>)[key];\n }\n }\n if (fromVarsToPass) {\n for (const key of Object.keys(fromVarsToPass)) {\n if (shouldStrip(key) && !strippedProps.includes(key)) {\n strippedProps.push(key);\n delete (fromVarsToPass as Record<string, unknown>)[key];\n }\n }\n }\n // Only warn about props stripped due to native type conflict (not per-cycle\n // user props — those are intentionally owned by the render callback).\n const conflictStripped = strippedProps.filter(p => !userPropKeys.has(p));\n if (conflictStripped.length > 0) {\n console.warn(`[Motion.page] Flap type '${flapType}' owns '${conflictStripped.join(', ')}' — user-defined values for these properties were removed. Use a different flap type to avoid this conflict.`);\n }\n }\n\n // Get properties to animate\n this._propsToAnimate = Object.keys(vars);\n if (fromVarsToPass) {\n for (const key of Object.keys(fromVarsToPass)) {\n if (!this._propsToAnimate.includes(key)) {\n this._propsToAnimate.push(key);\n }\n }\n }\n\n // Capture initial values for plain objects\n if (this._plainObjectInitialValues.size === 0) {\n this._capturePlainObjectValues(this._propsToAnimate);\n }\n\n // For FROM animations, clear cache and styles\n if (isFrom) {\n for (const target of this._targets) {\n if (target instanceof Element) {\n clearTransformCache(target);\n SDKRegistry.styleReset?.clearAnimationStylesForProps?.(target, this._propsToAnimate);\n }\n }\n }\n\n // Register animated properties for reset (only if StyleReset is loaded)\n if (SDKRegistry.styleReset?.registerAnimatedProps) {\n for (const target of this._targets) {\n if (target instanceof Element) {\n SDKRegistry.styleReset?.registerAnimatedProps(target, this._propsToAnimate);\n }\n }\n }\n\n // Build internal config for Engine\n const internalConfig = {\n repeat: this._repeat,\n repeatDelay: this._repeatDelay,\n yoyo: this._yoyo,\n stagger: this._stagger,\n onStart: this._onStart,\n onUpdate: this._onUpdate,\n onComplete: this._onComplete,\n onRepeat: this._onRepeat,\n onReverseComplete: this._onReverseComplete,\n _skipInitialRender: this._skipInitialFromRender || undefined,\n };\n\n if (!AnimationBuilder._createAnimationCallback) {\n throw new Error('AnimationBuilder: Engine not initialized. Call AnimationBuilder.setCreateAnimationCallback first.');\n }\n\n // Skip the main animation when flap-only (no CSS properties to tween).\n // The flap driver animations handle everything; an empty main animation\n // would just waste an Animation pool slot per target.\n const hasVars = Object.keys(vars).length > 0 || (fromVarsToPass && Object.keys(fromVarsToPass).length > 0);\n if (hasVars || !pendingFlapAnims?.length) {\n const result = AnimationBuilder._createAnimationCallback(\n this._targets,\n vars,\n this._duration,\n this._delay,\n this._ease,\n isFrom,\n fromVarsToPass,\n internalConfig\n );\n\n if (Array.isArray(result)) {\n this._animations = result;\n this._animation = result[0] || null;\n } else {\n this._animation = result;\n this._animations = [result];\n }\n }\n\n // Merge flap animations (collected earlier, before main animation creation)\n if (pendingFlapAnims && pendingFlapAnims.length > 0) {\n this._animations.push(...pendingFlapAnims);\n if (!this._animation) {\n this._animation = pendingFlapAnims[0];\n }\n }\n\n this._expectedAnimationIds = this._animations.map(a => a.getId());\n\n // Apply `set` vars as a zero-duration tween prepended to the animation list.\n // This mirrors GSAP's gsap.set() — properties are applied immediately at the\n // animation's start time without transition.\n if (this._setVars && Object.keys(this._setVars).length > 0 && AnimationBuilder._createAnimationCallback) {\n const setResult = AnimationBuilder._createAnimationCallback(\n this._targets,\n { ...this._setVars },\n 0, // duration: 0 (instant)\n 0, // delay: 0\n 'none',\n false,\n undefined,\n { _skipInitialRender: this._skipInitialFromRender || undefined },\n );\n const setAnims = Array.isArray(setResult) ? setResult : [setResult];\n // Prepend set animations so they are processed first in the timeline\n this._animations = [...setAnims, ...this._animations];\n this._expectedAnimationIds = this._animations.map(a => a.getId());\n }\n }\n\n getAnimation(): Animation | null {\n this._ensureBuilt();\n return this._animation;\n }\n\n getAnimations(): Animation[] {\n this._ensureBuilt();\n return this._animations;\n }\n\n getTargets(): AnimationTarget[] {\n return this._targets;\n }\n\n /**\n * Get the original (pre-split) targets. When text splitting is used,\n * _targets holds the split fragments; _originalTargets holds the source elements.\n */\n getOriginalTargets(): AnimationTarget[] {\n return this._originalTargets;\n }\n\n /**\n * Whether this builder uses text splitting\n */\n hasSplit(): boolean {\n return !!this._splitType;\n }\n\n /**\n * Whether this builder uses text flapper\n */\n hasFlap(): boolean {\n return !!this._flapConfig;\n }\n\n\n private _resetBuildState(): void {\n this._built = false;\n this._animations = [];\n this._animation = null;\n this._expectedAnimationIds = [];\n }\n\n /**\n * Get builder configuration for cloning (used by Timeline's each mode)\n */\n getConfig(): BuilderConfig {\n return {\n fromVars: this._fromVars ? { ...this._fromVars } : undefined,\n toVars: this._toVars ? { ...this._toVars } : undefined,\n setVars: this._setVars ? { ...this._setVars } : undefined,\n duration: this._duration,\n delay: this._delay,\n ease: this._ease,\n axis: this._axis,\n fit: this._fitConfig ? { ...this._fitConfig } : undefined,\n flap: this._flapConfig ? { ...this._flapConfig } : undefined,\n split: this._splitType,\n mask: this._mask,\n stagger: this._stagger,\n repeat: this._repeat,\n repeatDelay: this._repeatDelay,\n yoyo: this._yoyo,\n onStart: this._onStart,\n onUpdate: this._onUpdate,\n onComplete: this._onComplete,\n onRepeat: this._onRepeat,\n onReverseComplete: this._onReverseComplete,\n };\n }\n\n /**\n * Set axis for mouse movement binding\n */\n setAxis(axis: 'x' | 'y'): void {\n this._axis = axis;\n }\n\n /**\n * Get axis binding\n */\n getAxis(): 'x' | 'y' | undefined {\n return this._axis;\n }\n\n /**\n * Tell Engine to skip the immediate FROM render (animation.render(0)).\n * Used by Timeline so it can capture initial values BEFORE FROM is applied.\n */\n setSkipInitialFromRender(skip: boolean): void {\n this._skipInitialFromRender = skip;\n }\n}\n",
|
|
31
31
|
"/**\n * PositionParser - Parse timeline position parameters\n *\n * Supports:\n * - Number: absolute time (e.g., 2)\n * - \"+=0.5\": relative to current end (0.5s after)\n * - \"-=0.5\": relative to current end (0.5s before, overlap)\n * - \"<\": start of previous animation\n * - \">\": end of previous animation\n * - \"<0.5\" or \"<+0.5\": start of previous + offset\n * - \"<-0.5\": start of previous - offset\n * - \">0.5\" or \">+0.5\": end of previous + offset\n * - \">-0.5\": end of previous - offset\n */\n\nexport class PositionParser {\n /**\n * Parse position parameter and return absolute time\n * @param position Position parameter (string or number)\n * @param currentEnd Current end time of timeline\n * @param previousStart Start time of previous child\n * @param previousEnd End time of previous child\n */\n static parse(\n position: string | number | undefined,\n currentEnd: number,\n previousStart: number,\n previousEnd: number\n ): number {\n if (position === undefined) {\n return currentEnd;\n }\n\n if (typeof position === 'number') {\n return position;\n }\n\n const str = position.trim();\n\n // \"<\": start of previous animation\n if (str === '<') {\n return previousStart;\n }\n\n // \">\": end of previous animation\n if (str === '>') {\n return previousEnd;\n }\n\n // \"<offset\": start of previous + offset\n if (str.startsWith('<')) {\n const offset = parseFloat(str.substring(1));\n if (!isNaN(offset)) {\n return previousStart + offset;\n }\n }\n\n // \">offset\": end of previous + offset\n if (str.startsWith('>')) {\n const offset = parseFloat(str.substring(1));\n if (!isNaN(offset)) {\n return previousEnd + offset;\n }\n }\n\n // \"+=\" relative to current end\n if (str.startsWith('+=')) {\n const offset = parseFloat(str.substring(2));\n return currentEnd + offset;\n }\n\n // \"-=\" relative to current end\n if (str.startsWith('-=')) {\n const offset = parseFloat(str.substring(2));\n return currentEnd - offset;\n }\n\n console.warn(`Invalid position parameter: \"${str}\", using current end`);\n return currentEnd;\n }\n}\n",
|
|
32
|
-
"/**\n * Timeline for sequencing animations\n *\n * Features:\n * - Named timeline registry (create/retrieve by name)\n * - Position parameter support (absolute, +=, -=, <, >)\n * - Nested timelines\n * - Callback scheduling\n * - Control methods (play, pause, reverse, seek, etc.)\n * - Trigger system integration\n */\n\nimport type {\n HoverConfig,\n ClickConfig,\n ScrollConfig,\n MouseMoveConfig,\n GestureConfig,\n GestureConfigInternal,\n CursorConfig,\n PageExitConfig,\n TargetInput,\n AnimationConfig,\n TimelineConfig,\n RepeatConfig,\n} from '../types';\nimport { Animation } from './Animation';\nimport { AnimationBuilder } from './AnimationBuilder';\nimport { PositionParser } from '../utils/PositionParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { isElement } from '../utils/TargetResolver';\nimport { getCurrentValue, getOriginalCSSValueWithUnit, isTransformProp, isColorProp, isFilterProp, isPathProp } from '../utils/PropertyParser';\nimport { setTransformValue, buildTransformString, clearTransformCache } from '../render/TransformCache';\n\ninterface TimelineChild {\n type: 'animation' | 'timeline' | 'callback';\n child: Animation | Timeline | ((...args: unknown[]) => void);\n builder?: AnimationBuilder; // Store builder for each-mode cloning\n startTime: number;\n duration: number;\n params?: unknown[];\n}\n\nexport class Timeline {\n /** Monotonic counter for generating unique each-mode instance names */\n private static _eachCounter = 0;\n\n /**\n * Static callback for registering named timelines with Engine.\n * Set by Engine.getInstance() to break circular dependency.\n * @internal\n */\n private static _registerWithEngine: ((name: string, timeline: Timeline) => void) | null = null;\n\n /**\n * Set the engine registration callback. Called once by Engine during initialization.\n * @internal\n */\n static setEngineRegisterCallback(callback: (name: string, timeline: Timeline) => void): void {\n Timeline._registerWithEngine = callback;\n }\n\n private _name?: string;\n private _children: TimelineChild[] = [];\n private _duration: number = 0;\n private _time: number = 0;\n private _progress: number = 0;\n private _timeScale: number = 1;\n private _isActive: boolean = false;\n private _isReversed: boolean = false;\n private _killed: boolean = false;\n // Tracks whether the timeline has been rendered at position 0 and is still there.\n // Used to skip the expensive _restoreInitialValues() + _renderPositionZeroAnimations()\n // path when progress(0) is called repeatedly (e.g., scroll scrub for headings not yet\n // in view). Without this, each call clears the transform cache and re-hydrates it via\n // getComputedStyle(), causing massive layout thrashing.\n private _atZeroState: boolean = false;\n\n // Repeat support\n private _repeat: number = 0;\n private _repeatDelay: number = 0;\n private _yoyo: boolean = false;\n private _currentRepeat: number = 0;\n private _completeFired: boolean = false;\n private _inRepeatDelay: boolean = false;\n private _repeatDelayCounter: number = 0;\n\n // Callbacks\n private _onStart?: () => void;\n private _onUpdate?: (progress: number, time: number) => void;\n private _onComplete?: () => void;\n private _onRepeat?: (repeatCount: number) => void;\n private _startFired: boolean = false;\n\n /**\n * Reference to attached trigger (for cleanup in kill()).\n * @internal\n */\n _trigger?: { disable(): void };\n\n /** @internal */\n private _unregisterCallback?: () => void;\n\n /**\n * Set unregister callback for cleanup. Called by Engine after registration.\n * @internal\n */\n setUnregisterCallback(callback: () => void): void {\n this._unregisterCallback = callback;\n }\n\n // Track previous child for position parsing\n private _previousStart: number = 0;\n private _previousEnd: number = 0;\n\n // Store cloned timelines for each-mode (independent per-element triggers)\n private _eachInstances?: Timeline[];\n private _eachDuration?: number; // Preserves pre-each duration for forwarding\n\n // Initial property values per element (captured once at build time, restored on seek to 0)\n // For regular CSS: stores value/unit OR a flag to just remove inline style\n // For transforms: we just track that the element was animated (will clear cache on restore)\n private _initialValues: Map<Element, Map<string, { \n removeInline: boolean; // If true, just remove inline style (lets stylesheet auto/calc/clamp take over)\n value?: number; \n unit?: string;\n }>> = new Map();\n\n // Track original inline transform values for restoration\n private _initialTransforms: Map<Element, string | null> = new Map();\n\n // Track elements that have transform animations (for cache cleanup)\n private _transformElements: Set<Element> = new Set();\n\n // Original inline transition values per animated element. Populated at build time.\n // During active playback, transitions are overridden with 'none' to prevent CSS\n // transitions from interfering with frame-by-frame SDK rendering.\n // Disabled on timeline start, restored on completion and kill.\n private _savedTransitions: Map<HTMLElement, string> = new Map();\n private _transitionsDisabled: boolean = false;\n\n // Original will-change values per animated element with transform properties.\n // Saved at build time, applied on first tick (not at build time) so the browser\n // has at least one frame to layout newly-created elements (e.g. text split spans).\n // Applying will-change before first layout can cause compositor layer issues\n // where elements render as invisible despite correct inline styles.\n private _savedWillChange: Map<HTMLElement, string> = new Map();\n private _willChangeApplied: boolean = false;\n\n // Track parent elements whose opacity was overridden for split+mask animations.\n // When split text is used, the parent's CSS pre-hide (e.g. opacity: 0) would block\n // fragments from being visible. We override with opacity: 1, but must restore the\n // original value on kill/reset to prevent state leaks across timeline rebuilds.\n private _splitParentOverrides: Map<HTMLElement, string> = new Map();\n\n constructor(name?: string, config?: TimelineConfig) {\n this._name = name;\n\n // Parse repeat config\n if (config?.repeat !== undefined) {\n if (typeof config.repeat === 'number') {\n this._repeat = config.repeat;\n } else {\n this._repeat = config.repeat.times;\n this._repeatDelay = config.repeat.delay ?? 0;\n this._yoyo = config.repeat.yoyo ?? false;\n }\n }\n\n // Apply callbacks from config\n if (config?.onStart) this._onStart = config.onStart;\n if (config?.onUpdate) this._onUpdate = config.onUpdate;\n if (config?.onComplete) this._onComplete = config.onComplete;\n if (config?.onRepeat) this._onRepeat = config.onRepeat;\n\n // Register named timeline with Engine via callback (avoids circular dependency)\n if (name && Timeline._registerWithEngine) {\n Timeline._registerWithEngine(name, this);\n }\n }\n\n /**\n * Get timeline duration (single cycle)\n */\n duration(): number {\n if (this._eachDuration !== undefined) {\n return this._eachDuration;\n }\n return this._duration;\n }\n\n /**\n * Get total duration including all repeats.\n * Returns Infinity when repeat is -1 (infinite loop).\n */\n totalDuration(): number {\n if (this._repeat === -1) return Infinity;\n return this._duration * (this._repeat + 1) + this._repeatDelay * this._repeat;\n }\n\n /**\n * Clear timeline state for rebuild (used for re-triggerable animations)\n * Kills existing animations, clears children, resets state\n */\n clear(): this {\n // Kill existing animations\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill();\n }\n }\n\n // Reset state\n this._children = [];\n this._duration = 0;\n this._time = 0;\n this._progress = 0;\n this._isActive = false;\n this._isReversed = false;\n this._startFired = false;\n this._previousStart = 0;\n this._previousEnd = 0;\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._splitParentOverrides.clear();\n this._clearTransitionData();\n // Reset repeat state\n this._currentRepeat = 0;\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n\n return this;\n }\n\n /**\n * Capture initial values for an animation's properties (called once per property per element)\n */\n private _captureInitialValues(animation: Animation): void {\n const targets = animation.getTargets();\n // Access PropTweens to get property names\n let propTween = animation.getFirstPropTween();\n\n while (propTween) {\n const property = propTween.property;\n\n for (const target of targets) {\n if (!(target instanceof Element)) continue;\n\n // Get or create property map for this element\n let propMap = this._initialValues.get(target);\n if (!propMap) {\n propMap = new Map();\n this._initialValues.set(target, propMap);\n }\n\n // Only capture if not already recorded (first animation for this property wins)\n if (!propMap.has(property)) {\n const htmlEl = target as HTMLElement;\n \n if (isTransformProp(property) || isPathProp(property)) {\n // Transform properties AND path property: track that this element has transforms\n // Path animations modify x, y, and optionally rotate transforms\n // We'll clear the entire transform cache on restore\n this._transformElements.add(target);\n if (!this._initialTransforms.has(target)) {\n const inlineTransform = htmlEl.style.transform;\n this._initialTransforms.set(target, inlineTransform || null);\n }\n // Still add to propMap so we know to restore this element\n propMap.set(property, { removeInline: true });\n } else if (isColorProp(property) || isFilterProp(property)) {\n // Color and filter properties: always remove inline style on restore\n // These are strings (not numeric) - just let stylesheet take over\n propMap.set(property, { removeInline: true });\n } else {\n // Regular CSS properties (numeric values with units)\n const kebabProp = property.replace(/([A-Z])/g, '-$1').toLowerCase();\n const hasInlineStyle = htmlEl.style.getPropertyValue(kebabProp) !== '';\n \n if (hasInlineStyle) {\n // Inline style exists - capture its value/unit so we can restore it\n const initial = getOriginalCSSValueWithUnit(target, property);\n propMap.set(property, { removeInline: false, value: initial.value, unit: initial.unit });\n } else {\n // No inline style - just flag to remove inline on restore (lets stylesheet auto/calc/clamp take over)\n propMap.set(property, { removeInline: true });\n }\n }\n }\n }\n\n propTween = propTween.next;\n }\n }\n\n /**\n * Restore all elements to their initial property values (used when seeking to position 0)\n */\n private _restoreInitialValues(): void {\n // First, handle transform elements - clear cache and remove inline transform\n for (const element of this._transformElements) {\n const htmlEl = element as HTMLElement;\n // Clear the transform cache completely (resets to defaults: scale=1, others=0)\n clearTransformCache(element);\n const originalTransform = this._initialTransforms.get(element);\n if (originalTransform) {\n htmlEl.style.transform = originalTransform;\n } else {\n htmlEl.style.removeProperty('transform');\n }\n }\n\n // Then handle regular CSS properties\n for (const [element, propMap] of this._initialValues) {\n const htmlEl = element as HTMLElement;\n\n for (const [property, initial] of propMap) {\n // Skip transform properties - already handled above\n if (isTransformProp(property)) continue;\n\n const kebabProp = property.replace(/([A-Z])/g, '-$1').toLowerCase();\n\n if (initial.removeInline) {\n // No original inline style - just remove the inline style we added\n // This lets the stylesheet value (auto, calc, clamp, etc.) take over\n htmlEl.style.removeProperty(kebabProp);\n } else {\n // Regular CSS property - restore with original value and unit\n const cssValue = `${initial.value}${initial.unit}`;\n htmlEl.style.setProperty(kebabProp, cssValue);\n }\n }\n }\n }\n\n\n /**\n * Disable CSS transitions on all animated elements.\n * Called when the timeline starts playing to prevent CSS transitions\n * from fighting frame-by-frame SDK rendering.\n */\n private _disableTransitions(): void {\n if (this._transitionsDisabled) return;\n for (const element of this._savedTransitions.keys()) {\n element.style.transition = 'none';\n }\n this._transitionsDisabled = true;\n }\n\n /**\n * Apply will-change: transform to elements with transform animations.\n * Deferred from build time to first tick so the browser has at least one\n * frame to layout newly-created elements (e.g. text split spans).\n */\n private _applyWillChange(): void {\n if (this._willChangeApplied) return;\n for (const element of this._savedWillChange.keys()) {\n element.style.willChange = 'transform';\n }\n this._willChangeApplied = true;\n }\n\n /**\n * Restore original CSS transition values on all animated elements.\n * Called on timeline completion and kill to re-enable CSS hover/focus\n * transitions after the SDK animation is done.\n */\n private _restoreTransitions(): void {\n if (!this._transitionsDisabled) return;\n for (const [element, original] of this._savedTransitions) {\n element.style.transition = original;\n }\n this._transitionsDisabled = false;\n }\n\n /**\n * Clear all saved transition data (for kill/clear).\n */\n private _clearTransitionData(): void {\n this._restoreTransitions();\n this._savedTransitions.clear();\n this._transitionsDisabled = false;\n this._restoreWillChange();\n }\n\n /**\n * Restore original will-change values on elements promoted for transforms.\n */\n private _restoreWillChange(): void {\n if (this._willChangeApplied) {\n for (const [element, original] of this._savedWillChange) {\n if (original) {\n element.style.willChange = original;\n } else {\n element.style.removeProperty('will-change');\n }\n }\n }\n this._savedWillChange.clear();\n this._willChangeApplied = false;\n }\n\n /**\n * Internal: Add animation entry (used by Motion function)\n * @internal\n */\n _addEntry(target: TargetInput, config: AnimationConfig, position?: string | number): this {\n const startTime = PositionParser.parse(\n position,\n this._duration,\n this._previousStart,\n this._previousEnd\n );\n\n const builder = new AnimationBuilder(target, config);\n return this._addBuilder(builder, startTime);\n }\n\n /**\n * Internal method to add an AnimationBuilder directly\n */\n private _addBuilder(builder: AnimationBuilder, position: number): this {\n // Tell builder to skip Engine's immediate FROM render (animation.render(0)).\n // Timeline captures initial CSS values first via _captureInitialValues,\n // THEN renders the FROM state via renderAtTime(0) below.\n // Without this, Engine renders FROM values before capture, so kill()\n // would restore elements to FROM values instead of their true initial state.\n builder.setSkipInitialFromRender(true);\n\n const animations = builder.getAnimations();\n if (animations.length === 0) return this;\n\n let maxEndTime = position;\n for (const animation of animations) {\n const animDelay = animation.getDelay();\n const animStartTime = position + animDelay;\n animation.clearDelay();\n const animDuration = animation.totalDuration();\n\n const timelineChild: TimelineChild = {\n type: 'animation',\n child: animation,\n builder,\n startTime: animStartTime,\n duration: animDuration,\n };\n\n this._children.push(timelineChild);\n animation.setTimelineChild(true);\n animation.pause();\n this._captureInitialValues(animation);\n\n // Save original CSS transition values for later override during playback.\n // CSS transitions (e.g. `transition: transform 0.25s`) fight the SDK's\n // frame-by-frame rendering when both control the same property.\n // Values are saved here at build time; actual disable happens on timeline start.\n for (const target of animation.getTargets()) {\n if (target instanceof HTMLElement && !this._savedTransitions.has(target)) {\n this._savedTransitions.set(target, target.style.transition);\n }\n }\n\n // Save original will-change values for elements with transform animations.\n // will-change: transform is NOT applied here at build time — it's deferred\n // to the first tick (_applyWillChange) so the browser has at least one frame\n // to layout newly-created elements (e.g. text split spans). Applying\n // will-change before first layout can cause compositor layer bugs where\n // elements render invisible despite correct inline styles.\n for (const target of animation.getTargets()) {\n if (target instanceof HTMLElement && this._transformElements.has(target) && !this._savedWillChange.has(target)) {\n this._savedWillChange.set(target, target.style.willChange);\n }\n }\n\n const hasExplicitFrom = !!builder.getConfig().fromVars;\n if (animStartTime > 0 && !hasExplicitFrom) {\n animation.setLazyStartCapture(true);\n }\n\n // Immediately render from state for explicit-from animations to prevent\n // FOUC (Flash of Unstyled Content). Without this, elements remain at their\n // natural CSS state for one frame before the engine's rAF loop applies\n // the from values. Rendering synchronously ensures elements are hidden\n // (e.g. opacity: 0, y: '100%' behind a mask) before the browser paints.\n // Safe: animation is paused (_isActive=false) so no callbacks fire.\n if (hasExplicitFrom) {\n animation.renderAtTime(0);\n }\n\n // For split+mask animations, the from state targets split fragments (not\n // the original parent element). If the parent has a CSS pre-hide\n // (e.g. opacity: 0), it would block the fragments from ever becoming\n // visible — children cannot be more opaque than their parent. Override\n // the parent's opacity since the mask (overflow:hidden + y offset)\n // already handles visibility of the split fragments.\n //\n // Track the original opacity so kill() can restore it — without this,\n // the inline opacity: 1 leaks across timeline rebuilds and prevents\n // the element from returning to its CSS-defined state on reset.\n if (builder.hasSplit()) {\n for (const target of builder.getOriginalTargets()) {\n if (target instanceof Element) {\n const htmlTarget = target as HTMLElement;\n if (!this._splitParentOverrides.has(htmlTarget)) {\n this._splitParentOverrides.set(htmlTarget, htmlTarget.style.opacity);\n }\n htmlTarget.style.opacity = '1';\n }\n }\n }\n\n const endTime = animStartTime + animDuration;\n if (endTime > maxEndTime) {\n maxEndTime = endTime;\n }\n }\n\n this._previousStart = position;\n this._previousEnd = maxEndTime;\n\n if (maxEndTime > this._duration) {\n this._duration = maxEndTime;\n }\n\n return this;\n }\n\n /**\n * Call function at position\n */\n call(callback: (...args: unknown[]) => void, params?: unknown[], position?: string | number): this {\n const startTime = PositionParser.parse(\n position,\n this._duration,\n this._previousStart,\n this._previousEnd\n );\n\n const timelineChild: TimelineChild = {\n type: 'callback',\n child: callback,\n startTime,\n duration: 0,\n params,\n };\n\n this._children.push(timelineChild);\n\n // Update previous tracking\n this._previousStart = startTime;\n this._previousEnd = startTime;\n\n // Update timeline duration if callback is beyond current end\n if (startTime > this._duration) {\n this._duration = startTime;\n }\n\n return this;\n }\n\n /**\n * Get the first animation's target element for trigger inference.\n * When text splitting is active, returns the original pre-split container\n * element instead of the split char/word fragments, so the scroll trigger\n * is anchored to the full element rather than a tiny character span.\n * Note: Returns only DOM Elements, not plain objects\n */\n private _getFirstAnimationTarget(): Element | undefined {\n // Prefer builder path (split-aware)\n const firstChild = this._children[0];\n if (firstChild?.type === 'animation' && firstChild.builder) {\n const builder = firstChild.builder;\n const targets = builder.hasSplit()\n ? builder.getOriginalTargets()\n : builder.getTargets();\n for (const target of targets) {\n if (isElement(target)) {\n return target;\n }\n }\n }\n\n // Fallback: use Animation's target list (no builder available)\n const firstAnimation = this.getFirstChildAnimation();\n if (firstAnimation) {\n const targets = firstAnimation.getTargets();\n for (const target of targets) {\n if (isElement(target)) {\n return target;\n }\n }\n }\n return undefined;\n }\n\n /**\n * Get all target elements from the first animation (for each mode)\n * Note: Returns only DOM Elements, not plain objects\n *\n * When the builder uses text splitting (split: 'words' etc.), the animation's\n * targets are the split fragments — but for each-mode we need the ORIGINAL\n * pre-split elements so each gets its own ScrollTrigger. The cloned builder\n * will re-split inside that element independently.\n */\n private _getAllAnimationTargets(): Element[] {\n const all: Element[] = [];\n const seen = new Set<Element>();\n const processedBuilders = new Set<AnimationBuilder>();\n\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n if (processedBuilders.has(child.builder)) continue;\n processedBuilders.add(child.builder);\n\n const targets = child.builder.hasSplit()\n ? child.builder.getOriginalTargets()\n : child.builder.getTargets();\n\n for (const target of targets) {\n if (isElement(target) && !seen.has(target as Element)) {\n seen.add(target as Element);\n all.push(target as Element);\n }\n }\n }\n }\n return all;\n }\n\n /**\n * Create a timeline instance for a single element (for each mode)\n * This clones the timeline structure but only targets the specific element.\n */\n private _createInstanceForElement(element: Element, index: number): Timeline {\n // Create a named timeline so it gets registered with Engine for ticking\n // Use parent name (if any) + unique suffix to avoid collisions\n const baseName = this._name || '__each';\n const instanceName = `${baseName}__each_${index}_${Timeline._eachCounter++}`;\n const instance = new Timeline(instanceName);\n\n const processedBuilders = new Set<AnimationBuilder>();\n\n // Clone each child animation for this specific element\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n // Skip if we already processed this builder (stagger creates multiple children from one builder)\n if (processedBuilders.has(child.builder)) continue;\n processedBuilders.add(child.builder);\n\n // Only include builders that originally targeted this specific element\n const builderElements = (child.builder.hasSplit()\n ? child.builder.getOriginalTargets()\n : child.builder.getTargets()\n ).filter((t): t is Element => isElement(t));\n if (!builderElements.includes(element)) continue;\n\n const builderConfig = child.builder.getConfig();\n\n // Build repeat config if any repeat properties are set\n const repeatConfig = builderConfig.repeat != null\n ? { times: builderConfig.repeat, delay: builderConfig.repeatDelay, yoyo: builderConfig.yoyo }\n : undefined;\n\n // When the original builder uses split, pass split + stagger to the\n // clone so each element re-splits its own text and staggers internally.\n // Without split, stagger is omitted — each element animates independently.\n const hasSplit = child.builder.hasSplit();\n\n const newBuilder = new AnimationBuilder(element, {\n from: builderConfig.fromVars,\n to: builderConfig.toVars,\n duration: builderConfig.duration,\n delay: builderConfig.delay,\n ease: builderConfig.ease,\n repeat: repeatConfig,\n ...(hasSplit && builderConfig.split ? { split: builderConfig.split } : {}),\n ...(hasSplit && builderConfig.mask ? { mask: builderConfig.mask } : {}),\n ...(hasSplit && builderConfig.stagger !== undefined ? { stagger: builderConfig.stagger } : {}),\n onStart: builderConfig.onStart,\n onUpdate: builderConfig.onUpdate,\n onComplete: builderConfig.onComplete,\n onRepeat: builderConfig.onRepeat,\n onReverseComplete: builderConfig.onReverseComplete,\n });\n if (builderConfig.axis) {\n newBuilder.setAxis(builderConfig.axis);\n }\n\n // Add to instance at same position\n instance._addBuilder(newBuilder, child.startTime);\n } else if (child.type === 'callback') {\n // Clone callbacks too\n instance.call(\n child.child as (...args: unknown[]) => void,\n child.params,\n child.startTime\n );\n }\n // Note: nested timelines not supported in each mode for now\n }\n\n return instance;\n }\n\n /**\n * Create a timeline instance for multiple elements (for each mode with explicit container target).\n * This is a sibling of _createInstanceForElement — the only difference is that it accepts an\n * Element[] and passes the array directly to AnimationBuilder instead of a single element.\n */\n private _createInstanceForElements(elements: Element[], index: number): Timeline {\n const baseName = this._name || '__each';\n const instanceName = `${baseName}__each_${index}_${Timeline._eachCounter++}`;\n const instance = new Timeline(instanceName);\n\n const processedBuilders = new Set<AnimationBuilder>();\n\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n if (processedBuilders.has(child.builder)) continue;\n processedBuilders.add(child.builder);\n\n const builderConfig = child.builder.getConfig();\n\n const repeatConfig = builderConfig.repeat != null\n ? { times: builderConfig.repeat, delay: builderConfig.repeatDelay, yoyo: builderConfig.yoyo }\n : undefined;\n\n const hasSplit = child.builder.hasSplit();\n\n const newBuilder = new AnimationBuilder(elements, {\n from: builderConfig.fromVars,\n to: builderConfig.toVars,\n duration: builderConfig.duration,\n delay: builderConfig.delay,\n ease: builderConfig.ease,\n repeat: repeatConfig,\n ...(hasSplit && builderConfig.split ? { split: builderConfig.split } : {}),\n ...(hasSplit && builderConfig.mask ? { mask: builderConfig.mask } : {}),\n ...(hasSplit && builderConfig.stagger !== undefined ? { stagger: builderConfig.stagger } : {}),\n onStart: builderConfig.onStart,\n onUpdate: builderConfig.onUpdate,\n onComplete: builderConfig.onComplete,\n onRepeat: builderConfig.onRepeat,\n onReverseComplete: builderConfig.onReverseComplete,\n });\n if (builderConfig.axis) {\n newBuilder.setAxis(builderConfig.axis);\n }\n\n instance._addBuilder(newBuilder, child.startTime);\n } else if (child.type === 'callback') {\n instance.call(\n child.child as (...args: unknown[]) => void,\n child.params,\n child.startTime\n );\n }\n // Note: nested timelines not supported in each mode for now\n }\n\n return instance;\n }\n\n /**\n * Resolve a CSS selector string or Element into an array of DOM elements.\n */\n private _resolveTargetElements(selector: string | Element): Element[] {\n if (typeof selector !== 'string') return [selector as Element];\n if (typeof document !== 'undefined') {\n try { return Array.from(document.querySelectorAll(selector)); }\n catch (e) { console.warn(`[Motion] Invalid selector \"${selector}\":`, e); }\n }\n return [];\n }\n\n /**\n * Generic helper for each-mode setup: resolve targets, create instances, register triggers.\n * The setupTrigger callback receives each (instance, element) pair for trigger-specific registration.\n */\n private _setupEachModeGeneric(\n targets: Element[],\n setupTrigger: (instance: Timeline, target: Element) => void\n ): void {\n if (targets.length === 0) return;\n\n this._eachInstances = [];\n\n // If the original builder used text splitting, the build already mutated the\n // DOM for ALL targets. Revert those splits now so each per-element clone can\n // split its own target independently from clean (original) HTML.\n const splitBuilders = this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!);\n if (splitBuilders.length > 0) {\n for (const el of targets) {\n for (const builder of splitBuilders) {\n SDKRegistry.textSplitter?.revert?.(el, builder);\n }\n }\n }\n\n // Clear transition data from the initial build.\n // Each per-element instance will independently save + manage transitions\n // for its own target during its own playback lifecycle.\n this._clearTransitionData();\n\n for (let i = 0; i < targets.length; i++) {\n const element = targets[i];\n const instance = this._createInstanceForElement(element, i);\n\n setupTrigger(instance, element);\n\n this._eachInstances.push(instance);\n }\n\n // Propagate parent callbacks to instances with active-instance tracking.\n // In each-mode, all instances receive scroll/update events simultaneously,\n // but only the \"active\" one (currently mid-animation) should report progress\n // to the builder playhead. Without this, N instances send N conflicting\n // progress values per scroll tick, causing the playhead to jump erratically.\n {\n let activeInstance: Timeline | null = null;\n const parentOnUpdate = this._onUpdate;\n const parentOnStart = this._onStart;\n const parentOnComplete = this._onComplete;\n\n for (const instance of this._eachInstances) {\n if (parentOnUpdate) {\n instance.onUpdate((progress: number, time: number) => {\n const isInProgress = progress > 0 && progress < 1;\n\n // Claim active slot when this instance is mid-animation\n if (isInProgress) {\n activeInstance = instance;\n }\n\n // Only the active instance reports progress\n if (activeInstance === instance) {\n parentOnUpdate(progress, time);\n // Release active slot when reaching an endpoint (0 or 1)\n if (!isInProgress) {\n activeInstance = null;\n }\n }\n });\n }\n\n if (parentOnStart) {\n instance.onStart(() => {\n activeInstance = instance;\n parentOnStart();\n });\n }\n\n if (parentOnComplete) {\n instance.onComplete(() => {\n if (activeInstance === instance) {\n activeInstance = null;\n parentOnComplete();\n }\n });\n }\n }\n }\n\n // Save pre-each duration for forwarding (control methods use this after children are cleared)\n this._eachDuration = this._duration;\n\n // CRITICAL: Clear the original timeline's animations so they don't compete\n // with the cloned per-element timelines. The original AnimationBuilder already\n // built PropTweens for ALL elements and rendered them (e.g., at opacity: 0).\n // If we leave them alive, they keep overwriting elements back to their FROM\n // state, causing only the first element to appear to animate. The original\n // timeline becomes just a container that holds _eachInstances references;\n // it should have NO active animations of its own.\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n // Release from timeline ownership so it can be cleaned up\n animation.setTimelineChild(false);\n animation.kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill();\n }\n }\n this._children = [];\n this._duration = 0;\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._splitParentOverrides.clear();\n\n // Reset parent state after each-mode setup\n this._isActive = false;\n this._time = 0;\n this._progress = 0;\n this._isReversed = false;\n this._startFired = false;\n }\n\n /**\n * Set up each-mode: create independent timeline instances for each target element\n */\n private _setupEachMode(\n triggerType: 'hover' | 'click' | 'scroll',\n config: HoverConfig | ClickConfig | ScrollConfig\n ): void {\n const triggerManager = SDKRegistry.triggerManager?.getInstance?.();\n if (!triggerManager) return; // TriggerManager not loaded\n\n // Strip 'each' from config to prevent recursion in per-element instances\n const { each: _, ...cleanConfig } = config as ScrollConfig & { each?: boolean };\n\n // When an explicit container target is provided, scope animation targets to each container\n if (config.target) {\n const containers = this._resolveTargetElements(config.target as string | Element);\n if (containers.length === 0) return;\n\n const allAnimTargets = this._getAllAnimationTargets();\n\n // Check if container-scoped mode applies: all animation targets must be\n // contained by at least one container. If any target is not a descendant\n // of any container, container scoping doesn't apply — fall back to\n // generic per-target mode (e.g. when trigger target and animation targets\n // are siblings, not parent-child).\n const allContained = allAnimTargets.every(t =>\n containers.some(c => c !== t && c.contains(t))\n );\n\n if (!allContained) {\n // Container scoping doesn't apply — use generic per-target each mode\n const splitBuildersA = this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!);\n if (splitBuildersA.length > 0) {\n for (const el of allAnimTargets) {\n for (const builder of splitBuildersA) {\n SDKRegistry.textSplitter?.revert?.(el, builder);\n }\n }\n }\n this._setupEachModeGeneric(allAnimTargets, (instance, element) => {\n if (triggerType === 'scroll') {\n triggerManager.registerScroll(instance, { ...cleanConfig, target: element as Element } as ScrollConfig);\n } else if (triggerType === 'hover') {\n triggerManager.registerHover(instance, { ...cleanConfig, target: element as Element } as HoverConfig);\n } else {\n triggerManager.registerClick(instance, { ...cleanConfig, target: element as Element } as ClickConfig);\n }\n });\n return;\n }\n\n // Revert text splits on animation targets so each container-scoped clone can re-split\n const splitBuildersB = this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!);\n if (splitBuildersB.length > 0) {\n for (const el of allAnimTargets) {\n for (const builder of splitBuildersB) {\n SDKRegistry.textSplitter?.revert?.(el, builder);\n }\n }\n }\n\n this._clearTransitionData();\n this._eachInstances = [];\n\n for (let i = 0; i < containers.length; i++) {\n const container = containers[i];\n const scopedTargets = allAnimTargets.filter(t => container.contains(t));\n if (scopedTargets.length === 0) continue;\n\n const instance = this._createInstanceForElements(scopedTargets, i);\n\n // Register trigger on the CONTAINER, not the animation targets\n if (triggerType === 'scroll') {\n triggerManager.registerScroll(instance, { ...cleanConfig, target: container } as ScrollConfig);\n } else if (triggerType === 'hover') {\n triggerManager.registerHover(instance, { ...cleanConfig, target: container as Element } as HoverConfig);\n } else {\n triggerManager.registerClick(instance, { ...cleanConfig, target: container as Element } as ClickConfig);\n }\n\n this._eachInstances.push(instance);\n }\n\n // Propagate parent callbacks to instances with active-instance tracking.\n // In each-mode, all instances receive scroll/update events simultaneously,\n // but only the \"active\" one (currently mid-animation) should report progress\n // to the builder playhead. Without this, N instances send N conflicting\n // progress values per scroll tick, causing the playhead to jump erratically.\n {\n let activeInstance: Timeline | null = null;\n const parentOnUpdate = this._onUpdate;\n const parentOnStart = this._onStart;\n const parentOnComplete = this._onComplete;\n\n for (const instance of this._eachInstances) {\n if (parentOnUpdate) {\n instance.onUpdate((progress: number, time: number) => {\n const isInProgress = progress > 0 && progress < 1;\n\n // Claim active slot when this instance is mid-animation\n if (isInProgress) {\n activeInstance = instance;\n }\n\n // Only the active instance reports progress\n if (activeInstance === instance) {\n parentOnUpdate(progress, time);\n // Release active slot when reaching an endpoint (0 or 1)\n if (!isInProgress) {\n activeInstance = null;\n }\n }\n });\n }\n\n if (parentOnStart) {\n instance.onStart(() => {\n activeInstance = instance;\n parentOnStart();\n });\n }\n\n if (parentOnComplete) {\n instance.onComplete(() => {\n if (activeInstance === instance) {\n activeInstance = null;\n parentOnComplete();\n }\n });\n }\n }\n }\n\n // Save pre-each duration for forwarding (control methods use this after children are cleared)\n this._eachDuration = this._duration;\n\n // CRITICAL: Clear the original timeline's animations so they don't compete\n // with the cloned per-container timelines.\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n animation.setTimelineChild(false);\n animation.kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill();\n }\n }\n this._children = [];\n this._duration = 0;\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._splitParentOverrides.clear();\n\n // Reset parent state after each-mode setup\n this._isActive = false;\n this._time = 0;\n this._progress = 0;\n this._isReversed = false;\n this._startFired = false;\n return;\n }\n\n const targets = this._getAllAnimationTargets();\n\n this._setupEachModeGeneric(targets, (instance, element) => {\n if (triggerType === 'scroll') {\n triggerManager.registerScroll(instance, { ...cleanConfig, target: element as Element } as ScrollConfig);\n } else if (triggerType === 'hover') {\n triggerManager.registerHover(instance, { ...cleanConfig, target: element as Element } as HoverConfig);\n } else {\n triggerManager.registerClick(instance, { ...cleanConfig, target: element as Element } as ClickConfig);\n }\n });\n }\n\n /**\n * Trigger timeline on hover (mouseenter plays, mouseleave configurable)\n */\n onHover(config?: HoverConfig): this {\n if (config?.each) {\n this._setupEachMode('hover', config);\n return this;\n }\n\n const target = config?.target ?? this._getFirstAnimationTarget();\n SDKRegistry.triggerManager?.getInstance?.().registerHover(this, {\n target,\n onLeave: config?.onLeave,\n leaveDelay: config?.leaveDelay,\n });\n return this;\n }\n\n /**\n * Trigger timeline on click\n */\n onClick(config?: ClickConfig): this {\n if (config?.each) {\n this._setupEachMode('click', config);\n return this;\n }\n\n const target = config?.target ?? this._getFirstAnimationTarget();\n SDKRegistry.triggerManager?.getInstance?.().registerClick(this, {\n target,\n secondTarget: config?.secondTarget,\n toggle: config?.toggle,\n preventDefault: config?.preventDefault,\n });\n return this;\n }\n\n /**\n * Trigger timeline on scroll\n */\n onScroll(config?: ScrollConfig): this {\n if (config?.each) {\n this._setupEachMode('scroll', config);\n return this;\n }\n\n const target = config?.target ?? this._getFirstAnimationTarget();\n SDKRegistry.triggerManager?.getInstance?.().registerScroll(this, { ...config, target });\n return this;\n }\n\n /**\n * Trigger timeline on mouse movement\n * Drives animation progress based on mouse position (axis mode) or distance from center (distance mode)\n */\n onMouseMove(config?: MouseMoveConfig): this {\n // Handle each mode for mouse move (only with target)\n if (config?.each && config.target) {\n this._setupMouseMoveEachMode(config);\n return this;\n }\n\n SDKRegistry.triggerManager?.getInstance?.().registerMouseMove(this, {\n type: config?.type ?? 'distance',\n target: config?.target,\n smooth: config?.smooth,\n startProgress: config?.startProgress,\n leaveProgress: config?.leaveProgress,\n });\n return this;\n }\n\n /**\n * Set up each-mode for mouse movement: create independent timeline instances for each target element\n */\n private _setupMouseMoveEachMode(config: MouseMoveConfig): void {\n if (!config.target) return;\n\n // Resolve target elements\n let targets: Element[] = [];\n if (typeof config.target === 'string') {\n if (typeof document !== 'undefined') {\n targets = Array.from(document.querySelectorAll(config.target));\n }\n } else {\n targets = [config.target as Element];\n }\n\n const triggerManager = SDKRegistry.triggerManager?.getInstance?.();\n if (!triggerManager) return; // TriggerManager not loaded\n\n this._setupEachModeGeneric(targets, (instance, element) => {\n triggerManager.registerMouseMove(instance, {\n type: config.type ?? 'distance',\n target: element,\n smooth: config.smooth,\n startProgress: config.startProgress,\n leaveProgress: config.leaveProgress,\n });\n });\n }\n\n /**\n * Trigger timeline on page load.\n *\n * @param config - Optional configuration\n * @param config.timing - When to play: 'before' (immediately), 'during' (DOMContentLoaded), 'after' (window load). Defaults to 'during'.\n * @param config.paused - When true, the timeline is built but NOT played automatically.\n * Start it manually with `Motion('timelineName').play()`.\n */\n onPageLoad(config?: { timing?: 'before' | 'during' | 'after'; paused?: boolean }): this {\n SDKRegistry.triggerManager?.getInstance?.().registerPageLoad(this, config || {});\n return this;\n }\n\n /**\n * Trigger timeline when user navigates away from the page (clicks a link).\n * Intercepts the click, plays the exit animation, then navigates to the target URL.\n */\n onPageExit(config?: PageExitConfig): this {\n SDKRegistry.triggerManager?.getInstance?.().registerPageExit(this, config || {});\n return this;\n }\n\n /**\n * Trigger timeline based on user gestures (pointer, touch, wheel, scroll)\n * Maps directional callbacks to timeline actions\n */\n onGesture(config: GestureConfig): this {\n // Handle each mode for gesture\n if (config.each) {\n this._setupGestureEachMode(config);\n return this;\n }\n\n SDKRegistry.triggerManager?.getInstance?.().registerGesture(this, config);\n return this;\n }\n\n /**\n * Set up each-mode for gesture: create independent instances for each target element\n */\n private _setupGestureEachMode(config: GestureConfig): void {\n // Resolve target elements\n let targets: Element[] = [];\n\n if (config.target) {\n if (typeof config.target === 'string') {\n if (typeof document !== 'undefined') {\n targets = Array.from(document.querySelectorAll(config.target));\n }\n } else {\n targets = [config.target as Element];\n }\n } else {\n // If no target specified, use animation targets\n targets = this._getAllAnimationTargets();\n }\n\n // First pass: create all instances (using generic helper with a no-op trigger setup)\n this._setupEachModeGeneric(targets, () => {});\n\n if (!this._eachInstances || this._eachInstances.length === 0) return;\n\n // Second pass: register triggers with sibling info for playNext/playPrevious\n const triggerManager = SDKRegistry.triggerManager?.getInstance?.();\n if (!triggerManager) return; // TriggerManager not loaded\n\n for (let i = 0; i < targets.length; i++) {\n const element = targets[i];\n const instance = this._eachInstances[i];\n\n triggerManager.registerGesture(instance, {\n ...config,\n target: element,\n each: false, // Prevent recursion\n _siblings: this._eachInstances, // Pass all siblings for sequence control\n _index: i, // Current index in sequence\n } as GestureConfigInternal);\n }\n }\n\n /**\n * Attach cursor behavior to this timeline's target element\n * Creates a custom cursor that follows mouse with smooth tracking and state-based animations\n */\n onCursor(config: CursorConfig): this {\n SDKRegistry.triggerManager?.getInstance?.().registerCursor(this, config);\n return this;\n }\n\n /**\n * Update a callback child: fire when time crosses its position\n */\n private _updateCallbackChild(child: TimelineChild, prevTime: number, deltaTime: number): void {\n const childStartTime = child.startTime;\n\n // Execute callback when time crosses its position (from either direction)\n // or when seeking directly to the callback position\n const crossedForward = prevTime < childStartTime && this._time >= childStartTime;\n const crossedBackward = prevTime > childStartTime && this._time <= childStartTime;\n const atPosition = Math.abs(this._time - childStartTime) < 0.001; // Within 1ms\n\n if (crossedForward || crossedBackward || (atPosition && deltaTime === 0)) {\n const callback = child.child as (...args: unknown[]) => void;\n const params = child.params || [];\n callback(...params);\n }\n }\n\n /**\n * Update an animation child: seek/activate/deactivate based on parent time\n */\n private _updateAnimationChild(child: TimelineChild, deltaTime: number): void {\n const animation = child.child as Animation;\n const timeScale = Math.abs(animation.getTimeScale()) || 1;\n const scaledDuration = animation.totalDuration() / timeScale;\n\n // Calculate local time within the animation\n const localTime = this._time - child.startTime;\n const scaledLocalTime = localTime * timeScale;\n\n if (localTime >= 0 && localTime <= scaledDuration) {\n // Animation should be active\n if (!animation.isActive()) {\n // Capture start values from current element state (for chained animations)\n animation.captureStartValues();\n animation.play();\n }\n\n // Seek animation to local time\n animation.renderAtTime(scaledLocalTime);\n } else if (localTime < 0) {\n // Animation hasn't started yet\n // Only render at start state if it has explicit FROM values (not lazy capture)\n // Lazy-capture animations should NEVER render before they start,\n // even after values have been captured, to avoid overwriting earlier animations\n if (!animation.isLazyCapture()) {\n animation.renderAtTime(0);\n }\n if (animation.isActive()) {\n animation.pause();\n // Fire onReverseComplete when an active animation exits the start boundary\n // during reverse playback. Also resets _startFired/_completeFired for next play.\n animation.fireReverseComplete();\n }\n } else if (animation.isActive()) {\n // Animation past its end - pause at final state\n animation.renderAtTime(animation.totalDuration());\n animation.pause();\n } else if (deltaTime === 0) {\n // Seeking past animation end - render at final state\n animation.renderAtTime(animation.totalDuration());\n }\n }\n\n /**\n * Update a nested timeline child: seek/activate/deactivate based on parent time\n */\n private _updateTimelineChild(child: TimelineChild, deltaTime: number): void {\n const timeline = child.child as Timeline;\n const actualDuration = timeline.duration();\n\n // Calculate local time within the timeline\n const localTime = this._time - child.startTime;\n\n if (localTime >= 0 && localTime <= actualDuration) {\n // Timeline should be active\n if (!timeline.isActive()) {\n timeline.play();\n }\n\n // Seek timeline to local time\n timeline.time(localTime);\n } else if (timeline.isActive()) {\n // Timeline should not be active - pause it at boundary\n if (localTime < 0) {\n timeline.time(0);\n timeline.pause();\n } else {\n timeline.time(actualDuration);\n timeline.pause();\n }\n } else if (deltaTime === 0) {\n // Only render boundary state when SEEKING (not during normal playback)\n if (localTime < 0) {\n // Timeline hasn't started yet - don't render during seek\n } else {\n timeline.time(actualDuration);\n }\n }\n // During normal playback, don't render inactive timelines outside their range\n }\n\n /**\n * Update timeline by delta time\n */\n update(deltaTime: number): void {\n // In each-mode, forward updates to instances\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.update(deltaTime);\n return;\n }\n\n // Store previous time before updating\n const prevTime = this._time;\n\n if (!this._isActive && deltaTime === 0) {\n // This is a seek operation, still need to update children\n } else if (!this._isActive) {\n return;\n }\n\n // Apply time scale\n const scaledDelta = deltaTime * this._timeScale;\n\n // Handle repeat delay countdown (real playback only, not seeking)\n if (this._inRepeatDelay && deltaTime > 0) {\n this._repeatDelayCounter -= scaledDelta;\n if (this._repeatDelayCounter > 0) {\n // Still waiting out the repeat delay — don't advance time or update children\n return;\n }\n // Delay just finished: set up for the incoming cycle\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n if (this._isReversed) {\n // Yoyo: about to start the reverse pass — position at end and reset child flags\n this._time = this._duration;\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n } else {\n // Non-yoyo (or yoyo returning to forward): start from the beginning\n this._time = 0;\n this._startFired = false;\n this._resetChildrenForCycle();\n }\n // Fall through to run progress + children update for this frame\n } else if (!this._inRepeatDelay) {\n // Normal time advancement\n this._time += this._isReversed ? -scaledDelta : scaledDelta;\n\n // Cycle boundary detection — only during real playback, not seeking\n if (deltaTime > 0) {\n if (!this._isReversed && this._time >= this._duration && this._repeat !== 0) {\n // Forward cycle just ended\n const hasMoreRepeats = this._repeat === -1 || this._currentRepeat < this._repeat;\n if (hasMoreRepeats) {\n this._time = this._duration;\n this._currentRepeat++;\n try { this._onRepeat?.(this._currentRepeat); } catch (e) { console.error('[Motion] onRepeat callback error:', e); }\n\n if (this._yoyo) {\n // Flip to reverse; reset child callback flags so their onStart/onComplete re-fire\n this._isReversed = true;\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n } else {\n // Non-yoyo: restore elements and restart from beginning\n this._time = 0;\n this._startFired = false;\n this._resetChildrenForCycle();\n }\n\n if (this._repeatDelay > 0) {\n this._inRepeatDelay = true;\n this._repeatDelayCounter = this._repeatDelay;\n }\n }\n // When hasMoreRepeats is false, fall through to clamping + completion check below\n } else if (this._isReversed && this._time <= 0 && this._yoyo && this._repeat !== 0) {\n // Yoyo reverse cycle just ended (time hit 0)\n const hasMoreRepeats = this._repeat === -1 || this._currentRepeat < this._repeat;\n if (hasMoreRepeats) {\n this._time = 0;\n this._currentRepeat++;\n try { this._onRepeat?.(this._currentRepeat); } catch (e) { console.error('[Motion] onRepeat callback error:', e); }\n\n // Flip back to forward\n this._isReversed = false;\n this._startFired = false;\n this._resetChildrenForCycle();\n\n if (this._repeatDelay > 0) {\n this._inRepeatDelay = true;\n this._repeatDelayCounter = this._repeatDelay;\n }\n }\n // When hasMoreRepeats is false, fall through to clamping + completion check below\n }\n }\n }\n\n // Clamp time to current cycle bounds\n if (this._time < 0) {\n this._time = 0;\n } else if (this._time > this._duration) {\n this._time = this._duration;\n }\n\n // Update progress\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n\n // Fire onStart callback on first update\n // Use _time > 0 instead of _progress > 0 because _progress = _time / _duration\n // is always 0 when _duration is Infinity (repeat: -1), preventing onStart from\n // ever firing for infinite-repeat timelines.\n if (!this._startFired && this._isActive && this._time > 0) {\n this._startFired = true;\n // Disable CSS transitions during active playback to prevent interference\n this._disableTransitions();\n // Apply will-change: transform now (deferred from build time for layout safety)\n this._applyWillChange();\n this._onStart?.();\n }\n\n // Fire onUpdate callback\n if (this._isActive) {\n this._onUpdate?.(this._progress, this._time);\n }\n\n // Update children - dispatch to type-specific helpers\n for (const child of this._children) {\n if (child.type === 'callback') {\n this._updateCallbackChild(child, prevTime, deltaTime);\n } else if (child.type === 'animation') {\n this._updateAnimationChild(child, deltaTime);\n } else if (child.type === 'timeline') {\n this._updateTimelineChild(child, deltaTime);\n }\n }\n\n // Check if complete: fires only when all cycles are done\n if (!this._completeFired) {\n const atForwardEnd = !this._isReversed && this._time >= this._duration;\n // Reverse completion: either a non-yoyo reverse (user called reverse()) or final yoyo cycle\n const atReverseStart = this._isReversed && this._time <= 0;\n const noMoreRepeats = this._repeat !== -1 && this._currentRepeat >= this._repeat;\n\n if (atForwardEnd && (this._repeat === 0 || noMoreRepeats)) {\n this._completeFired = true;\n this._isActive = false;\n this._startFired = false; // Reset so next play/reverse fires onStart\n // Restore CSS transitions now that animation is done.\n // If the timeline replays (hover, restart), _disableTransitions re-fires on next onStart.\n this._restoreTransitions();\n this._onComplete?.();\n } else if (atReverseStart && (this._repeat === 0 || (this._yoyo && noMoreRepeats))) {\n this._completeFired = true;\n this._isActive = false;\n this._startFired = false; // Reset so next play/reverse fires onStart\n this._restoreTransitions();\n this._onComplete?.();\n }\n }\n }\n\n // Control methods\n\n /**\n * Play timeline from position.\n * If already playing forward, continues from current position.\n * No-op if already completed forward (at the end) and no `from` is specified —\n * prevents spurious onStart/onComplete re-firing during continuous gesture events.\n */\n play(from?: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot play a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.play(from);\n return this;\n }\n\n if (from !== undefined) {\n this._time = from;\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n // Reset repeat state when restarting from beginning\n if (from === 0) {\n this._currentRepeat = 0;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n }\n } else if (this._time >= this._duration && !this._isReversed) {\n // Already at the end going forward — nothing to play.\n // Silently skip to avoid re-firing onStart/onComplete and\n // toggling CSS transitions on every frame (e.g. continuous scroll gestures).\n return this;\n }\n\n // Allow completion to fire again on the next play-through\n this._completeFired = false;\n this._isReversed = false;\n this._isActive = true;\n this._atZeroState = false;\n\n return this;\n }\n\n /**\n * Pause timeline at time\n */\n pause(atTime?: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot pause a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.pause(atTime);\n return this;\n }\n\n if (atTime !== undefined) {\n this._time = atTime;\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n }\n\n this._isActive = false;\n\n // Pause all children\n for (const child of this._children) {\n if (child.type !== 'callback') {\n const animation = child.child as Animation | Timeline;\n if ('pause' in animation) {\n animation.pause();\n }\n }\n }\n\n return this;\n }\n\n /**\n * Reverse timeline from position.\n * If already reversing, continues from current position.\n * No-op if already completed reverse (at the start) and no `from` is specified —\n * prevents spurious onStart/onComplete re-firing during continuous gesture events.\n */\n reverse(from?: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot reverse a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.reverse(from);\n return this;\n }\n\n if (from !== undefined) {\n this._time = from;\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n } else if (this._time <= 0 && this._isReversed) {\n // Already at the start going reverse — nothing to reverse.\n // Silently skip to avoid re-firing onStart/onComplete and\n // toggling CSS transitions on every frame (e.g. continuous scroll gestures).\n return this;\n }\n\n // Allow completion to fire again on the next reverse pass\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n this._isReversed = true;\n this._isActive = true;\n this._atZeroState = false;\n\n return this;\n }\n\n /**\n * Restart timeline\n */\n restart(): this {\n if (this._killed) {\n console.warn('[Motion] Cannot restart a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.restart();\n return this;\n }\n\n this._time = 0;\n this._progress = 0;\n this._isActive = true;\n this._isReversed = false;\n this._startFired = false;\n this._atZeroState = false;\n // Reset repeat state for a clean replay from the beginning\n this._currentRepeat = 0;\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n\n // Reset callback flags for clean replay\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n\n // Restore all properties to initial values, then render position-0 animations\n this._restoreInitialValues();\n this._renderPositionZeroAnimations();\n\n return this;\n }\n\n /**\n * Reset all children for the start of a new repeat cycle.\n * Restores elements to their captured initial values and re-renders\n * position-0 animations at their FROM state.\n */\n private _resetChildrenForCycle(): void {\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n this._restoreInitialValues();\n this._renderPositionZeroAnimations();\n }\n\n /**\n * Render only position-0 (non-lazy) animations at their start state\n */\n private _renderPositionZeroAnimations(): void {\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n // Only render non-lazy animations (they have explicit FROM or are at position 0)\n if (!animation.isLazyCapture()) {\n animation.renderAtTime(0);\n }\n if (animation.isActive()) {\n animation.pause();\n }\n } else if (child.type === 'timeline') {\n (child.child as Timeline).progress(0);\n }\n }\n }\n\n /**\n * Seek to position\n */\n seek(position: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot seek a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.seek(position);\n return this;\n }\n\n this._time = Math.max(0, Math.min(position, this._duration));\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n this._atZeroState = false;\n\n // Update all children to match seek position\n this.update(0);\n\n return this;\n }\n\n /**\n * Set progress for animations bound to a specific axis (for mouse movement trigger)\n * Only affects animations that have .axis() set to the specified axis.\n * @param axis - 'x' or 'y'\n * @param value - Progress value (0-1)\n */\n setAxisProgress(axis: 'x' | 'y', value: number): this {\n const clampedValue = Math.max(0, Math.min(1, value));\n\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n const builderAxis = child.builder.getConfig().axis;\n if (builderAxis === axis) {\n const animation = child.child as Animation;\n const duration = animation.totalDuration();\n\n // Ensure animation is initialized (capture start values for TO-only animations)\n if (!animation.isActive()) {\n animation.captureStartValues();\n animation.play();\n }\n\n // Set animation time based on progress\n animation.renderAtTime(clampedValue * duration);\n }\n }\n }\n\n return this;\n }\n\n /**\n * Set callback for when timeline starts playing\n */\n onStart(callback: () => void): this {\n this._onStart = callback;\n return this;\n }\n\n /**\n * Set callback for progress updates (called every frame while active)\n * @param callback - Receives (progress: 0-1, time: seconds)\n */\n onUpdate(callback: (progress: number, time: number) => void): this {\n this._onUpdate = callback;\n return this;\n }\n\n /**\n * Set callback for when timeline completes\n */\n onComplete(callback: () => void): this {\n this._onComplete = callback;\n return this;\n }\n\n /**\n * Set callback for each repeat cycle completion\n * @param callback - Receives the current repeat count (starts at 1)\n */\n onRepeat(callback: (repeatCount: number) => void): this {\n this._onRepeat = callback;\n return this;\n }\n\n /**\n * Configure timeline-level repeat behavior (fluent).\n * Use -1 for infinite repeat, or a RepeatConfig for advanced options.\n * @example\n * Motion('loop', '.box', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 0.5 })\n * .withRepeat(-1)\n * .onPageLoad()\n *\n * Motion('bounce', [entries])\n * .withRepeat({ times: 3, yoyo: true, delay: 0.2 })\n * .onPageLoad()\n */\n withRepeat(config: number | RepeatConfig): this {\n if (typeof config === 'number') {\n this._repeat = config;\n this._repeatDelay = 0;\n this._yoyo = false;\n } else {\n this._repeat = config.times;\n this._repeatDelay = config.delay ?? 0;\n this._yoyo = config.yoyo ?? false;\n }\n return this;\n }\n\n /**\n * Get or set progress (0-1)\n */\n progress(): number;\n progress(value: number): this;\n progress(value?: number): number | this {\n if (value === undefined) {\n // Getter: aggregate from each instances if in each-mode\n if (this._eachInstances && this._eachInstances.length > 0) {\n let maxProgress = 0;\n for (const inst of this._eachInstances) {\n const p = inst.progress();\n if (p > maxProgress) maxProgress = p;\n }\n return maxProgress;\n }\n return this._progress;\n }\n\n // Setter\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.progress(value);\n return this;\n }\n\n const clampedValue = Math.max(0, Math.min(1, value));\n\n // Disable CSS transitions on first non-zero progress set.\n // For scrub-driven timelines (ScrollTrigger with scrub), play() is never\n // called so _isActive stays false and the normal _disableTransitions() in\n // update() never fires. Without this, CSS transitions on animated elements\n // fight with frame-by-frame scrub rendering, causing elements to lag or\n // get stuck in incorrect states during rapid scrub direction changes.\n if (!this._transitionsDisabled && this._savedTransitions.size > 0) {\n this._disableTransitions();\n this._applyWillChange();\n }\n\n // Reset callback flags when seeking to beginning\n if (clampedValue === 0) {\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n }\n\n this._time = clampedValue * this._duration;\n this._progress = clampedValue;\n\n if (clampedValue === 0) {\n // When progress is set to 0, restore initial values and render from state.\n // OPTIMIZATION: Skip the expensive restore+render cycle if we've already\n // rendered at position 0 and haven't moved away since. The restore path\n // clears the transform cache, and re-rendering re-hydrates it via\n // getComputedStyle() — causing massive layout thrashing when called every\n // frame (e.g., scroll scrub for headings not yet in view).\n if (this._atZeroState) {\n // Already rendered at 0 and haven't moved — skip\n } else {\n this._restoreInitialValues();\n this._renderPositionZeroAnimations();\n this._atZeroState = true;\n }\n } else {\n this._atZeroState = false;\n this.update(0);\n }\n\n // Fire onUpdate callback when progress is explicitly set (e.g., scroll scrubbing)\n // This ensures playhead sync even when timeline isn't \"active\"\n this._onUpdate?.(this._progress, this._time);\n\n return this;\n }\n\n /**\n * Get or set time in seconds\n */\n time(): number;\n time(value: number): this;\n time(value?: number): number | this {\n if (value === undefined) {\n if (this._eachInstances && this._eachInstances.length > 0) {\n let maxTime = 0;\n for (const inst of this._eachInstances) {\n const t = inst.time();\n if (t > maxTime) maxTime = t;\n }\n return maxTime;\n }\n return this._time;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.time(value);\n return this;\n }\n\n this._time = Math.max(0, Math.min(value, this._duration));\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n\n // Update all children\n this.update(0);\n\n return this;\n }\n\n /**\n * Get or set time scale (speed multiplier)\n */\n timeScale(): number;\n timeScale(value: number): this;\n timeScale(value?: number): number | this {\n if (value === undefined) {\n return this._timeScale;\n }\n\n this._timeScale = value;\n\n return this;\n }\n\n /**\n * Kill timeline and all children, reset all state\n * @param clearProps - If true (default), restore elements to their initial CSS values\n */\n kill(clearProps: boolean = true): void {\n this._killed = true;\n this._isActive = false;\n this._atZeroState = false;\n\n // Clean up attached triggers (including page load)\n SDKRegistry.triggerManager?.getInstance?.().unregister(this);\n\n // Clean up each-mode instances\n if (this._eachInstances) {\n for (const instance of this._eachInstances) {\n instance.kill(clearProps);\n }\n this._eachInstances = undefined;\n }\n\n // Restore CSS transitions and clear saved data before restoring initial values\n this._clearTransitionData();\n\n // Restore initial CSS values BEFORE killing animations\n // This resets elements to their state before the timeline was created\n // Check both _initialValues (CSS props) AND _transformElements (transform props like rotation from path)\n if (clearProps && (this._initialValues.size > 0 || this._transformElements.size > 0)) {\n this._restoreInitialValues();\n }\n\n // Restore parent opacity overrides from split+mask animations.\n // Without this, the inline opacity: 1 set for split text visibility\n // leaks across timeline rebuilds, preventing proper CSS state restoration.\n if (clearProps && this._splitParentOverrides.size > 0) {\n for (const [element, originalOpacity] of this._splitParentOverrides) {\n if (originalOpacity) {\n element.style.opacity = originalOpacity;\n } else {\n element.style.removeProperty('opacity');\n }\n }\n this._splitParentOverrides.clear();\n }\n\n // Release and kill all child animations (returns them to pool)\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n // Release from timeline ownership so it can be pooled\n animation.setTimelineChild(false);\n animation.kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill(clearProps);\n }\n }\n\n // Unregister from Engine if this is a named timeline\n if (this._unregisterCallback) {\n this._unregisterCallback();\n this._unregisterCallback = undefined;\n }\n\n // Reset all state so timeline can be reused cleanly\n this._children = [];\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._savedTransitions.clear();\n this._savedWillChange.clear();\n this._splitParentOverrides.clear();\n this._willChangeApplied = false;\n this._transitionsDisabled = false;\n this._duration = 0;\n this._time = 0;\n this._progress = 0;\n this._previousStart = 0;\n this._previousEnd = 0;\n this._isReversed = false;\n this._startFired = false;\n // Reset repeat state\n this._currentRepeat = 0;\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n }\n\n /**\n * Check if timeline is currently active\n */\n isActive(): boolean {\n if (this._eachInstances) {\n return this._eachInstances.some(inst => inst.isActive());\n }\n return this._isActive;\n }\n\n /**\n * Check if timeline is currently playing in reverse direction\n */\n reversed(): boolean {\n if (this._eachInstances) {\n return this._eachInstances.some(inst => inst.reversed());\n }\n return this._isReversed;\n }\n\n /**\n * Get timeline name\n */\n getName(): string | undefined {\n return this._name;\n }\n\n /**\n * Get first child animation (for trigger target resolution).\n * @internal\n */\n getFirstChildAnimation(): Animation | null {\n const firstChild = this._children[0];\n if (firstChild?.type === 'animation') {\n return firstChild.child as Animation;\n }\n return null;\n }\n\n /**\n * Get first child builder (for split-aware trigger target resolution).\n * When text splitting is active, the builder holds original pre-split targets.\n * @internal\n */\n getFirstChildBuilder(): AnimationBuilder | null {\n const firstChild = this._children[0];\n if (firstChild?.type === 'animation' && firstChild.builder) {\n return firstChild.builder;\n }\n return null;\n }\n}\n\n\n",
|
|
32
|
+
"/**\n * Timeline for sequencing animations\n *\n * Features:\n * - Named timeline registry (create/retrieve by name)\n * - Position parameter support (absolute, +=, -=, <, >)\n * - Nested timelines\n * - Callback scheduling\n * - Control methods (play, pause, reverse, seek, etc.)\n * - Trigger system integration\n */\n\nimport type {\n HoverConfig,\n ClickConfig,\n ScrollConfig,\n MouseMoveConfig,\n GestureConfig,\n GestureConfigInternal,\n CursorConfig,\n PageExitConfig,\n TargetInput,\n AnimationConfig,\n TimelineConfig,\n RepeatConfig,\n} from '../types';\nimport { Animation } from './Animation';\nimport { AnimationBuilder } from './AnimationBuilder';\nimport { PositionParser } from '../utils/PositionParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { isElement } from '../utils/TargetResolver';\nimport { getCurrentValue, getOriginalCSSValueWithUnit, isTransformProp, isColorProp, isFilterProp, isPathProp, isDrawSVGProp } from '../utils/PropertyParser';\nimport { setTransformValue, buildTransformString, clearTransformCache } from '../render/TransformCache';\n\ninterface TimelineChild {\n type: 'animation' | 'timeline' | 'callback';\n child: Animation | Timeline | ((...args: unknown[]) => void);\n builder?: AnimationBuilder; // Store builder for each-mode cloning\n startTime: number;\n duration: number;\n params?: unknown[];\n}\n\nexport class Timeline {\n /** Monotonic counter for generating unique each-mode instance names */\n private static _eachCounter = 0;\n\n /**\n * Static callback for registering named timelines with Engine.\n * Set by Engine.getInstance() to break circular dependency.\n * @internal\n */\n private static _registerWithEngine: ((name: string, timeline: Timeline) => void) | null = null;\n\n /**\n * Set the engine registration callback. Called once by Engine during initialization.\n * @internal\n */\n static setEngineRegisterCallback(callback: (name: string, timeline: Timeline) => void): void {\n Timeline._registerWithEngine = callback;\n }\n\n private _name?: string;\n private _children: TimelineChild[] = [];\n private _duration: number = 0;\n private _time: number = 0;\n private _progress: number = 0;\n private _timeScale: number = 1;\n private _isActive: boolean = false;\n private _isReversed: boolean = false;\n private _killed: boolean = false;\n // Tracks whether the timeline has been rendered at position 0 and is still there.\n // Used to skip the expensive _restoreInitialValues() + _renderPositionZeroAnimations()\n // path when progress(0) is called repeatedly (e.g., scroll scrub for headings not yet\n // in view). Without this, each call clears the transform cache and re-hydrates it via\n // getComputedStyle(), causing massive layout thrashing.\n private _atZeroState: boolean = false;\n\n // Repeat support\n private _repeat: number = 0;\n private _repeatDelay: number = 0;\n private _yoyo: boolean = false;\n private _currentRepeat: number = 0;\n private _completeFired: boolean = false;\n private _inRepeatDelay: boolean = false;\n private _repeatDelayCounter: number = 0;\n\n // Callbacks\n private _onStart?: () => void;\n private _onUpdate?: (progress: number, time: number) => void;\n private _onComplete?: () => void;\n private _onRepeat?: (repeatCount: number) => void;\n private _startFired: boolean = false;\n\n /**\n * Reference to attached trigger (for cleanup in kill()).\n * @internal\n */\n _trigger?: { disable(): void };\n\n /** @internal */\n private _unregisterCallback?: () => void;\n\n /**\n * Set unregister callback for cleanup. Called by Engine after registration.\n * @internal\n */\n setUnregisterCallback(callback: () => void): void {\n this._unregisterCallback = callback;\n }\n\n // Track previous child for position parsing\n private _previousStart: number = 0;\n private _previousEnd: number = 0;\n\n // Store cloned timelines for each-mode (independent per-element triggers)\n private _eachInstances?: Timeline[];\n private _eachDuration?: number; // Preserves pre-each duration for forwarding\n\n // Initial property values per element (captured once at build time, restored on seek to 0)\n // For regular CSS: stores value/unit OR a flag to just remove inline style\n // For transforms: we just track that the element was animated (will clear cache on restore)\n private _initialValues: Map<Element, Map<string, { \n removeInline: boolean; // If true, just remove inline style (lets stylesheet auto/calc/clamp take over)\n value?: number; \n unit?: string;\n }>> = new Map();\n\n // Track original inline transform values for restoration\n private _initialTransforms: Map<Element, string | null> = new Map();\n\n // Track elements that have transform animations (for cache cleanup)\n private _transformElements: Set<Element> = new Set();\n\n // Original inline transition values per animated element. Populated at build time.\n // During active playback, transitions are overridden with 'none' to prevent CSS\n // transitions from interfering with frame-by-frame SDK rendering.\n // Disabled on timeline start, restored on completion and kill.\n private _savedTransitions: Map<HTMLElement, string> = new Map();\n private _transitionsDisabled: boolean = false;\n\n // GPU layer promotion is handled via force3D (translateZ) in TransformCache,\n // matching GSAP's approach. will-change: transform is NOT used because it\n // pre-rasterizes elements as bitmaps at their original size, causing blurry\n // text when scaled up (e.g. scale(10) stretches a 1x bitmap).\n\n // Track parent elements whose opacity was overridden for split+mask animations.\n // When split text is used, the parent's CSS pre-hide (e.g. opacity: 0) would block\n // fragments from being visible. We override with opacity: 1, but must restore the\n // original value on kill/reset to prevent state leaks across timeline rebuilds.\n private _splitParentOverrides: Map<HTMLElement, string> = new Map();\n\n\n constructor(name?: string, config?: TimelineConfig) {\n this._name = name;\n\n // Parse repeat config\n if (config?.repeat !== undefined) {\n if (typeof config.repeat === 'number') {\n this._repeat = config.repeat;\n } else {\n this._repeat = config.repeat.times;\n this._repeatDelay = config.repeat.delay ?? 0;\n this._yoyo = config.repeat.yoyo ?? false;\n }\n }\n\n // Apply callbacks from config\n if (config?.onStart) this._onStart = config.onStart;\n if (config?.onUpdate) this._onUpdate = config.onUpdate;\n if (config?.onComplete) this._onComplete = config.onComplete;\n if (config?.onRepeat) this._onRepeat = config.onRepeat;\n\n // Register named timeline with Engine via callback (avoids circular dependency)\n if (name && Timeline._registerWithEngine) {\n Timeline._registerWithEngine(name, this);\n }\n }\n\n /**\n * Get timeline duration (single cycle)\n */\n duration(): number {\n if (this._eachDuration !== undefined) {\n return this._eachDuration;\n }\n return this._duration;\n }\n\n /**\n * Get total duration including all repeats.\n * Returns Infinity when repeat is -1 (infinite loop).\n */\n totalDuration(): number {\n if (this._repeat === -1) return Infinity;\n return this._duration * (this._repeat + 1) + this._repeatDelay * this._repeat;\n }\n\n /**\n * Clear timeline state for rebuild (used for re-triggerable animations)\n * Kills existing animations, clears children, resets state\n */\n clear(): this {\n // Kill existing animations\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill();\n }\n }\n\n // Reset state\n this._children = [];\n this._duration = 0;\n this._time = 0;\n this._progress = 0;\n this._isActive = false;\n this._isReversed = false;\n this._startFired = false;\n this._previousStart = 0;\n this._previousEnd = 0;\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._splitParentOverrides.clear();\n this._clearTransitionData();\n // Reset repeat state\n this._currentRepeat = 0;\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n\n return this;\n }\n\n /**\n * Capture initial values for an animation's properties (called once per property per element)\n */\n private _captureInitialValues(animation: Animation): void {\n const targets = animation.getTargets();\n // Access PropTweens to get property names\n let propTween = animation.getFirstPropTween();\n\n while (propTween) {\n const property = propTween.property;\n\n for (const target of targets) {\n if (!(target instanceof Element)) continue;\n\n // Get or create property map for this element\n let propMap = this._initialValues.get(target);\n if (!propMap) {\n propMap = new Map();\n this._initialValues.set(target, propMap);\n }\n\n // Only capture if not already recorded (first animation for this property wins)\n if (!propMap.has(property)) {\n const htmlEl = target as HTMLElement;\n \n if (isTransformProp(property) || isPathProp(property)) {\n // Transform properties AND path property: track that this element has transforms\n // Path animations modify x, y, and optionally rotate transforms\n // We'll clear the entire transform cache on restore\n this._transformElements.add(target);\n if (!this._initialTransforms.has(target)) {\n const inlineTransform = htmlEl.style.transform;\n this._initialTransforms.set(target, inlineTransform || null);\n }\n // Still add to propMap so we know to restore this element\n propMap.set(property, { removeInline: true });\n } else if (isColorProp(property) || isFilterProp(property)) {\n // Color and filter properties: always remove inline style on restore\n // These are strings (not numeric) - just let stylesheet take over\n propMap.set(property, { removeInline: true });\n } else if (isDrawSVGProp(property)) {\n // drawSVG is a virtual property — it sets stroke-dasharray and\n // stroke-dashoffset on SVG elements. Flag for special restore.\n propMap.set(property, { removeInline: true });\n } else {\n // Regular CSS properties (numeric values with units)\n const kebabProp = property.replace(/([A-Z])/g, '-$1').toLowerCase();\n const hasInlineStyle = htmlEl.style.getPropertyValue(kebabProp) !== '';\n \n if (hasInlineStyle) {\n // Inline style exists - capture its value/unit so we can restore it\n const initial = getOriginalCSSValueWithUnit(target, property);\n propMap.set(property, { removeInline: false, value: initial.value, unit: initial.unit });\n } else {\n // No inline style - just flag to remove inline on restore (lets stylesheet auto/calc/clamp take over)\n propMap.set(property, { removeInline: true });\n }\n }\n }\n }\n\n propTween = propTween.next;\n }\n }\n\n /**\n * Restore all elements to their initial property values (used when seeking to position 0)\n */\n private _restoreInitialValues(): void {\n // First, handle transform elements - clear cache and remove inline transform\n for (const element of this._transformElements) {\n const htmlEl = element as HTMLElement;\n // Clear the transform cache completely (resets to defaults: scale=1, others=0)\n clearTransformCache(element);\n const originalTransform = this._initialTransforms.get(element);\n if (originalTransform) {\n htmlEl.style.transform = originalTransform;\n } else {\n htmlEl.style.removeProperty('transform');\n }\n }\n\n // Then handle regular CSS properties\n for (const [element, propMap] of this._initialValues) {\n const htmlEl = element as HTMLElement;\n\n for (const [property, initial] of propMap) {\n // Skip transform properties - already handled above\n if (isTransformProp(property)) continue;\n\n if (isDrawSVGProp(property)) {\n // drawSVG is a virtual property that sets stroke-dasharray and\n // stroke-dashoffset on SVG elements. Remove the actual CSS properties.\n htmlEl.style.removeProperty('stroke-dasharray');\n htmlEl.style.removeProperty('stroke-dashoffset');\n continue;\n }\n\n const kebabProp = property.replace(/([A-Z])/g, '-$1').toLowerCase();\n\n if (initial.removeInline) {\n // No original inline style - just remove the inline style we added\n // This lets the stylesheet value (auto, calc, clamp, etc.) take over\n htmlEl.style.removeProperty(kebabProp);\n } else {\n // Regular CSS property - restore with original value and unit\n const cssValue = `${initial.value}${initial.unit}`;\n htmlEl.style.setProperty(kebabProp, cssValue);\n }\n }\n }\n }\n\n\n /**\n * Disable CSS transitions on all animated elements.\n * Called when the timeline starts playing to prevent CSS transitions\n * from fighting frame-by-frame SDK rendering.\n */\n private _disableTransitions(): void {\n if (this._transitionsDisabled) return;\n for (const element of this._savedTransitions.keys()) {\n element.style.transition = 'none';\n }\n this._transitionsDisabled = true;\n }\n\n\n /**\n * Restore original CSS transition values on all animated elements.\n * Called on timeline completion and kill to re-enable CSS hover/focus\n * transitions after the SDK animation is done.\n */\n private _restoreTransitions(): void {\n if (!this._transitionsDisabled) return;\n for (const [element, original] of this._savedTransitions) {\n element.style.transition = original;\n }\n this._transitionsDisabled = false;\n }\n\n /**\n * Clear all saved transition data (for kill/clear).\n */\n private _clearTransitionData(): void {\n this._restoreTransitions();\n this._savedTransitions.clear();\n this._transitionsDisabled = false;\n }\n\n\n /**\n * Internal: Add animation entry (used by Motion function)\n * @internal\n */\n _addEntry(target: TargetInput, config: AnimationConfig, position?: string | number): this {\n const startTime = PositionParser.parse(\n position,\n this._duration,\n this._previousStart,\n this._previousEnd\n );\n\n const builder = new AnimationBuilder(target, config);\n return this._addBuilder(builder, startTime);\n }\n\n /**\n * Internal method to add an AnimationBuilder directly\n */\n private _addBuilder(builder: AnimationBuilder, position: number): this {\n // Tell builder to skip Engine's immediate FROM render (animation.render(0)).\n // Timeline captures initial CSS values first via _captureInitialValues,\n // THEN renders the FROM state via renderAtTime(0) below.\n // Without this, Engine renders FROM values before capture, so kill()\n // would restore elements to FROM values instead of their true initial state.\n builder.setSkipInitialFromRender(true);\n\n const animations = builder.getAnimations();\n if (animations.length === 0) return this;\n\n let maxEndTime = position;\n for (const animation of animations) {\n const animDelay = animation.getDelay();\n const animStartTime = position + animDelay;\n animation.clearDelay();\n const animDuration = animation.totalDuration();\n\n const timelineChild: TimelineChild = {\n type: 'animation',\n child: animation,\n builder,\n startTime: animStartTime,\n duration: animDuration,\n };\n\n this._children.push(timelineChild);\n animation.setTimelineChild(true);\n animation.pause();\n this._captureInitialValues(animation);\n\n // Save original CSS transition values for later override during playback.\n // CSS transitions (e.g. `transition: transform 0.25s`) fight the SDK's\n // frame-by-frame rendering when both control the same property.\n // Values are saved here at build time; actual disable happens on timeline start.\n for (const target of animation.getTargets()) {\n if (target instanceof HTMLElement && !this._savedTransitions.has(target)) {\n this._savedTransitions.set(target, target.style.transition);\n }\n }\n\n\n const hasExplicitFrom = !!builder.getConfig().fromVars;\n if (animStartTime > 0 && !hasExplicitFrom) {\n animation.setLazyStartCapture(true);\n }\n\n // Immediately render from state for explicit-from animations to prevent\n // FOUC (Flash of Unstyled Content). Without this, elements remain at their\n // natural CSS state for one frame before the engine's rAF loop applies\n // the from values. Rendering synchronously ensures elements are hidden\n // (e.g. opacity: 0, y: '100%' behind a mask) before the browser paints.\n // Safe: animation is paused (_isActive=false) so no callbacks fire.\n if (hasExplicitFrom) {\n animation.renderAtTime(0);\n }\n\n // For split+mask animations, the from state targets split fragments (not\n // the original parent element). If the parent has a CSS pre-hide\n // (e.g. opacity: 0), it would block the fragments from ever becoming\n // visible — children cannot be more opaque than their parent. Override\n // the parent's opacity since the mask (overflow:hidden + y offset)\n // already handles visibility of the split fragments.\n //\n // Track the original opacity so kill() can restore it — without this,\n // the inline opacity: 1 leaks across timeline rebuilds and prevents\n // the element from returning to its CSS-defined state on reset.\n if (builder.hasSplit()) {\n for (const target of builder.getOriginalTargets()) {\n if (target instanceof Element) {\n const htmlTarget = target as HTMLElement;\n if (!this._splitParentOverrides.has(htmlTarget)) {\n this._splitParentOverrides.set(htmlTarget, htmlTarget.style.opacity);\n }\n htmlTarget.style.opacity = '1';\n }\n }\n }\n\n const endTime = animStartTime + animDuration;\n if (endTime > maxEndTime) {\n maxEndTime = endTime;\n }\n }\n\n this._previousStart = position;\n this._previousEnd = maxEndTime;\n\n if (maxEndTime > this._duration) {\n this._duration = maxEndTime;\n }\n\n return this;\n }\n\n /**\n * Call function at position\n */\n call(callback: (...args: unknown[]) => void, params?: unknown[], position?: string | number): this {\n const startTime = PositionParser.parse(\n position,\n this._duration,\n this._previousStart,\n this._previousEnd\n );\n\n const timelineChild: TimelineChild = {\n type: 'callback',\n child: callback,\n startTime,\n duration: 0,\n params,\n };\n\n this._children.push(timelineChild);\n\n // Update previous tracking\n this._previousStart = startTime;\n this._previousEnd = startTime;\n\n // Update timeline duration if callback is beyond current end\n if (startTime > this._duration) {\n this._duration = startTime;\n }\n\n return this;\n }\n\n /**\n * Get the first animation's target element for trigger inference.\n * When text splitting is active, returns the original pre-split container\n * element instead of the split char/word fragments, so the scroll trigger\n * is anchored to the full element rather than a tiny character span.\n * Note: Returns only DOM Elements, not plain objects\n */\n private _getFirstAnimationTarget(): Element | undefined {\n // Prefer builder path (split-aware)\n const firstChild = this._children[0];\n if (firstChild?.type === 'animation' && firstChild.builder) {\n const builder = firstChild.builder;\n const targets = builder.hasSplit()\n ? builder.getOriginalTargets()\n : builder.getTargets();\n for (const target of targets) {\n if (isElement(target)) {\n return target;\n }\n }\n }\n\n // Fallback: use Animation's target list (no builder available)\n const firstAnimation = this.getFirstChildAnimation();\n if (firstAnimation) {\n const targets = firstAnimation.getTargets();\n for (const target of targets) {\n if (isElement(target)) {\n return target;\n }\n }\n }\n return undefined;\n }\n\n /**\n * Get all target elements from the first animation (for each mode)\n * Note: Returns only DOM Elements, not plain objects\n *\n * When the builder uses text splitting (split: 'words' etc.), the animation's\n * targets are the split fragments — but for each-mode we need the ORIGINAL\n * pre-split elements so each gets its own ScrollTrigger. The cloned builder\n * will re-split inside that element independently.\n */\n private _getAllAnimationTargets(): Element[] {\n const all: Element[] = [];\n const seen = new Set<Element>();\n const processedBuilders = new Set<AnimationBuilder>();\n\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n if (processedBuilders.has(child.builder)) continue;\n processedBuilders.add(child.builder);\n\n const targets = child.builder.hasSplit()\n ? child.builder.getOriginalTargets()\n : child.builder.getTargets();\n\n for (const target of targets) {\n if (isElement(target) && !seen.has(target as Element)) {\n seen.add(target as Element);\n all.push(target as Element);\n }\n }\n }\n }\n return all;\n }\n\n /**\n * Create a timeline instance for a single element (for each mode)\n * This clones the timeline structure but only targets the specific element.\n */\n private _createInstanceForElement(element: Element, index: number): Timeline {\n // Create a named timeline so it gets registered with Engine for ticking\n // Use parent name (if any) + unique suffix to avoid collisions\n const baseName = this._name || '__each';\n const instanceName = `${baseName}__each_${index}_${Timeline._eachCounter++}`;\n const instance = new Timeline(instanceName);\n\n const processedBuilders = new Set<AnimationBuilder>();\n\n // Clone each child animation for this specific element\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n // Skip if we already processed this builder (stagger creates multiple children from one builder)\n if (processedBuilders.has(child.builder)) continue;\n processedBuilders.add(child.builder);\n\n // Only include builders that originally targeted this specific element\n const builderElements = (child.builder.hasSplit()\n ? child.builder.getOriginalTargets()\n : child.builder.getTargets()\n ).filter((t): t is Element => isElement(t));\n if (!builderElements.includes(element)) continue;\n\n const builderConfig = child.builder.getConfig();\n\n // Build repeat config if any repeat properties are set\n const repeatConfig = builderConfig.repeat != null\n ? { times: builderConfig.repeat, delay: builderConfig.repeatDelay, yoyo: builderConfig.yoyo }\n : undefined;\n\n // When the original builder uses split, pass split + stagger to the\n // clone so each element re-splits its own text and staggers internally.\n // Without split, stagger is omitted — each element animates independently.\n const hasSplit = child.builder.hasSplit();\n\n const newBuilder = new AnimationBuilder(element, {\n from: builderConfig.fromVars,\n to: builderConfig.toVars,\n set: builderConfig.setVars,\n duration: builderConfig.duration,\n delay: builderConfig.delay,\n ease: builderConfig.ease,\n repeat: repeatConfig,\n ...(hasSplit && builderConfig.split ? { split: builderConfig.split } : {}),\n ...(hasSplit && builderConfig.mask ? { mask: builderConfig.mask } : {}),\n ...(hasSplit && builderConfig.stagger !== undefined ? { stagger: builderConfig.stagger } : {}),\n // Forward flap + fit so per-element clones keep their special-purpose\n // effects. Without this, onHover({each:true}) + flap produced empty\n // animations that silently did nothing on each cloned per-link timeline.\n ...(builderConfig.flap ? { flap: builderConfig.flap } : {}),\n ...(builderConfig.fit ? { fit: builderConfig.fit } : {}),\n onStart: builderConfig.onStart,\n onUpdate: builderConfig.onUpdate,\n onComplete: builderConfig.onComplete,\n onRepeat: builderConfig.onRepeat,\n onReverseComplete: builderConfig.onReverseComplete,\n });\n if (builderConfig.axis) {\n newBuilder.setAxis(builderConfig.axis);\n }\n\n // Add to instance at same position\n instance._addBuilder(newBuilder, child.startTime);\n } else if (child.type === 'callback') {\n // Clone callbacks too\n instance.call(\n child.child as (...args: unknown[]) => void,\n child.params,\n child.startTime\n );\n }\n // Note: nested timelines not supported in each mode for now\n }\n\n return instance;\n }\n\n /**\n * Create a timeline instance for multiple elements (for each mode with explicit container target).\n * This is a sibling of _createInstanceForElement — the only difference is that it accepts an\n * Element[] and passes the array directly to AnimationBuilder instead of a single element.\n */\n private _createInstanceForElements(elements: Element[], index: number): Timeline {\n const baseName = this._name || '__each';\n const instanceName = `${baseName}__each_${index}_${Timeline._eachCounter++}`;\n const instance = new Timeline(instanceName);\n\n const processedBuilders = new Set<AnimationBuilder>();\n\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n if (processedBuilders.has(child.builder)) continue;\n processedBuilders.add(child.builder);\n\n const builderConfig = child.builder.getConfig();\n\n const repeatConfig = builderConfig.repeat != null\n ? { times: builderConfig.repeat, delay: builderConfig.repeatDelay, yoyo: builderConfig.yoyo }\n : undefined;\n\n const hasSplit = child.builder.hasSplit();\n\n const newBuilder = new AnimationBuilder(elements, {\n from: builderConfig.fromVars,\n to: builderConfig.toVars,\n set: builderConfig.setVars,\n duration: builderConfig.duration,\n delay: builderConfig.delay,\n ease: builderConfig.ease,\n repeat: repeatConfig,\n ...(hasSplit && builderConfig.split ? { split: builderConfig.split } : {}),\n ...(hasSplit && builderConfig.mask ? { mask: builderConfig.mask } : {}),\n ...(hasSplit && builderConfig.stagger !== undefined ? { stagger: builderConfig.stagger } : {}),\n // Forward flap + fit so per-element clones keep their special-purpose\n // effects. Without this, onHover({each:true}) + flap produced empty\n // animations that silently did nothing on each cloned per-link timeline.\n ...(builderConfig.flap ? { flap: builderConfig.flap } : {}),\n ...(builderConfig.fit ? { fit: builderConfig.fit } : {}),\n onStart: builderConfig.onStart,\n onUpdate: builderConfig.onUpdate,\n onComplete: builderConfig.onComplete,\n onRepeat: builderConfig.onRepeat,\n onReverseComplete: builderConfig.onReverseComplete,\n });\n if (builderConfig.axis) {\n newBuilder.setAxis(builderConfig.axis);\n }\n\n instance._addBuilder(newBuilder, child.startTime);\n } else if (child.type === 'callback') {\n instance.call(\n child.child as (...args: unknown[]) => void,\n child.params,\n child.startTime\n );\n }\n // Note: nested timelines not supported in each mode for now\n }\n\n return instance;\n }\n\n /**\n * Resolve a CSS selector string or Element into an array of DOM elements.\n */\n private _resolveTargetElements(selector: string | Element): Element[] {\n if (typeof selector !== 'string') return [selector as Element];\n if (typeof document !== 'undefined') {\n try { return Array.from(document.querySelectorAll(selector)); }\n catch (e) { console.warn(`[Motion] Invalid selector \"${selector}\":`, e); }\n }\n return [];\n }\n\n /**\n * Generic helper for each-mode setup: resolve targets, create instances, register triggers.\n * The setupTrigger callback receives each (instance, element) pair for trigger-specific registration.\n */\n private _setupEachModeGeneric(\n targets: Element[],\n setupTrigger: (instance: Timeline, target: Element) => void\n ): void {\n if (targets.length === 0) return;\n\n this._eachInstances = [];\n\n // If the original builder used text splitting, the build already mutated the\n // DOM for ALL targets. Revert those splits now so each per-element clone can\n // split its own target independently from clean (original) HTML.\n const splitBuilders = new Set(\n this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!),\n );\n if (splitBuilders.size > 0) {\n for (const el of targets) {\n for (const builder of splitBuilders) {\n SDKRegistry.textSplitter?.revert?.(el, builder);\n }\n }\n }\n\n // Clear transition data from the initial build.\n // Each per-element instance will independently save + manage transitions\n // for its own target during its own playback lifecycle.\n this._clearTransitionData();\n\n for (let i = 0; i < targets.length; i++) {\n const element = targets[i];\n const instance = this._createInstanceForElement(element, i);\n\n setupTrigger(instance, element);\n\n this._eachInstances.push(instance);\n }\n\n // Propagate parent callbacks to instances with active-instance tracking.\n // In each-mode, all instances receive scroll/update events simultaneously,\n // but only the \"active\" one (currently mid-animation) should report progress\n // to the builder playhead. Without this, N instances send N conflicting\n // progress values per scroll tick, causing the playhead to jump erratically.\n {\n let activeInstance: Timeline | null = null;\n const parentOnUpdate = this._onUpdate;\n const parentOnStart = this._onStart;\n const parentOnComplete = this._onComplete;\n\n for (const instance of this._eachInstances) {\n if (parentOnUpdate) {\n instance.onUpdate((progress: number, time: number) => {\n const isInProgress = progress > 0 && progress < 1;\n\n // Claim active slot when this instance is mid-animation\n if (isInProgress) {\n activeInstance = instance;\n }\n\n // Only the active instance reports progress\n if (activeInstance === instance) {\n parentOnUpdate(progress, time);\n // Release active slot when reaching an endpoint (0 or 1)\n if (!isInProgress) {\n activeInstance = null;\n }\n }\n });\n }\n\n if (parentOnStart) {\n instance.onStart(() => {\n activeInstance = instance;\n parentOnStart();\n });\n }\n\n if (parentOnComplete) {\n instance.onComplete(() => {\n if (activeInstance === instance) {\n activeInstance = null;\n parentOnComplete();\n }\n });\n }\n }\n }\n\n // Save pre-each duration for forwarding (control methods use this after children are cleared)\n this._eachDuration = this._duration;\n\n // CRITICAL: Clear the original timeline's animations so they don't compete\n // with the cloned per-element timelines. The original AnimationBuilder already\n // built PropTweens for ALL elements and rendered them (e.g., at opacity: 0).\n // If we leave them alive, they keep overwriting elements back to their FROM\n // state, causing only the first element to appear to animate. The original\n // timeline becomes just a container that holds _eachInstances references;\n // it should have NO active animations of its own.\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n // Release from timeline ownership so it can be cleaned up\n animation.setTimelineChild(false);\n animation.kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill();\n }\n }\n this._children = [];\n this._duration = 0;\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._splitParentOverrides.clear();\n\n // Reset parent state after each-mode setup\n this._isActive = false;\n this._time = 0;\n this._progress = 0;\n this._isReversed = false;\n this._startFired = false;\n }\n\n /**\n * Set up each-mode: create independent timeline instances for each target element\n */\n private _setupEachMode(\n triggerType: 'hover' | 'click' | 'scroll',\n config: HoverConfig | ClickConfig | ScrollConfig\n ): void {\n const triggerManager = SDKRegistry.triggerManager?.getInstance?.();\n if (!triggerManager) return; // TriggerManager not loaded\n\n // Strip 'each' from config to prevent recursion in per-element instances\n const { each: _, ...cleanConfig } = config as ScrollConfig & { each?: boolean };\n\n // When an explicit container target is provided, scope animation targets to each container\n if (config.target) {\n const containers = this._resolveTargetElements(config.target as string | Element);\n if (containers.length === 0) return;\n\n const allAnimTargets = this._getAllAnimationTargets();\n\n // Check if container-scoped mode applies: all animation targets must be\n // contained by at least one container. If any target is not a descendant\n // of any container, container scoping doesn't apply — fall back to\n // generic per-target mode (e.g. when trigger target and animation targets\n // are siblings, not parent-child).\n const allContained = allAnimTargets.every(t =>\n containers.some(c => c !== t && c.contains(t))\n );\n\n if (!allContained) {\n // Container scoping doesn't apply — use generic per-target each mode\n const splitBuildersA = new Set(\n this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!),\n );\n if (splitBuildersA.size > 0) {\n for (const el of allAnimTargets) {\n for (const builder of splitBuildersA) {\n SDKRegistry.textSplitter?.revert?.(el, builder);\n }\n }\n }\n this._setupEachModeGeneric(allAnimTargets, (instance, element) => {\n if (triggerType === 'scroll') {\n triggerManager.registerScroll(instance, { ...cleanConfig, target: element as Element } as ScrollConfig);\n } else if (triggerType === 'hover') {\n triggerManager.registerHover(instance, { ...cleanConfig, target: element as Element } as HoverConfig);\n } else {\n triggerManager.registerClick(instance, { ...cleanConfig, target: element as Element } as ClickConfig);\n }\n });\n return;\n }\n\n // Revert text splits on animation targets so each container-scoped clone can re-split\n const splitBuildersB = new Set(\n this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!),\n );\n if (splitBuildersB.size > 0) {\n for (const el of allAnimTargets) {\n for (const builder of splitBuildersB) {\n SDKRegistry.textSplitter?.revert?.(el, builder);\n }\n }\n }\n\n this._clearTransitionData();\n this._eachInstances = [];\n\n for (let i = 0; i < containers.length; i++) {\n const container = containers[i];\n const scopedTargets = allAnimTargets.filter(t => container.contains(t));\n if (scopedTargets.length === 0) continue;\n\n const instance = this._createInstanceForElements(scopedTargets, i);\n\n // Register trigger on the CONTAINER, not the animation targets\n if (triggerType === 'scroll') {\n triggerManager.registerScroll(instance, { ...cleanConfig, target: container } as ScrollConfig);\n } else if (triggerType === 'hover') {\n triggerManager.registerHover(instance, { ...cleanConfig, target: container as Element } as HoverConfig);\n } else {\n triggerManager.registerClick(instance, { ...cleanConfig, target: container as Element } as ClickConfig);\n }\n\n this._eachInstances.push(instance);\n }\n\n // Propagate parent callbacks to instances with active-instance tracking.\n // In each-mode, all instances receive scroll/update events simultaneously,\n // but only the \"active\" one (currently mid-animation) should report progress\n // to the builder playhead. Without this, N instances send N conflicting\n // progress values per scroll tick, causing the playhead to jump erratically.\n {\n let activeInstance: Timeline | null = null;\n const parentOnUpdate = this._onUpdate;\n const parentOnStart = this._onStart;\n const parentOnComplete = this._onComplete;\n\n for (const instance of this._eachInstances) {\n if (parentOnUpdate) {\n instance.onUpdate((progress: number, time: number) => {\n const isInProgress = progress > 0 && progress < 1;\n\n // Claim active slot when this instance is mid-animation\n if (isInProgress) {\n activeInstance = instance;\n }\n\n // Only the active instance reports progress\n if (activeInstance === instance) {\n parentOnUpdate(progress, time);\n // Release active slot when reaching an endpoint (0 or 1)\n if (!isInProgress) {\n activeInstance = null;\n }\n }\n });\n }\n\n if (parentOnStart) {\n instance.onStart(() => {\n activeInstance = instance;\n parentOnStart();\n });\n }\n\n if (parentOnComplete) {\n instance.onComplete(() => {\n if (activeInstance === instance) {\n activeInstance = null;\n parentOnComplete();\n }\n });\n }\n }\n }\n\n // Save pre-each duration for forwarding (control methods use this after children are cleared)\n this._eachDuration = this._duration;\n\n // CRITICAL: Clear the original timeline's animations so they don't compete\n // with the cloned per-container timelines.\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n animation.setTimelineChild(false);\n animation.kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill();\n }\n }\n this._children = [];\n this._duration = 0;\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._splitParentOverrides.clear();\n\n // Reset parent state after each-mode setup\n this._isActive = false;\n this._time = 0;\n this._progress = 0;\n this._isReversed = false;\n this._startFired = false;\n return;\n }\n\n const targets = this._getAllAnimationTargets();\n\n this._setupEachModeGeneric(targets, (instance, element) => {\n if (triggerType === 'scroll') {\n triggerManager.registerScroll(instance, { ...cleanConfig, target: element as Element } as ScrollConfig);\n } else if (triggerType === 'hover') {\n triggerManager.registerHover(instance, { ...cleanConfig, target: element as Element } as HoverConfig);\n } else {\n triggerManager.registerClick(instance, { ...cleanConfig, target: element as Element } as ClickConfig);\n }\n });\n }\n\n /**\n * Trigger timeline on hover (mouseenter plays, mouseleave configurable)\n */\n onHover(config?: HoverConfig): this {\n if (config?.each) {\n this._setupEachMode('hover', config);\n return this;\n }\n\n const target = config?.target ?? this._getFirstAnimationTarget();\n SDKRegistry.triggerManager?.getInstance?.().registerHover(this, {\n target,\n onEnter: config?.onEnter,\n onLeave: config?.onLeave,\n leaveDelay: config?.leaveDelay,\n });\n return this;\n }\n\n /**\n * Trigger timeline on click\n */\n onClick(config?: ClickConfig): this {\n if (config?.each) {\n this._setupEachMode('click', config);\n return this;\n }\n\n const target = config?.target ?? this._getFirstAnimationTarget();\n SDKRegistry.triggerManager?.getInstance?.().registerClick(this, {\n target,\n secondTarget: config?.secondTarget,\n toggle: config?.toggle,\n preventDefault: config?.preventDefault,\n });\n return this;\n }\n\n /**\n * Trigger timeline on scroll\n */\n onScroll(config?: ScrollConfig): this {\n if (config?.each) {\n this._setupEachMode('scroll', config);\n return this;\n }\n\n const target = config?.target ?? this._getFirstAnimationTarget();\n SDKRegistry.triggerManager?.getInstance?.().registerScroll(this, { ...config, target });\n return this;\n }\n\n /**\n * Trigger timeline on mouse movement\n * Drives animation progress based on mouse position (axis mode) or distance from center (distance mode)\n */\n onMouseMove(config?: MouseMoveConfig): this {\n // Handle each mode for mouse move (only with target)\n if (config?.each && config.target) {\n this._setupMouseMoveEachMode(config);\n return this;\n }\n\n SDKRegistry.triggerManager?.getInstance?.().registerMouseMove(this, {\n type: config?.type ?? 'distance',\n target: config?.target,\n smooth: config?.smooth,\n startProgress: config?.startProgress,\n leaveProgress: config?.leaveProgress,\n });\n return this;\n }\n\n /**\n * Set up each-mode for mouse movement: create independent timeline instances for each target element\n */\n private _setupMouseMoveEachMode(config: MouseMoveConfig): void {\n if (!config.target) return;\n\n // Resolve target elements\n let targets: Element[] = [];\n if (typeof config.target === 'string') {\n if (typeof document !== 'undefined') {\n targets = Array.from(document.querySelectorAll(config.target));\n }\n } else {\n targets = [config.target as Element];\n }\n\n const triggerManager = SDKRegistry.triggerManager?.getInstance?.();\n if (!triggerManager) return; // TriggerManager not loaded\n\n this._setupEachModeGeneric(targets, (instance, element) => {\n triggerManager.registerMouseMove(instance, {\n type: config.type ?? 'distance',\n target: element,\n smooth: config.smooth,\n startProgress: config.startProgress,\n leaveProgress: config.leaveProgress,\n });\n });\n }\n\n /**\n * Trigger timeline on page load.\n *\n * @param config - Optional configuration\n * @param config.timing - When to play: 'before' (immediately), 'during' (DOMContentLoaded), 'after' (window load). Defaults to 'during'.\n * @param config.paused - When true, the timeline is built but NOT played automatically.\n * Start it manually with `Motion('timelineName').play()`.\n */\n onPageLoad(config?: { timing?: 'before' | 'during' | 'after'; paused?: boolean }): this {\n SDKRegistry.triggerManager?.getInstance?.().registerPageLoad(this, config || {});\n return this;\n }\n\n /**\n * Trigger timeline when user navigates away from the page (clicks a link).\n * Intercepts the click, plays the exit animation, then navigates to the target URL.\n */\n onPageExit(config?: PageExitConfig): this {\n SDKRegistry.triggerManager?.getInstance?.().registerPageExit(this, config || {});\n return this;\n }\n\n /**\n * Trigger timeline based on user gestures (pointer, touch, wheel, scroll)\n * Maps directional callbacks to timeline actions\n */\n onGesture(config: GestureConfig): this {\n // Handle each mode for gesture\n if (config.each) {\n this._setupGestureEachMode(config);\n return this;\n }\n\n SDKRegistry.triggerManager?.getInstance?.().registerGesture(this, config);\n return this;\n }\n\n /**\n * Set up each-mode for gesture: create independent instances for each target element\n */\n private _setupGestureEachMode(config: GestureConfig): void {\n // Resolve target elements\n let targets: Element[] = [];\n\n if (config.target) {\n if (typeof config.target === 'string') {\n if (typeof document !== 'undefined') {\n targets = Array.from(document.querySelectorAll(config.target));\n }\n } else {\n targets = [config.target as Element];\n }\n } else {\n // If no target specified, use animation targets\n targets = this._getAllAnimationTargets();\n }\n\n // First pass: create all instances (using generic helper with a no-op trigger setup)\n this._setupEachModeGeneric(targets, () => {});\n\n if (!this._eachInstances || this._eachInstances.length === 0) return;\n\n // Second pass: register triggers with sibling info for playNext/playPrevious\n const triggerManager = SDKRegistry.triggerManager?.getInstance?.();\n if (!triggerManager) return; // TriggerManager not loaded\n\n for (let i = 0; i < targets.length; i++) {\n const element = targets[i];\n const instance = this._eachInstances[i];\n\n triggerManager.registerGesture(instance, {\n ...config,\n target: element,\n each: false, // Prevent recursion\n _siblings: this._eachInstances, // Pass all siblings for sequence control\n _index: i, // Current index in sequence\n } as GestureConfigInternal);\n }\n }\n\n /**\n * Attach cursor behavior to this timeline's target element\n * Creates a custom cursor that follows mouse with smooth tracking and state-based animations\n */\n onCursor(config: CursorConfig): this {\n SDKRegistry.triggerManager?.getInstance?.().registerCursor(this, config);\n return this;\n }\n\n /**\n * Update a callback child: fire when time crosses its position\n */\n private _updateCallbackChild(child: TimelineChild, prevTime: number, deltaTime: number): void {\n const childStartTime = child.startTime;\n\n // Execute callback when time crosses its position (from either direction)\n // or when seeking directly to the callback position\n const crossedForward = prevTime < childStartTime && this._time >= childStartTime;\n const crossedBackward = prevTime > childStartTime && this._time <= childStartTime;\n const atPosition = Math.abs(this._time - childStartTime) < 0.001; // Within 1ms\n\n if (crossedForward || crossedBackward || (atPosition && deltaTime === 0)) {\n const callback = child.child as (...args: unknown[]) => void;\n const params = child.params || [];\n callback(...params);\n }\n }\n\n /**\n * Update an animation child: seek/activate/deactivate based on parent time\n */\n private _updateAnimationChild(child: TimelineChild, deltaTime: number): void {\n const animation = child.child as Animation;\n const timeScale = Math.abs(animation.getTimeScale()) || 1;\n const scaledDuration = animation.totalDuration() / timeScale;\n\n // Calculate local time within the animation\n const localTime = this._time - child.startTime;\n const scaledLocalTime = localTime * timeScale;\n\n if (localTime >= 0 && localTime <= scaledDuration) {\n // Animation should be active\n if (!animation.isActive()) {\n // Capture start values from current element state (for chained animations)\n animation.captureStartValues();\n animation.play();\n }\n\n // Seek animation to local time\n animation.renderAtTime(scaledLocalTime);\n } else if (localTime < 0) {\n // Animation hasn't started yet\n // Only render at start state if it has explicit FROM values (not lazy capture)\n // Lazy-capture animations should NEVER render before they start,\n // even after values have been captured, to avoid overwriting earlier animations\n if (!animation.isLazyCapture()) {\n animation.renderAtTime(0);\n }\n if (animation.isActive()) {\n animation.pause();\n // Fire onReverseComplete when an active animation exits the start boundary\n // during reverse playback. Also resets _startFired/_completeFired for next play.\n animation.fireReverseComplete();\n }\n } else if (animation.isActive()) {\n // Animation past its end - pause at final state\n animation.renderAtTime(animation.totalDuration());\n animation.pause();\n } else if (deltaTime === 0) {\n // Seeking past animation end - render at final state.\n // Lazy-capture animations (fit, TO-only at position > 0) must have their\n // start values captured before they can render meaningful PropTweens.\n // When a scroll-scrubbed timeline loads at a position past these animations\n // (e.g. page refreshed below the pin zone, smooth scrub catch-up overshoots),\n // the timeline never passes through the animation's active range, so\n // captureStartValues() was never called. Do it now — children are processed\n // in insertion order, so earlier animations have already rendered their end\n // states and getBoundingClientRect() returns correct positions for fit geometry.\n if (animation.needsStartCapture()) {\n animation.captureStartValues();\n }\n animation.renderAtTime(animation.totalDuration());\n }\n }\n\n /**\n * Update a nested timeline child: seek/activate/deactivate based on parent time\n */\n private _updateTimelineChild(child: TimelineChild, deltaTime: number): void {\n const timeline = child.child as Timeline;\n const actualDuration = timeline.duration();\n\n // Calculate local time within the timeline\n const localTime = this._time - child.startTime;\n\n if (localTime >= 0 && localTime <= actualDuration) {\n // Timeline should be active\n if (!timeline.isActive()) {\n timeline.play();\n }\n\n // Seek timeline to local time\n timeline.time(localTime);\n } else if (timeline.isActive()) {\n // Timeline should not be active - pause it at boundary\n if (localTime < 0) {\n timeline.time(0);\n timeline.pause();\n } else {\n timeline.time(actualDuration);\n timeline.pause();\n }\n } else if (deltaTime === 0) {\n // Only render boundary state when SEEKING (not during normal playback)\n if (localTime < 0) {\n // Timeline hasn't started yet - don't render during seek\n } else {\n timeline.time(actualDuration);\n }\n }\n // During normal playback, don't render inactive timelines outside their range\n }\n\n /**\n * Update timeline by delta time\n */\n update(deltaTime: number): void {\n // In each-mode, forward updates to instances\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.update(deltaTime);\n return;\n }\n\n // Store previous time before updating\n const prevTime = this._time;\n\n if (!this._isActive && deltaTime === 0) {\n // This is a seek operation, still need to update children\n } else if (!this._isActive) {\n return;\n }\n\n // Apply time scale\n const scaledDelta = deltaTime * this._timeScale;\n\n // Handle repeat delay countdown (real playback only, not seeking)\n if (this._inRepeatDelay && deltaTime > 0) {\n this._repeatDelayCounter -= scaledDelta;\n if (this._repeatDelayCounter > 0) {\n // Still waiting out the repeat delay — don't advance time or update children\n return;\n }\n // Delay just finished: set up for the incoming cycle\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n if (this._isReversed) {\n // Yoyo: about to start the reverse pass — position at end and reset child flags\n this._time = this._duration;\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n } else {\n // Non-yoyo (or yoyo returning to forward): start from the beginning\n this._time = 0;\n this._startFired = false;\n this._resetChildrenForCycle();\n }\n // Fall through to run progress + children update for this frame\n } else if (!this._inRepeatDelay) {\n // Normal time advancement\n this._time += this._isReversed ? -scaledDelta : scaledDelta;\n\n // Cycle boundary detection — only during real playback, not seeking\n if (deltaTime > 0) {\n if (!this._isReversed && this._time >= this._duration && this._repeat !== 0) {\n // Forward cycle just ended\n const hasMoreRepeats = this._repeat === -1 || this._currentRepeat < this._repeat;\n if (hasMoreRepeats) {\n this._time = this._duration;\n this._currentRepeat++;\n try { this._onRepeat?.(this._currentRepeat); } catch (e) { console.error('[Motion] onRepeat callback error:', e); }\n\n if (this._yoyo) {\n // Flip to reverse; reset child callback flags so their onStart/onComplete re-fire\n this._isReversed = true;\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n } else {\n // Non-yoyo: restore elements and restart from beginning.\n // Carry over any excess time beyond the cycle duration so the\n // new cycle starts at the correct sub-frame offset instead of\n // snapping to exactly 0 (avoids micro-stutter on fast refreshes).\n const excess = this._time - this._duration;\n this._time = Math.max(0, excess);\n this._startFired = false;\n this._resetChildrenForCycle();\n }\n\n if (this._repeatDelay > 0) {\n this._inRepeatDelay = true;\n this._repeatDelayCounter = this._repeatDelay;\n }\n }\n // When hasMoreRepeats is false, fall through to clamping + completion check below\n } else if (this._isReversed && this._time <= 0 && this._yoyo && this._repeat !== 0) {\n // Yoyo reverse cycle just ended (time hit 0)\n const hasMoreRepeats = this._repeat === -1 || this._currentRepeat < this._repeat;\n if (hasMoreRepeats) {\n this._time = 0;\n this._currentRepeat++;\n try { this._onRepeat?.(this._currentRepeat); } catch (e) { console.error('[Motion] onRepeat callback error:', e); }\n\n // Flip back to forward\n this._isReversed = false;\n this._startFired = false;\n this._resetChildrenForCycle();\n\n if (this._repeatDelay > 0) {\n this._inRepeatDelay = true;\n this._repeatDelayCounter = this._repeatDelay;\n }\n }\n // When hasMoreRepeats is false, fall through to clamping + completion check below\n }\n }\n }\n\n // Clamp time to current cycle bounds\n if (this._time < 0) {\n this._time = 0;\n } else if (this._time > this._duration) {\n this._time = this._duration;\n }\n\n // Update progress\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n\n // Fire onStart callback on first update\n // Use _time > 0 instead of _progress > 0 because _progress = _time / _duration\n // is always 0 when _duration is Infinity (repeat: -1), preventing onStart from\n // ever firing for infinite-repeat timelines.\n if (!this._startFired && this._isActive && this._time > 0) {\n this._startFired = true;\n // Disable CSS transitions during active playback to prevent interference\n this._disableTransitions();\n this._onStart?.();\n }\n\n // Fire onUpdate callback\n if (this._isActive) {\n this._onUpdate?.(this._progress, this._time);\n }\n\n // Update children - dispatch to type-specific helpers\n for (const child of this._children) {\n if (child.type === 'callback') {\n this._updateCallbackChild(child, prevTime, deltaTime);\n } else if (child.type === 'animation') {\n this._updateAnimationChild(child, deltaTime);\n } else if (child.type === 'timeline') {\n this._updateTimelineChild(child, deltaTime);\n }\n }\n\n // Check if complete: fires only when all cycles are done\n if (!this._completeFired) {\n const atForwardEnd = !this._isReversed && this._time >= this._duration;\n // Reverse completion: either a non-yoyo reverse (user called reverse()) or final yoyo cycle\n const atReverseStart = this._isReversed && this._time <= 0;\n const noMoreRepeats = this._repeat !== -1 && this._currentRepeat >= this._repeat;\n\n if (atForwardEnd && (this._repeat === 0 || noMoreRepeats)) {\n this._completeFired = true;\n this._isActive = false;\n this._startFired = false; // Reset so next play/reverse fires onStart\n // Restore CSS transitions now that animation is done.\n // If the timeline replays (hover, restart), _disableTransitions re-fires on next onStart.\n this._restoreTransitions();\n this._onComplete?.();\n } else if (atReverseStart && (this._repeat === 0 || (this._yoyo && noMoreRepeats))) {\n this._completeFired = true;\n this._isActive = false;\n this._startFired = false; // Reset so next play/reverse fires onStart\n this._restoreTransitions();\n this._onComplete?.();\n }\n }\n }\n\n // Control methods\n\n /**\n * Play timeline from position.\n * If already playing forward, continues from current position.\n * No-op if already completed forward (at the end) and no `from` is specified —\n * prevents spurious onStart/onComplete re-firing during continuous gesture events.\n */\n play(from?: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot play a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.play(from);\n return this;\n }\n\n if (from !== undefined) {\n this._time = from;\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n // Reset repeat state when restarting from beginning\n if (from === 0) {\n this._currentRepeat = 0;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n }\n } else if (this._time >= this._duration && !this._isReversed) {\n // Already at the end going forward — nothing to play.\n // Silently skip to avoid re-firing onStart/onComplete and\n // toggling CSS transitions on every frame (e.g. continuous scroll gestures).\n return this;\n }\n\n // Allow completion to fire again on the next play-through\n this._completeFired = false;\n this._isReversed = false;\n this._isActive = true;\n this._atZeroState = false;\n\n return this;\n }\n\n /**\n * Pause timeline at time\n */\n pause(atTime?: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot pause a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.pause(atTime);\n return this;\n }\n\n if (atTime !== undefined) {\n this._time = atTime;\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n }\n\n this._isActive = false;\n\n // Pause all children\n for (const child of this._children) {\n if (child.type !== 'callback') {\n const animation = child.child as Animation | Timeline;\n if ('pause' in animation) {\n animation.pause();\n }\n }\n }\n\n return this;\n }\n\n /**\n * Reverse timeline from position.\n * If already reversing, continues from current position.\n * No-op if already completed reverse (at the start) and no `from` is specified —\n * prevents spurious onStart/onComplete re-firing during continuous gesture events.\n */\n reverse(from?: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot reverse a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.reverse(from);\n return this;\n }\n\n if (from !== undefined) {\n this._time = from;\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n } else if (this._time <= 0 && this._isReversed) {\n // Already at the start going reverse — nothing to reverse.\n // Silently skip to avoid re-firing onStart/onComplete and\n // toggling CSS transitions on every frame (e.g. continuous scroll gestures).\n return this;\n }\n\n // Allow completion to fire again on the next reverse pass\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n this._isReversed = true;\n this._isActive = true;\n this._atZeroState = false;\n\n return this;\n }\n\n /**\n * Restart timeline\n */\n restart(): this {\n if (this._killed) {\n console.warn('[Motion] Cannot restart a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.restart();\n return this;\n }\n\n this._time = 0;\n this._progress = 0;\n this._isActive = true;\n this._isReversed = false;\n this._startFired = false;\n this._atZeroState = false;\n // Reset repeat state for a clean replay from the beginning\n this._currentRepeat = 0;\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n\n // Reset callback flags for clean replay\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n\n // Restore all properties to initial values, then render position-0 animations\n this._restoreInitialValues();\n this._renderPositionZeroAnimations();\n\n return this;\n }\n\n /**\n * Reset all children for the start of a new repeat cycle.\n * Restores elements to their captured initial values and re-renders\n * position-0 animations at their FROM state.\n */\n private _resetChildrenForCycle(): void {\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n this._restoreInitialValues();\n this._renderPositionZeroAnimations();\n }\n\n /**\n * Render only position-0 (non-lazy) animations at their start state\n */\n private _renderPositionZeroAnimations(): void {\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n // Only render non-lazy animations (they have explicit FROM or are at position 0)\n if (!animation.isLazyCapture()) {\n animation.renderAtTime(0);\n }\n if (animation.isActive()) {\n animation.pause();\n }\n } else if (child.type === 'timeline') {\n (child.child as Timeline).progress(0);\n }\n }\n }\n\n /**\n * Seek to position\n */\n seek(position: number): this {\n if (this._killed) {\n console.warn('[Motion] Cannot seek a killed timeline. Create a new one with Motion(name).');\n return this;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.seek(position);\n return this;\n }\n\n this._time = Math.max(0, Math.min(position, this._duration));\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n this._atZeroState = false;\n\n // For repeating timelines, reset animation callback flags so children\n // can cleanly activate at the seek position. Cycle-boundary handling\n // may have deactivated/paused children that need to be active at the\n // target time — resetForReplay() clears onStart/onComplete flags while\n // preserving captured start values so animations render correctly.\n if (this._repeat !== 0) {\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n }\n\n // Update all children to match seek position\n this.update(0);\n\n return this;\n }\n\n /**\n * Set progress for animations bound to a specific axis (for mouse movement trigger)\n * Only affects animations that have .axis() set to the specified axis.\n * @param axis - 'x' or 'y'\n * @param value - Progress value (0-1)\n */\n setAxisProgress(axis: 'x' | 'y', value: number): this {\n const clampedValue = Math.max(0, Math.min(1, value));\n\n for (const child of this._children) {\n if (child.type === 'animation' && child.builder) {\n const builderAxis = child.builder.getConfig().axis;\n if (builderAxis === axis) {\n const animation = child.child as Animation;\n const duration = animation.totalDuration();\n\n // Ensure animation is initialized (capture start values for TO-only animations)\n if (!animation.isActive()) {\n animation.captureStartValues();\n animation.play();\n }\n\n // Set animation time based on progress\n animation.renderAtTime(clampedValue * duration);\n }\n }\n }\n\n return this;\n }\n\n /**\n * Set callback for when timeline starts playing\n */\n onStart(callback: () => void): this {\n this._onStart = callback;\n return this;\n }\n\n /**\n * Set callback for progress updates (called every frame while active)\n * @param callback - Receives (progress: 0-1, time: seconds)\n */\n onUpdate(callback: (progress: number, time: number) => void): this {\n this._onUpdate = callback;\n return this;\n }\n\n /**\n * Set callback for when timeline completes\n */\n onComplete(callback: () => void): this {\n this._onComplete = callback;\n return this;\n }\n\n /**\n * Set callback for each repeat cycle completion\n * @param callback - Receives the current repeat count (starts at 1)\n */\n onRepeat(callback: (repeatCount: number) => void): this {\n this._onRepeat = callback;\n return this;\n }\n\n /**\n * Configure timeline-level repeat behavior (fluent).\n * Use -1 for infinite repeat, or a RepeatConfig for advanced options.\n * @example\n * Motion('loop', '.box', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 0.5 })\n * .withRepeat(-1)\n * .onPageLoad()\n *\n * Motion('bounce', [entries])\n * .withRepeat({ times: 3, yoyo: true, delay: 0.2 })\n * .onPageLoad()\n */\n withRepeat(config: number | RepeatConfig): this {\n if (typeof config === 'number') {\n this._repeat = config;\n this._repeatDelay = 0;\n this._yoyo = false;\n } else {\n this._repeat = config.times;\n this._repeatDelay = config.delay ?? 0;\n this._yoyo = config.yoyo ?? false;\n }\n return this;\n }\n\n /**\n * Get or set progress (0-1)\n */\n progress(): number;\n progress(value: number): this;\n progress(value?: number): number | this {\n if (value === undefined) {\n // Getter: aggregate from each instances if in each-mode\n if (this._eachInstances && this._eachInstances.length > 0) {\n let maxProgress = 0;\n for (const inst of this._eachInstances) {\n const p = inst.progress();\n if (p > maxProgress) maxProgress = p;\n }\n return maxProgress;\n }\n return this._progress;\n }\n\n // Setter\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.progress(value);\n return this;\n }\n\n const clampedValue = Math.max(0, Math.min(1, value));\n\n // Disable CSS transitions on first non-zero progress set.\n // For scrub-driven timelines (ScrollTrigger with scrub), play() is never\n // called so _isActive stays false and the normal _disableTransitions() in\n // update() never fires. Without this, CSS transitions on animated elements\n // fight with frame-by-frame scrub rendering, causing elements to lag or\n // get stuck in incorrect states during rapid scrub direction changes.\n if (!this._transitionsDisabled && this._savedTransitions.size > 0) {\n this._disableTransitions();\n }\n\n // Reset callback flags when seeking to beginning\n if (clampedValue === 0) {\n for (const child of this._children) {\n if (child.type === 'animation') {\n (child.child as Animation).resetForReplay();\n }\n }\n }\n\n this._time = clampedValue * this._duration;\n this._progress = clampedValue;\n\n if (clampedValue === 0) {\n // When progress is set to 0, restore initial values and render from state.\n // OPTIMIZATION: Skip the expensive restore+render cycle if we've already\n // rendered at position 0 and haven't moved away since. The restore path\n // clears the transform cache, and re-rendering re-hydrates it via\n // getComputedStyle() — causing massive layout thrashing when called every\n // frame (e.g., scroll scrub for headings not yet in view).\n if (this._atZeroState) {\n // Already rendered at 0 and haven't moved — skip\n } else {\n this._restoreInitialValues();\n this._renderPositionZeroAnimations();\n this._atZeroState = true;\n }\n } else {\n this._atZeroState = false;\n this.update(0);\n }\n\n // Fire onUpdate callback when progress is explicitly set (e.g., scroll scrubbing)\n // This ensures playhead sync even when timeline isn't \"active\"\n this._onUpdate?.(this._progress, this._time);\n\n return this;\n }\n\n /**\n * Get or set time in seconds\n */\n time(): number;\n time(value: number): this;\n time(value?: number): number | this {\n if (value === undefined) {\n if (this._eachInstances && this._eachInstances.length > 0) {\n let maxTime = 0;\n for (const inst of this._eachInstances) {\n const t = inst.time();\n if (t > maxTime) maxTime = t;\n }\n return maxTime;\n }\n return this._time;\n }\n\n if (this._eachInstances) {\n for (const inst of this._eachInstances) inst.time(value);\n return this;\n }\n\n this._time = Math.max(0, Math.min(value, this._duration));\n this._progress = this._duration > 0 ? this._time / this._duration : 0;\n\n // Update all children\n this.update(0);\n\n return this;\n }\n\n /**\n * Get or set time scale (speed multiplier)\n */\n timeScale(): number;\n timeScale(value: number): this;\n timeScale(value?: number): number | this {\n if (value === undefined) {\n return this._timeScale;\n }\n\n this._timeScale = value;\n\n return this;\n }\n\n /**\n * Kill timeline and all children, reset all state\n * @param clearProps - If true (default), restore elements to their initial CSS values\n *\n * Any text splits owned by this timeline's builders are reverted via\n * SDKRegistry.textSplitter.revert() (reference-counted — safe when\n * other timelines still share the split element).\n */\n kill(clearProps: boolean = true): void {\n this._killed = true;\n this._isActive = false;\n this._atZeroState = false;\n\n // Clean up attached triggers (including page load)\n SDKRegistry.triggerManager?.getInstance?.().unregister(this);\n\n // Clean up each-mode instances\n if (this._eachInstances) {\n for (const instance of this._eachInstances) {\n instance.kill(clearProps);\n }\n this._eachInstances = undefined;\n }\n\n // Restore CSS transitions and clear saved data before restoring initial values\n this._clearTransitionData();\n\n // Restore initial CSS values BEFORE killing animations\n // This resets elements to their state before the timeline was created\n // Check both _initialValues (CSS props) AND _transformElements (transform props like rotation from path)\n if (clearProps && (this._initialValues.size > 0 || this._transformElements.size > 0)) {\n this._restoreInitialValues();\n }\n\n // Restore parent opacity overrides from split+mask animations.\n // Without this, the inline opacity: 1 set for split text visibility\n // leaks across timeline rebuilds, preventing proper CSS state restoration.\n if (clearProps && this._splitParentOverrides.size > 0) {\n for (const [element, originalOpacity] of this._splitParentOverrides) {\n if (originalOpacity) {\n element.style.opacity = originalOpacity;\n } else {\n element.style.removeProperty('opacity');\n }\n }\n this._splitParentOverrides.clear();\n }\n\n // Revert text splits owned by this timeline's builders (symmetric with each-mode).\n // Uses SDKRegistry.textSplitter.revert() which is reference-counted — safe when\n // another timeline still has the same element registered as a consumer.\n // Deduplicate via Set: stagger creates multiple TimelineChild entries sharing one builder.\n const splitBuilders = new Set(\n this._children\n .filter(c => c.type === 'animation' && c.builder?.hasSplit())\n .map(c => c.builder!),\n );\n if (splitBuilders.size > 0) {\n for (const builder of splitBuilders) {\n for (const target of builder.getOriginalTargets()) {\n if (target instanceof Element) {\n SDKRegistry.textSplitter?.revert?.(target, builder);\n }\n }\n }\n }\n\n // Release and kill all child animations (returns them to pool)\n for (const child of this._children) {\n if (child.type === 'animation') {\n const animation = child.child as Animation;\n // Release from timeline ownership so it can be pooled\n animation.setTimelineChild(false);\n animation.kill();\n } else if (child.type === 'timeline') {\n (child.child as Timeline).kill(clearProps);\n }\n }\n\n // Unregister from Engine if this is a named timeline\n if (this._unregisterCallback) {\n this._unregisterCallback();\n this._unregisterCallback = undefined;\n }\n\n // Reset all state so timeline can be reused cleanly\n this._children = [];\n this._initialValues.clear();\n this._initialTransforms.clear();\n this._transformElements.clear();\n this._savedTransitions.clear();\n this._splitParentOverrides.clear();\n this._transitionsDisabled = false;\n this._duration = 0;\n this._time = 0;\n this._progress = 0;\n this._previousStart = 0;\n this._previousEnd = 0;\n this._isReversed = false;\n this._startFired = false;\n // Reset repeat state\n this._currentRepeat = 0;\n this._completeFired = false;\n this._inRepeatDelay = false;\n this._repeatDelayCounter = 0;\n }\n\n /**\n * Check if timeline is currently active.\n */\n isActive(): boolean {\n if (this._eachInstances) {\n return this._eachInstances.some(inst => inst.isActive());\n }\n return this._isActive;\n }\n\n /**\n * Check if timeline is currently playing in reverse direction\n */\n reversed(): boolean {\n if (this._eachInstances) {\n return this._eachInstances.some(inst => inst.reversed());\n }\n return this._isReversed;\n }\n\n /**\n * Get timeline name\n */\n getName(): string | undefined {\n return this._name;\n }\n\n /**\n * Get first child animation (for trigger target resolution).\n * @internal\n */\n getFirstChildAnimation(): Animation | null {\n const firstChild = this._children[0];\n if (firstChild?.type === 'animation') {\n return firstChild.child as Animation;\n }\n return null;\n }\n\n /**\n * Get first child builder (for split-aware trigger target resolution).\n * When text splitting is active, the builder holds original pre-split targets.\n * @internal\n */\n getFirstChildBuilder(): AnimationBuilder | null {\n const firstChild = this._children[0];\n if (firstChild?.type === 'animation' && firstChild.builder) {\n return firstChild.builder;\n }\n return null;\n }\n}\n\n\n",
|
|
33
33
|
"/**\n * QuickSetter - High-performance property setter for per-frame updates\n *\n * Bypasses the full animation pipeline for maximum speed.\n * Used by CursorTrigger for smooth position tracking.\n */\n\nimport { setTransformValue, buildTransformString } from '../render/TransformCache';\nimport { isTransformProp } from './PropertyParser';\n\nexport type QuickSetterFn = (value: number) => void;\n\n/**\n * Create a high-performance property setter for per-frame updates.\n * Returns a function that sets the value directly without animation overhead.\n *\n * @param target - The element to animate\n * @param property - CSS property name (camelCase)\n * @param unit - Unit to append (default: 'px' for transforms, '' for others)\n */\nexport function createQuickSetter(\n target: Element,\n property: string,\n unit?: string\n): QuickSetterFn {\n const htmlEl = target as HTMLElement;\n\n // Transform properties use the cache system\n if (isTransformProp(property)) {\n const resolvedUnit = unit ?? (property.startsWith('scale') ? '' : 'px');\n return (value: number) => {\n setTransformValue(target, property, value, resolvedUnit);\n htmlEl.style.transform = buildTransformString(target);\n };\n }\n\n // CSS custom properties (variables)\n if (property.startsWith('--')) {\n const resolvedUnit = unit ?? '';\n return (value: number) => {\n htmlEl.style.setProperty(property, `${value}${resolvedUnit}`);\n };\n }\n\n // Regular CSS properties\n const kebabProp = property.replace(/([A-Z])/g, '-$1').toLowerCase();\n const resolvedUnit = unit ?? '';\n\n return (value: number) => {\n htmlEl.style.setProperty(kebabProp, `${value}${resolvedUnit}`);\n };\n}\n\n",
|
|
34
34
|
"/**\n * CursorTrigger - Custom cursor controller\n *\n * Features:\n * - Smooth position tracking with configurable lag\n * - State-based animations (default, hover, click)\n * - Velocity-based squeeze effect\n * - Text and media cursor variants\n */\n\nimport type { CursorConfig, CursorStateVars, CursorSqueezeConfig } from '../types';\nimport { Timeline } from '../core/Timeline';\nimport { Ticker } from '../core/Ticker';\nimport { createQuickSetter, type QuickSetterFn } from '../utils/QuickSetter';\nimport { camelToKebab, isTransformProp, parseValue, getDefaultUnit } from '../utils/PropertyParser';\nimport { setTransformValue, buildTransformString } from '../render/TransformCache';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\n\ntype CursorState = 'default' | 'hover' | 'click';\n\n/**\n * CSS properties that accept bare numbers without a unit.\n * Everything else gets `px` appended when the value is a plain number.\n */\nconst UNITLESS_CSS_PROPS = new Set([\n 'opacity',\n 'z-index',\n 'font-weight',\n 'line-height',\n 'flex',\n 'flex-grow',\n 'flex-shrink',\n 'order',\n 'scale',\n 'column-count',\n 'columns',\n 'tab-size',\n 'orphans',\n 'widows',\n 'zoom',\n]);\n\n/**\n * Validate that a media URL is safe to load (http/https or relative path).\n * Blocks file://, data:, javascript:, and internal network URLs.\n */\nfunction isSafeMediaUrl(src: string): boolean {\n // SSR guard: if location is unavailable, only allow relative paths (no protocol)\n if (typeof location === 'undefined') {\n return !src.includes(':');\n }\n try {\n const url = new URL(src, location.href);\n return url.protocol === 'https:' || url.protocol === 'http:';\n } catch {\n // Relative paths without a protocol are safe\n return !src.includes(':');\n }\n}\n\ninterface ParsedStateConfig {\n properties: Record<string, string | number>;\n duration: number;\n ease: string;\n targets?: string[];\n enabled: boolean;\n}\n\nexport class CursorTrigger extends BaseTrigger<CursorConfig> {\n private _element: HTMLElement | null = null;\n private _innerElement: HTMLElement | null = null;\n\n // Position tracking\n private _mousePos = { x: -100, y: -100 };\n private _cursorPos = { x: -100, y: -100 };\n private _xSetter: QuickSetterFn | null = null;\n private _ySetter: QuickSetterFn | null = null;\n\n // Squeeze effect\n private _squeezeSetter: QuickSetterFn | null = null;\n private _squeezeConfig: CursorSqueezeConfig = { min: 0.55, max: 1.2, multiplier: 150 };\n\n // State management\n private _state: CursorState = 'default';\n private _stateTimelines: Map<CursorState, Timeline> = new Map();\n private _parsedStates: Map<CursorState, ParsedStateConfig> = new Map();\n\n // Special cursor elements\n private _textElement: HTMLElement | null = null;\n private _mediaElement: HTMLImageElement | HTMLVideoElement | null = null;\n\n // Event listeners\n private _tickerCallback?: (dt: number) => void;\n private _boundMouseMove?: (e: MouseEvent) => void;\n private _boundMouseDown?: () => void;\n private _boundMouseUp?: () => void;\n\n // Hover live hit-testing state\n private _hoverSelectors: string[] | null = null;\n private _isHoveringCursorTarget: boolean = false;\n private _boundHoverTargetMoveHandler: ((e: MouseEvent) => void) | null = null;\n\n // Style tag for native cursor hiding (overrides child-element cursor styles)\n private _cursorStyleTag: HTMLStyleElement | null = null;\n\n // Text/media attribute listeners\n private _attributeListeners: Map<Element, { enter: (e: Event) => void; leave: () => void }> = new Map();\n\n constructor(timeline: Timeline, config: CursorConfig) {\n super(timeline, config);\n this._config = {\n type: 'basic',\n smooth: 0.25,\n hideNative: false,\n ...config,\n };\n\n // Parse squeeze config\n if (config.squeeze === true) {\n this._squeezeConfig = { min: 0.55, max: 1.2, multiplier: 150 };\n } else if (typeof config.squeeze === 'object') {\n this._squeezeConfig = {\n min: config.squeeze.min ?? 0.55,\n max: config.squeeze.max ?? 1.2,\n multiplier: config.squeeze.multiplier ?? 150,\n };\n }\n\n // Parse state configs\n this._parsedStates.set('default', this._parseStateConfig(config.default));\n if (config.hover) {\n this._parsedStates.set('hover', this._parseStateConfig(config.hover));\n }\n if (config.click) {\n this._parsedStates.set('click', this._parseStateConfig(config.click));\n }\n }\n\n private _parseStateConfig(config: CursorStateVars): ParsedStateConfig {\n const { targets, duration, ease, enabled, ...properties } = config;\n return {\n properties: properties as Record<string, string | number>,\n duration: duration ?? 0.15,\n ease: ease ?? 'power3.inOut',\n targets,\n enabled: enabled ?? true,\n };\n }\n\n /**\n * Set up the cursor: create container, create setters, attach listeners, start ticker.\n *\n * Architecture (mirrors original GSAP cursor):\n * - Container (_element): position:fixed, zero-size, moved via x/y QuickSetters\n * - Inner element (_innerElement): centered via translate(-50%, -50%),\n * receives all visual CSS properties and state transitions\n */\n protected _onEnable(): void {\n // Create the container element — SDK owns it entirely\n this._element = this._createContainer();\n\n // Create inner element for visual styling, centered on the container origin.\n // Centering uses TransformCache (not inline CSS) so it composes correctly\n // with transform animations (e.g. scale) on hover/click states.\n this._innerElement = document.createElement('div');\n this._innerElement.setAttribute('data-cursor-inner', '');\n this._innerElement.style.cssText = 'pointer-events:none;position:absolute;top:0;left:0;';\n this._element.appendChild(this._innerElement);\n\n // Set centering via TransformCache so it composes with transform animations\n setTransformValue(this._innerElement, 'x', -50, '%');\n setTransformValue(this._innerElement, 'y', -50, '%');\n\n // Create quick setters for position on the container\n this._xSetter = createQuickSetter(this._element, 'x', 'px');\n this._ySetter = createQuickSetter(this._element, 'y', 'px');\n\n // Create squeeze setter if enabled (on inner element for visual effect)\n if (this._config.squeeze) {\n this._squeezeSetter = createQuickSetter(this._element, 'scale');\n }\n\n // Set initial position (off-screen)\n this._cursorPos = { x: -100, y: -100 };\n this._mousePos = { x: -100, y: -100 };\n this._xSetter(-100);\n this._ySetter(-100);\n\n // Apply default properties to the inner element\n // (may include transform shorthand which gets expanded via TransformCache)\n this._applyProperties(this._parsedStates.get('default')!.properties);\n\n // Ensure centering transform (and any default transforms) are flushed to DOM\n this._innerElement.style.transform = buildTransformString(this._innerElement);\n\n // Hide native cursor across all elements (including interactive ones that\n // have their own cursor styles — style tag with !important overrides them all)\n if (this._config.hideNative) {\n this._cursorStyleTag = document.createElement('style');\n this._cursorStyleTag.setAttribute('data-cursor-hide', '');\n this._cursorStyleTag.textContent = '* { cursor: none !important; }';\n document.head.appendChild(this._cursorStyleTag);\n }\n\n // Setup special cursor types\n if (this._config.type === 'text') {\n this._setupTextCursor();\n } else if (this._config.type === 'media') {\n this._setupMediaCursor();\n }\n\n // Create state transition timelines\n this._createStateTimelines();\n\n // Add event listeners\n this._setupEventListeners();\n\n // Start ticker for position updates\n this._tickerCallback = (dt) => this._tick(dt);\n Ticker.getInstance().add(this._tickerCallback);\n }\n\n /**\n * Tear down the cursor: remove ticker, restore native cursor, remove listeners.\n */\n protected _onDisable(): void {\n // Remove ticker\n if (this._tickerCallback) {\n Ticker.getInstance().remove(this._tickerCallback);\n this._tickerCallback = undefined;\n }\n\n // Restore native cursor by removing the injected style tag\n if (this._cursorStyleTag) {\n this._cursorStyleTag.remove();\n this._cursorStyleTag = null;\n }\n\n // Remove event listeners\n this._removeEventListeners();\n\n // Kill state timelines\n this._stateTimelines.forEach(tl => tl.kill());\n this._stateTimelines.clear();\n\n // Remove inner element from DOM\n if (this._innerElement?.parentNode) {\n this._innerElement.remove();\n }\n this._innerElement = null;\n\n // Remove container element from DOM — SDK always owns the container it created.\n if (this._element?.parentNode) {\n this._element.remove();\n }\n this._element = null;\n }\n\n /**\n * Create the cursor container element and append it to document.body.\n * The SDK always owns this container; it is removed in _onDisable().\n */\n private _createContainer(): HTMLElement {\n const container = document.createElement('div');\n container.setAttribute('data-cursor-container', '');\n container.style.cssText = 'position:fixed;top:0;left:0;pointer-events:none;z-index:999999;width:0;height:0;';\n document.body.appendChild(container);\n return container;\n }\n\n private _applyProperties(properties: Record<string, string | number>): void {\n if (!this._innerElement) return;\n\n for (const [key, value] of Object.entries(properties)) {\n if (key === 'transform') {\n // Expand CSS transform shorthand (e.g. \"scale(2)\") into individual\n // SDK transform properties and apply via TransformCache so they\n // compose with the centering translate(-50%, -50%).\n const expanded = CursorTrigger._expandTransformShorthand(String(value));\n for (const [prop, val] of Object.entries(expanded)) {\n setTransformValue(this._innerElement!, prop, val);\n }\n // Flush is handled by _onEnable after this call\n } else if (isTransformProp(key)) {\n // Route SDK transform properties (scale, x, y, rotate, etc.) through\n // TransformCache so they compose with the centering translate(-50%, -50%).\n // Using style.setProperty() writes a standalone CSS property that doesn't\n // compose correctly with the TransformCache-managed transform string.\n const parsed =\n typeof value === 'number'\n ? { value, unit: getDefaultUnit(key) }\n : parseValue(value);\n setTransformValue(\n this._innerElement!,\n key,\n parsed.value,\n parsed.unit || getDefaultUnit(key) || undefined,\n );\n } else {\n const kebab = camelToKebab(key);\n const cssValue =\n typeof value === 'number' && !UNITLESS_CSS_PROPS.has(kebab)\n ? `${value}px`\n : String(value);\n this._innerElement.style.setProperty(kebab, cssValue);\n }\n }\n }\n\n private _createStateTimelines(): void {\n // State transitions target the inner element (visual styling)\n if (!this._innerElement) return;\n\n // Create hover timeline\n const hoverConfig = this._parsedStates.get('hover');\n if (hoverConfig && hoverConfig.enabled) {\n const hoverTl = new Timeline();\n hoverTl._addEntry(this._innerElement, {\n to: CursorTrigger._expandStateProperties(hoverConfig.properties),\n duration: hoverConfig.duration,\n ease: hoverConfig.ease,\n });\n this._stateTimelines.set('hover', hoverTl);\n }\n\n // Create click timeline\n const clickConfig = this._parsedStates.get('click');\n if (clickConfig && clickConfig.enabled) {\n const clickTl = new Timeline();\n clickTl._addEntry(this._innerElement, {\n to: CursorTrigger._expandStateProperties(clickConfig.properties),\n duration: clickConfig.duration,\n ease: clickConfig.ease,\n });\n this._stateTimelines.set('click', clickTl);\n }\n }\n\n /**\n * Expand CSS transform shorthand in state properties into individual\n * SDK transform properties that the animation system can interpolate.\n * E.g., {transform: 'scale(2)', width: '80px'} → {scale: 2, width: '80px'}\n */\n private static _expandStateProperties(properties: Record<string, string | number>): Record<string, string | number> {\n const result: Record<string, string | number> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (key === 'transform') {\n const expanded = CursorTrigger._expandTransformShorthand(String(value));\n Object.assign(result, expanded);\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n\n /**\n * Parse CSS transform shorthand string into individual SDK transform properties.\n * Handles: scale, scaleX/Y, rotate, rotateX/Y/Z, skewX/Y\n * E.g., \"scale(2)\" → {scale: 2}\n * \"scale(2, 3)\" → {scaleX: 2, scaleY: 3}\n * \"scale(2) rotate(45deg)\" → {scale: 2, rotate: 45}\n */\n private static _expandTransformShorthand(transformStr: string): Record<string, number> {\n const result: Record<string, number> = {};\n const regex = /([a-zA-Z0-9]+)\\(([^)]+)\\)/g;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(transformStr)) !== null) {\n const fn = match[1];\n const args = match[2].split(',').map(s => parseFloat(s.trim()));\n\n switch (fn) {\n case 'scale': {\n const x = args[0] ?? 1;\n const y = args.length > 1 ? args[1] : x;\n if (x === y) {\n result.scale = x;\n } else {\n result.scaleX = x;\n result.scaleY = y;\n }\n break;\n }\n case 'scaleX':\n result.scaleX = args[0] ?? 1;\n break;\n case 'scaleY':\n result.scaleY = args[0] ?? 1;\n break;\n case 'scaleZ':\n result.scaleZ = args[0] ?? 1;\n break;\n case 'rotate':\n result.rotate = args[0] ?? 0;\n break;\n case 'rotateX':\n result.rotateX = args[0] ?? 0;\n break;\n case 'rotateY':\n result.rotateY = args[0] ?? 0;\n break;\n case 'rotateZ':\n result.rotateZ = args[0] ?? 0;\n break;\n case 'skewX':\n result.skewX = args[0] ?? 0;\n break;\n case 'skewY':\n result.skewY = args[0] ?? 0;\n break;\n }\n }\n\n return result;\n }\n\n private _setupEventListeners(): void {\n // Mouse move (always needed for position tracking)\n this._boundMouseMove = (e) => {\n this._mousePos.x = e.clientX;\n this._mousePos.y = e.clientY;\n };\n document.addEventListener('mousemove', this._boundMouseMove);\n\n // Mouse down/up for click state\n const clickConfig = this._parsedStates.get('click');\n if (clickConfig?.enabled) {\n this._boundMouseDown = () => this._setState('click');\n this._boundMouseUp = () => this._setState(this._state === 'click' ? 'default' : this._state);\n document.addEventListener('mousedown', this._boundMouseDown);\n document.addEventListener('mouseup', this._boundMouseUp);\n }\n\n // Hover targets\n const hoverConfig = this._parsedStates.get('hover');\n if (hoverConfig?.enabled && hoverConfig.targets && hoverConfig.targets.length > 0) {\n this._setupHoverTargets(hoverConfig.targets);\n }\n }\n\n private _setupHoverTargets(selectors: string[]): void {\n // Store selectors for live matching instead of frozen rects.\n // elementsFromPoint always reflects current layout regardless of scroll,\n // layout shifts, or elements added after setup (React SPA timing).\n // The cursor container has pointer-events:none so it is transparent to\n // elementsFromPoint — we correctly see the elements beneath the cursor.\n this._hoverSelectors = selectors;\n\n this._boundHoverTargetMoveHandler = (e: MouseEvent) => {\n const elements = document.elementsFromPoint(e.clientX, e.clientY);\n const isOver = elements.some(el =>\n this._hoverSelectors!.some(selector => {\n try {\n return el.matches(selector);\n } catch {\n return false;\n }\n })\n );\n\n if (isOver && !this._isHoveringCursorTarget) {\n this._isHoveringCursorTarget = true;\n this._setState('hover');\n } else if (!isOver && this._isHoveringCursorTarget) {\n this._isHoveringCursorTarget = false;\n if (this._state === 'hover') {\n this._setState('default');\n }\n }\n };\n\n window.addEventListener('mousemove', this._boundHoverTargetMoveHandler);\n }\n\n private _setupTextCursor(): void {\n if (!this._innerElement) return;\n\n // Style inner element for text cursor: grid centering + overflow visible\n // so text is visible even when the inner element is 0x0\n // (matches old GSAP TextCursor: display:grid, placeItems:center)\n this._innerElement.style.display = 'grid';\n this._innerElement.style.placeItems = 'center';\n this._innerElement.style.overflow = 'visible';\n\n // Create text element inside the inner cursor element\n this._textElement = document.createElement('div');\n this._textElement.setAttribute('data-cursor-text', '');\n // Prevent text wrapping inside zero-width container, start invisible\n this._textElement.style.whiteSpace = 'nowrap';\n this._textElement.style.opacity = '0';\n this._innerElement.appendChild(this._textElement);\n\n // Get transition duration from hover config for opacity animation\n const hoverConfig = this._parsedStates.get('hover');\n const transitionDuration = hoverConfig?.duration ?? 0.15;\n this._textElement.style.transition = `opacity ${transitionDuration}s ease-in-out`;\n\n // Apply text styles if provided\n if (this._config.text) {\n for (const [key, value] of Object.entries(this._config.text)) {\n this._textElement.style.setProperty(camelToKebab(key), String(value));\n }\n }\n\n // Setup attribute-based text triggers\n // Support both mp-cursor-text and mp-cursor-tooltip attributes\n const textElements = document.querySelectorAll('[mp-cursor-text], [mp-cursor-tooltip]');\n for (const el of textElements) {\n const enter = (e: Event) => {\n const target = e.target as Element;\n const text = target.getAttribute('mp-cursor-text')\n ?? target.getAttribute('mp-cursor-tooltip')\n ?? '';\n if (this._textElement) {\n this._textElement.textContent = text;\n this._textElement.style.opacity = '1';\n }\n this._setState('hover');\n };\n const leave = () => {\n if (this._textElement) {\n this._textElement.style.opacity = '0';\n }\n if (this._state === 'hover') {\n this._setState('default');\n }\n };\n\n el.addEventListener('mouseenter', enter);\n el.addEventListener('mouseleave', leave);\n this._attributeListeners.set(el, { enter, leave });\n }\n }\n\n private _setupMediaCursor(): void {\n if (!this._innerElement) return;\n\n // Create image element inside the inner cursor element\n const img = document.createElement('img');\n img.setAttribute('data-cursor-media', '');\n img.style.display = 'none';\n this._innerElement.appendChild(img);\n\n const video = document.createElement('video');\n video.setAttribute('data-cursor-media', '');\n video.autoplay = true;\n video.muted = true;\n video.loop = true;\n video.style.display = 'none';\n this._innerElement.appendChild(video);\n\n // Apply media styles if provided\n if (this._config.media) {\n for (const [key, value] of Object.entries(this._config.media)) {\n img.style.setProperty(camelToKebab(key), String(value));\n video.style.setProperty(camelToKebab(key), String(value));\n }\n }\n\n // Setup attribute-based media triggers\n const mediaElements = document.querySelectorAll('[mp-cursor-media]');\n for (const el of mediaElements) {\n const enter = (e: Event) => {\n const src = (e.target as Element).getAttribute('mp-cursor-media') || '';\n if (!src || !isSafeMediaUrl(src)) {\n if (src) console.warn(`[Motion] Blocked unsafe media URL: \"${src}\". Only http/https URLs and relative paths are allowed.`);\n return;\n }\n const isVideo = src.endsWith('.mp4') || src.endsWith('.webm');\n\n if (isVideo) {\n video.src = src;\n video.style.display = 'block';\n img.style.display = 'none';\n video.play().catch(() => {});\n this._mediaElement = video;\n } else {\n img.src = src;\n img.style.display = 'block';\n video.style.display = 'none';\n this._mediaElement = img;\n }\n this._setState('hover');\n };\n const leave = () => {\n img.style.display = 'none';\n video.style.display = 'none';\n video.pause();\n if (this._state === 'hover') {\n this._setState('default');\n }\n };\n\n el.addEventListener('mouseenter', enter);\n el.addEventListener('mouseleave', leave);\n this._attributeListeners.set(el, { enter, leave });\n }\n }\n\n private _removeEventListeners(): void {\n if (this._boundMouseMove) {\n document.removeEventListener('mousemove', this._boundMouseMove);\n }\n if (this._boundMouseDown) {\n document.removeEventListener('mousedown', this._boundMouseDown);\n }\n if (this._boundMouseUp) {\n document.removeEventListener('mouseup', this._boundMouseUp);\n }\n\n // Remove hover mousemove handler\n if (this._boundHoverTargetMoveHandler) {\n window.removeEventListener('mousemove', this._boundHoverTargetMoveHandler);\n this._boundHoverTargetMoveHandler = null;\n }\n this._hoverSelectors = null;\n this._isHoveringCursorTarget = false;\n\n // Remove attribute listeners\n this._attributeListeners.forEach((listeners, el) => {\n el.removeEventListener('mouseenter', listeners.enter);\n el.removeEventListener('mouseleave', listeners.leave);\n });\n this._attributeListeners.clear();\n }\n\n private _setState(newState: CursorState): void {\n if (this._state === newState) return;\n\n const prevState = this._state;\n this._state = newState;\n\n // Get the timeline for the new state\n const newTimeline = this._stateTimelines.get(newState);\n const prevTimeline = this._stateTimelines.get(prevState);\n\n if (newState === 'default') {\n // Return to default - reverse the previous state timeline\n if (prevTimeline) {\n prevTimeline.reverse();\n }\n } else {\n // Enter new state\n // If coming from a different non-default state, reverse the old one\n if (prevState !== 'default' && prevTimeline) {\n prevTimeline.reverse();\n }\n\n if (newTimeline) {\n // Use play() instead of restart() to resume from current position.\n // This prevents a visual jump when hovering between adjacent elements:\n // mouseleave on A starts reversing, mouseenter on B resumes forward\n // from wherever the timeline currently is — matching GSAP's overwrite behavior.\n newTimeline.play();\n }\n }\n }\n\n private _tick(deltaTime: number): void {\n if (!this._xSetter || !this._ySetter) return;\n\n // Smooth interpolation matching original GSAP cursor behavior:\n // smooth 0 = most delay/smoothing, smooth 1 = instant tracking\n // Uses power curve (2.5) for natural feel, frame-rate independent\n const smooth = this._config.smooth ?? 0.25;\n const rescaled = Math.pow(smooth, 2.5) || 0.000001;\n const lerpFactor = 1 - Math.pow(1 - rescaled, deltaTime * 60);\n\n // Update cursor position\n this._cursorPos.x += (this._mousePos.x - this._cursorPos.x) * lerpFactor;\n this._cursorPos.y += (this._mousePos.y - this._cursorPos.y) * lerpFactor;\n\n // Apply position\n this._xSetter(this._cursorPos.x);\n this._ySetter(this._cursorPos.y);\n\n // Apply squeeze effect if enabled\n if (this._squeezeSetter && this._config.squeeze) {\n const scale = this._calculateSqueeze();\n this._squeezeSetter(scale);\n }\n\n // Tick state transition timelines (they are unnamed, so not registered\n // with Engine and must be updated manually)\n for (const tl of this._stateTimelines.values()) {\n if (tl.isActive()) {\n tl.update(deltaTime);\n }\n }\n }\n\n private _calculateSqueeze(): number {\n const dx = this._cursorPos.x - this._mousePos.x;\n const dy = this._cursorPos.y - this._mousePos.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n const { min, max, multiplier } = this._squeezeConfig;\n const scale = 1 - (Math.exp(distance / multiplier!) - 1) / 10;\n\n if (Number.isNaN(scale)) {\n return 1;\n }\n\n return Math.max(min!, Math.min(max!, scale));\n }\n\n /**\n * Get the cursor element\n */\n getElement(): HTMLElement | null {\n return this._element;\n }\n\n /**\n * Get current state\n */\n getState(): CursorState {\n return this._state;\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('cursor', CursorTrigger);\n",
|
|
35
35
|
"/**\n * PageExitTrigger\n *\n * Triggers timeline playback when a user clicks a link that would navigate away\n * from the current page. Intercepts the click, plays the exit animation, then\n * navigates to the target URL after the timeline completes.\n *\n * Supports three link selection modes:\n * - 'all': intercept all navigation links (default)\n * - 'include': only intercept links matching specific selectors\n * - 'exclude': intercept all links except those matching specific selectors\n *\n * Supports skipping certain href patterns:\n * - 'anchor': skip # links (same-page anchors)\n * - 'javascript': skip javascript: hrefs\n * - 'mailto': skip mailto: and tel: hrefs\n *\n * Works on all websites — no server-side dependencies (PHP, Node, etc.).\n */\n\nimport type { Timeline } from '../core/Timeline';\nimport { TriggerManager } from './TriggerManager';\nimport { BaseTrigger } from './BaseTrigger';\n\n/** Configuration for PageExitTrigger */\nexport interface PageExitTriggerConfig {\n /** Link selection mode: 'all' (default), 'include', or 'exclude' */\n mode?: 'all' | 'include' | 'exclude';\n /** CSS selector(s) for links — required for 'include'/'exclude' modes */\n selectors?: string;\n /** Href patterns to skip (never intercept) */\n skipHref?: ('anchor' | 'javascript' | 'mailto')[];\n}\n\nexport class PageExitTrigger extends BaseTrigger<PageExitTriggerConfig> {\n private _links: HTMLAnchorElement[] = [];\n private _listeners: Map<EventTarget, Map<string, EventListener>> = new Map();\n private _isNavigating: boolean = false;\n private _pendingUrl: string | null = null;\n private _onCompleteHandler: (() => void) | null = null;\n private _pollTimerId: ReturnType<typeof setTimeout> | null = null;\n\n constructor(timeline: Timeline, config: PageExitTriggerConfig) {\n super(timeline, config);\n }\n\n /**\n * Set up click listeners on qualifying links once DOM is ready.\n */\n protected _onEnable(): void {\n // Skip inside builder iframe\n if (this._isInsideBuilder()) return;\n\n this._resolveLinks();\n this._addClickListeners();\n }\n\n /**\n * Remove all click listeners and cancel pending navigation.\n */\n protected _onDisable(): void {\n this._cleanupListenerMap(this._listeners);\n this._links = [];\n this._isNavigating = false;\n this._pendingUrl = null;\n\n // Cancel pending poll timer\n if (this._pollTimerId !== null) {\n clearTimeout(this._pollTimerId);\n this._pollTimerId = null;\n }\n\n // Remove onComplete callback from timeline\n if (this._onCompleteHandler) {\n this._onCompleteHandler = null;\n }\n }\n\n /**\n * Detect if we're running inside the Motion.page builder iframe.\n * Exit animations should not intercept clicks in the builder.\n */\n private _isInsideBuilder(): boolean {\n try {\n if (typeof window !== 'undefined' && (window as any).top?.mp_iframe) {\n return true;\n }\n } catch {\n // Cross-origin access to top — not in builder\n }\n return false;\n }\n\n /**\n * Resolve which <a> elements should be intercepted based on config mode.\n */\n private _resolveLinks(): void {\n if (typeof document === 'undefined') return;\n\n const mode = this._config.mode || 'all';\n\n let candidates: HTMLAnchorElement[];\n\n if (mode === 'include' && this._config.selectors && this._config.selectors.trim() !== '') {\n // Only links matching the selectors\n try {\n candidates = Array.from(\n document.querySelectorAll<HTMLAnchorElement>(this._config.selectors)\n ).filter(el => el.tagName === 'A');\n\n // If selectors don't specifically target <a> tags, also find <a> descendants\n const containers = document.querySelectorAll(this._config.selectors);\n containers.forEach(container => {\n if (container.tagName !== 'A') {\n const nested = container.querySelectorAll<HTMLAnchorElement>('a');\n candidates.push(...Array.from(nested));\n }\n });\n } catch (e) {\n console.warn(`[Motion] Invalid selector \"${this._config.selectors}\":`, e);\n candidates = [];\n }\n } else if (mode === 'include') {\n // Include mode with missing or empty selectors — no links to intercept\n candidates = [];\n } else if (mode === 'exclude' && this._config.selectors && this._config.selectors.trim() !== '') {\n // All links except those matching selectors\n const allLinks = Array.from(document.querySelectorAll<HTMLAnchorElement>('a'));\n let excluded: Set<Element>;\n try {\n excluded = new Set(document.querySelectorAll(this._config.selectors));\n } catch (e) {\n console.warn(`[Motion] Invalid selector \"${this._config.selectors}\":`, e);\n excluded = new Set();\n }\n candidates = allLinks.filter(link => !excluded.has(link));\n } else {\n // 'all' mode or 'exclude' without selectors — all links on the page\n candidates = Array.from(document.querySelectorAll<HTMLAnchorElement>('a'));\n }\n\n // Deduplicate\n this._links = [...new Set(candidates)];\n\n // Filter out links whose href should be skipped\n this._links = this._links.filter(link => this._shouldIntercept(link));\n }\n\n /**\n * Determine if a link should be intercepted based on its href and skipHref config.\n * Skips links that don't actually navigate away.\n */\n private _shouldIntercept(link: HTMLAnchorElement): boolean {\n const href = link.getAttribute('href');\n\n // No href at all — skip\n if (!href || href === '') return false;\n\n const skipHref = this._config.skipHref || [];\n\n // Anchor links (same-page navigation)\n if (href.startsWith('#')) {\n return !skipHref.includes('anchor');\n }\n\n // javascript: protocol\n if (href.toLowerCase().startsWith('javascript:')) {\n return !skipHref.includes('javascript');\n }\n\n // mailto: and tel: protocols\n if (href.toLowerCase().startsWith('mailto:') || href.toLowerCase().startsWith('tel:')) {\n return !skipHref.includes('mailto');\n }\n\n return true;\n }\n\n /**\n * Attach click listeners to all resolved links.\n */\n private _addClickListeners(): void {\n this._links.forEach(link => {\n const elementListeners = new Map<string, EventListener>();\n\n const clickListener = (e: Event) => {\n // Don't intercept if already navigating (animation in progress)\n if (this._isNavigating) return;\n\n // Don't intercept if modifier keys are held (open in new tab, etc.)\n const mouseEvent = e as MouseEvent;\n if (mouseEvent.ctrlKey || mouseEvent.metaKey || mouseEvent.shiftKey || mouseEvent.altKey) {\n return;\n }\n\n // Don't intercept if link has target=\"_blank\" or similar\n const target = link.getAttribute('target');\n if (target && target !== '_self') return;\n\n // Get the navigation URL\n const href = link.href; // Use .href (resolved absolute URL), not getAttribute('href')\n if (!href) return;\n\n // Prevent default navigation\n e.preventDefault();\n e.stopPropagation();\n\n // Store the target URL and start exit animation\n this._isNavigating = true;\n this._pendingUrl = href;\n\n // Set up completion handler to navigate after animation finishes\n this._onCompleteHandler = () => {\n this._navigate();\n };\n\n // Play the timeline from start\n this._timeline.restart();\n\n // Listen for timeline completion\n // We use a polling approach since Timeline doesn't expose onComplete externally\n this._waitForCompletion();\n };\n\n link.addEventListener('click', clickListener);\n elementListeners.set('click', clickListener);\n this._listeners.set(link, elementListeners);\n });\n }\n\n /**\n * Wait for the timeline to complete, then navigate.\n * Uses requestAnimationFrame polling to check timeline progress.\n */\n private _waitForCompletion(): void {\n const timeline = this._timeline;\n const checkInterval = 16; // ~60fps check\n\n const check = () => {\n this._pollTimerId = null;\n\n // Bail if disabled or no longer navigating\n if (!this._enabled || !this._isNavigating || !this._pendingUrl) return;\n\n // Check if timeline has completed (progress >= 1 or not active)\n const progress = timeline.progress();\n const isActive = timeline.isActive();\n\n if (progress >= 1 || (!isActive && progress > 0)) {\n // Timeline finished — navigate\n this._navigate();\n } else {\n // Keep checking\n this._pollTimerId = setTimeout(check, checkInterval);\n }\n };\n\n // Start checking after a short delay to let the timeline begin\n this._pollTimerId = setTimeout(check, checkInterval);\n }\n\n /**\n * Navigate to the pending URL.\n */\n private _navigate(): void {\n if (!this._pendingUrl) return;\n\n const url = this._pendingUrl;\n this._pendingUrl = null;\n this._isNavigating = false;\n\n if (typeof window !== 'undefined') {\n window.location.href = url;\n }\n }\n}\n\n// Register with TriggerManager\nTriggerManager.registerTrigger('pageExit', PageExitTrigger);\n",
|
|
36
|
-
"/**\n * ColorParser - Parse any color format to RGBA\n *\n * Supports:\n * - Hex: #ff0000, #f00, #ff0000ff, #f00f\n * - RGB/RGBA: rgb(255, 0, 0), rgba(255, 0, 0, 0.5)\n * - HSL/HSLA: hsl(0, 100%, 50%), hsla(0, 100%, 50%, 0.5)\n * - Named colors: red, coral, transparent\n * - CSS variables: var(--color)\n */\n\n// Cached canvas context for browser-native color parsing\nlet canvasCtx: CanvasRenderingContext2D | null = null;\n\n/**\n * Get or create cached canvas context\n */\nfunction getCanvasContext(): CanvasRenderingContext2D | null {\n if (!canvasCtx && typeof document !== 'undefined') {\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n canvasCtx = canvas.getContext('2d');\n }\n return canvasCtx;\n}\n\n/**\n * Parse hex color to RGBA\n * Supports: #rgb, #rrggbb, #rgba, #rrggbbaa\n */\nfunction parseHex(hex: string): Float32Array | null {\n const h = hex.replace('#', '');\n let r: number, g: number, b: number, a = 1;\n\n if (h.length === 3) {\n // #rgb\n r = parseInt(h[0] + h[0], 16);\n g = parseInt(h[1] + h[1], 16);\n b = parseInt(h[2] + h[2], 16);\n } else if (h.length === 4) {\n // #rgba\n r = parseInt(h[0] + h[0], 16);\n g = parseInt(h[1] + h[1], 16);\n b = parseInt(h[2] + h[2], 16);\n a = parseInt(h[3] + h[3], 16) / 255;\n } else if (h.length === 6) {\n // #rrggbb\n r = parseInt(h.slice(0, 2), 16);\n g = parseInt(h.slice(2, 4), 16);\n b = parseInt(h.slice(4, 6), 16);\n } else if (h.length === 8) {\n // #rrggbbaa\n r = parseInt(h.slice(0, 2), 16);\n g = parseInt(h.slice(2, 4), 16);\n b = parseInt(h.slice(4, 6), 16);\n a = parseInt(h.slice(6, 8), 16) / 255;\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(a)) {\n return null;\n }\n\n return new Float32Array([r, g, b, a]);\n}\n\n/**\n * Parse rgb() or rgba() color string\n */\nfunction parseRgb(color: string): Float32Array | null {\n const match = color.match(/rgba?\\s*\\(\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)\\s*(?:[,\\/]\\s*([\\d.]+))?\\s*\\)/i);\n if (!match) return null;\n\n const r = parseFloat(match[1]);\n const g = parseFloat(match[2]);\n const b = parseFloat(match[3]);\n const a = match[4] !== undefined ? parseFloat(match[4]) : 1;\n\n if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(a)) {\n return null;\n }\n\n return new Float32Array([r, g, b, a]);\n}\n\n/**\n * Convert HSL to RGB\n * h: 0-360, s: 0-100, l: 0-100\n */\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n h = h / 360;\n s = s / 100;\n l = l / 100;\n\n let r: number, g: number, b: number;\n\n if (s === 0) {\n r = g = b = l;\n } else {\n const hue2rgb = (p: number, q: number, t: number): number => {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n };\n\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n const p = 2 * l - q;\n r = hue2rgb(p, q, h + 1 / 3);\n g = hue2rgb(p, q, h);\n b = hue2rgb(p, q, h - 1 / 3);\n }\n\n return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];\n}\n\n/**\n * Parse hsl() or hsla() color string\n */\nfunction parseHsl(color: string): Float32Array | null {\n const match = color.match(/hsla?\\s*\\(\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)%\\s*[,\\s]\\s*([\\d.]+)%\\s*(?:[,\\/]\\s*([\\d.]+))?\\s*\\)/i);\n if (!match) return null;\n\n const h = parseFloat(match[1]);\n const s = parseFloat(match[2]);\n const l = parseFloat(match[3]);\n const a = match[4] !== undefined ? parseFloat(match[4]) : 1;\n\n if (isNaN(h) || isNaN(s) || isNaN(l) || isNaN(a)) {\n return null;\n }\n\n const [r, g, b] = hslToRgb(h, s, l);\n return new Float32Array([r, g, b, a]);\n}\n\n/**\n * Parse CSS variable by resolving its computed value
|
|
36
|
+
"/**\n * ColorParser - Parse any color format to RGBA\n *\n * Supports:\n * - Hex: #ff0000, #f00, #ff0000ff, #f00f\n * - RGB/RGBA: rgb(255, 0, 0), rgba(255, 0, 0, 0.5)\n * - HSL/HSLA: hsl(0, 100%, 50%), hsla(0, 100%, 50%, 0.5)\n * - Named colors: red, coral, transparent\n * - CSS variables: var(--color)\n */\n\n// Cached canvas context for browser-native color parsing\nlet canvasCtx: CanvasRenderingContext2D | null = null;\n\n/**\n * Get or create cached canvas context\n */\nfunction getCanvasContext(): CanvasRenderingContext2D | null {\n if (!canvasCtx && typeof document !== 'undefined') {\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n canvasCtx = canvas.getContext('2d');\n }\n return canvasCtx;\n}\n\n/**\n * Parse hex color to RGBA\n * Supports: #rgb, #rrggbb, #rgba, #rrggbbaa\n */\nfunction parseHex(hex: string): Float32Array | null {\n const h = hex.replace('#', '');\n let r: number, g: number, b: number, a = 1;\n\n if (h.length === 3) {\n // #rgb\n r = parseInt(h[0] + h[0], 16);\n g = parseInt(h[1] + h[1], 16);\n b = parseInt(h[2] + h[2], 16);\n } else if (h.length === 4) {\n // #rgba\n r = parseInt(h[0] + h[0], 16);\n g = parseInt(h[1] + h[1], 16);\n b = parseInt(h[2] + h[2], 16);\n a = parseInt(h[3] + h[3], 16) / 255;\n } else if (h.length === 6) {\n // #rrggbb\n r = parseInt(h.slice(0, 2), 16);\n g = parseInt(h.slice(2, 4), 16);\n b = parseInt(h.slice(4, 6), 16);\n } else if (h.length === 8) {\n // #rrggbbaa\n r = parseInt(h.slice(0, 2), 16);\n g = parseInt(h.slice(2, 4), 16);\n b = parseInt(h.slice(4, 6), 16);\n a = parseInt(h.slice(6, 8), 16) / 255;\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(a)) {\n return null;\n }\n\n return new Float32Array([r, g, b, a]);\n}\n\n/**\n * Parse rgb() or rgba() color string\n */\nfunction parseRgb(color: string): Float32Array | null {\n const match = color.match(/rgba?\\s*\\(\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)\\s*(?:[,\\/]\\s*([\\d.]+))?\\s*\\)/i);\n if (!match) return null;\n\n const r = parseFloat(match[1]);\n const g = parseFloat(match[2]);\n const b = parseFloat(match[3]);\n const a = match[4] !== undefined ? parseFloat(match[4]) : 1;\n\n if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(a)) {\n return null;\n }\n\n return new Float32Array([r, g, b, a]);\n}\n\n/**\n * Convert HSL to RGB\n * h: 0-360, s: 0-100, l: 0-100\n */\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n h = h / 360;\n s = s / 100;\n l = l / 100;\n\n let r: number, g: number, b: number;\n\n if (s === 0) {\n r = g = b = l;\n } else {\n const hue2rgb = (p: number, q: number, t: number): number => {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n };\n\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n const p = 2 * l - q;\n r = hue2rgb(p, q, h + 1 / 3);\n g = hue2rgb(p, q, h);\n b = hue2rgb(p, q, h - 1 / 3);\n }\n\n return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];\n}\n\n/**\n * Parse hsl() or hsla() color string\n */\nfunction parseHsl(color: string): Float32Array | null {\n const match = color.match(/hsla?\\s*\\(\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)%\\s*[,\\s]\\s*([\\d.]+)%\\s*(?:[,\\/]\\s*([\\d.]+))?\\s*\\)/i);\n if (!match) return null;\n\n const h = parseFloat(match[1]);\n const s = parseFloat(match[2]);\n const l = parseFloat(match[3]);\n const a = match[4] !== undefined ? parseFloat(match[4]) : 1;\n\n if (isNaN(h) || isNaN(s) || isNaN(l) || isNaN(a)) {\n return null;\n }\n\n const [r, g, b] = hslToRgb(h, s, l);\n return new Float32Array([r, g, b, a]);\n}\n\n/**\n * Parse CSS variable by resolving its computed value.\n * Supports both `var(--name)` and `var(--name, fallback)` syntax.\n * If the variable is undefined, the fallback value (if provided) is parsed instead.\n */\nfunction parseCssVariable(color: string, element?: Element): Float32Array | null {\n if (!element || typeof window === 'undefined') return null;\n\n // Greedy .+ lets the final \\) match the outermost closing paren,\n // so fallback values with parens like rgb(255, 0, 0) are captured correctly.\n const match = color.match(/var\\s*\\(\\s*(--[\\w-]+)\\s*(?:,\\s*(.+))?\\s*\\)\\s*$/i);\n if (!match) return null;\n\n const varName = match[1];\n const computed = window.getComputedStyle(element).getPropertyValue(varName).trim();\n\n if (computed) {\n // Recursively parse the resolved value\n return parseColor(computed, element);\n }\n\n // Variable not defined — try fallback value if provided\n const fallback = match[2]?.trim();\n if (fallback) {\n return parseColor(fallback, element);\n }\n\n return null;\n}\n\n/**\n * Parse color using browser's canvas context\n * Handles named colors and validates any color string\n */\nfunction parseWithCanvas(color: string): Float32Array | null {\n const ctx = getCanvasContext();\n if (!ctx) return null;\n\n // Reset to known state\n ctx.fillStyle = '#000000';\n ctx.fillStyle = color;\n\n const result = ctx.fillStyle;\n\n // If result is still black and input wasn't black, invalid color\n if (result === '#000000' && color.toLowerCase() !== 'black' && color !== '#000000' && color !== '#000') {\n return null;\n }\n\n // Canvas returns hex, parse it\n return parseHex(result);\n}\n\n/**\n * Parse any color format to RGBA Float32Array\n * Returns [R, G, B, A] where RGB is 0-255 and A is 0-1\n */\nexport function parseColor(color: string, element?: Element): Float32Array | null {\n if (!color || typeof color !== 'string') return null;\n\n const trimmed = color.trim().toLowerCase();\n\n // Handle transparent\n if (trimmed === 'transparent') {\n return new Float32Array([0, 0, 0, 0]);\n }\n\n // Try hex\n if (trimmed.startsWith('#')) {\n return parseHex(trimmed);\n }\n\n // Try rgb/rgba\n if (trimmed.startsWith('rgb')) {\n return parseRgb(trimmed);\n }\n\n // Try hsl/hsla\n if (trimmed.startsWith('hsl')) {\n return parseHsl(trimmed);\n }\n\n // Try CSS variable\n if (trimmed.startsWith('var(')) {\n return parseCssVariable(trimmed, element);\n }\n\n // Fallback to canvas for named colors\n return parseWithCanvas(trimmed);\n}\n\n/**\n * Convert RGBA Float32Array to CSS rgba() string\n */\nexport function rgbaToString(rgba: Float32Array): string {\n const r = Math.round(rgba[0]);\n const g = Math.round(rgba[1]);\n const b = Math.round(rgba[2]);\n const a = rgba[3];\n\n // Use rgb() if fully opaque for cleaner output\n if (a === 1) {\n return `rgb(${r}, ${g}, ${b})`;\n }\n\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\n/**\n * Get current color value from element's computed style\n */\nexport function getCurrentColor(element: Element, property: string): Float32Array {\n if (typeof window === 'undefined') {\n return new Float32Array([0, 0, 0, 1]);\n }\n\n const computed = window.getComputedStyle(element);\n const kebabProp = property.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);\n let value = computed.getPropertyValue(kebabProp).trim();\n\n // Freshly-inserted elements (e.g. split text spans) may not have\n // computed styles yet. Force a reflow and retry before falling back.\n if (!value || value === 'none') {\n void (element as HTMLElement).offsetHeight;\n value = window.getComputedStyle(element).getPropertyValue(kebabProp).trim();\n }\n\n if (!value || value === 'none') {\n // Default to transparent for unset colors\n return new Float32Array([0, 0, 0, 0]);\n }\n\n const parsed = parseColor(value, element);\n return parsed || new Float32Array([0, 0, 0, 1]);\n}\n\n// Register with SDKRegistry for conditional bundling\nimport { SDKRegistry } from '../registries/SDKRegistry';\nSDKRegistry.color = { parseColor, getCurrentColor };\n",
|
|
37
37
|
"/**\n * FilterParser - Parse CSS filter strings for animation\n *\n * Supports:\n * - blur(px)\n * - brightness(ratio)\n * - contrast(ratio)\n * - saturate(ratio)\n * - grayscale(ratio or %)\n * - sepia(ratio or %)\n * - hue-rotate(deg)\n * - invert(ratio or %)\n * - opacity(ratio or %)\n */\n\n/**\n * Parsed filter function with name, value, and unit\n */\nexport interface ParsedFilterFunction {\n name: string;\n value: number;\n unit: string;\n}\n\n/**\n * Default values for filter functions (when not specified)\n */\nconst FILTER_DEFAULTS: Record<string, { value: number; unit: string }> = {\n blur: { value: 0, unit: 'px' },\n brightness: { value: 1, unit: '' },\n contrast: { value: 1, unit: '' },\n saturate: { value: 1, unit: '' },\n grayscale: { value: 0, unit: '' },\n sepia: { value: 0, unit: '' },\n 'hue-rotate': { value: 0, unit: 'deg' },\n invert: { value: 0, unit: '' },\n opacity: { value: 1, unit: '' },\n};\n\n/**\n * Parse a single filter function like \"blur(10px)\" or \"brightness(1.5)\"\n */\nfunction parseFilterFunction(fn: string): ParsedFilterFunction | null {\n const match = fn.trim().match(/^([\\w-]+)\\(\\s*([\\d.]+)(px|%|deg)?\\s*\\)$/);\n if (!match) return null;\n\n const name = match[1].toLowerCase();\n let value = parseFloat(match[2]);\n let unit = match[3] || '';\n\n // Normalize percentage values to ratio (0-1) for consistency\n if (unit === '%') {\n value = value / 100;\n unit = '';\n }\n\n return { name, value, unit };\n}\n\n/**\n * Parse a complete filter string into an array of filter functions\n * Example: \"blur(10px) brightness(1.5)\" -> [{name: 'blur', value: 10, unit: 'px'}, ...]\n */\nexport function parseFilter(filter: string): ParsedFilterFunction[] | null {\n if (filter === null || filter === undefined || typeof filter !== 'string') return null;\n\n const trimmed = filter.trim().toLowerCase();\n\n // Handle 'none' or empty\n if (trimmed === 'none' || trimmed === '') {\n return [];\n }\n\n // Split by closing parenthesis followed by optional space\n // This handles \"blur(10px) brightness(1.5)\" correctly\n const functions: ParsedFilterFunction[] = [];\n const regex = /([\\w-]+)\\([^)]+\\)/g;\n let match;\n\n while ((match = regex.exec(trimmed)) !== null) {\n const parsed = parseFilterFunction(match[0]);\n if (parsed) {\n functions.push(parsed);\n }\n }\n\n return functions.length > 0 ? functions : null;\n}\n\n/**\n * Check if a filter function is at its identity (no-op) value.\n * Identity means the filter has no visual effect — e.g. blur(0px),\n * brightness(1), grayscale(0), etc.\n */\nfunction isIdentityFilter(f: ParsedFilterFunction): boolean {\n const def = FILTER_DEFAULTS[f.name];\n if (!def) return false;\n return f.value === def.value;\n}\n\n/**\n * Convert parsed filter functions back to CSS filter string.\n *\n * Returns 'none' when all filters are at their identity/default values\n * (e.g. blur(0px), brightness(1)). This is critical because any filter\n * value other than 'none' — even visually invisible ones like blur(0px) —\n * creates a CSS containing block that breaks position:fixed on descendants.\n * This would cause pinned sections to disappear during scroll animations.\n */\nexport function filterToString(filters: ParsedFilterFunction[]): string {\n if (!filters || filters.length === 0) {\n return 'none';\n }\n\n // If every filter is at its identity value, return 'none' to avoid\n // creating an unnecessary containing block on the element.\n if (filters.every(isIdentityFilter)) {\n return 'none';\n }\n\n return filters.map(f => {\n // Format value based on filter type\n let valueStr: string;\n if (f.name === 'blur') {\n valueStr = `${f.value}${f.unit || 'px'}`;\n } else if (f.name === 'hue-rotate') {\n valueStr = `${f.value}${f.unit || 'deg'}`;\n } else {\n // For ratio-based filters, output as number\n valueStr = `${f.value}`;\n }\n return `${f.name}(${valueStr})`;\n }).join(' ');\n}\n\n/**\n * Get current filter value from element's computed style\n */\nexport function getCurrentFilter(element: Element): ParsedFilterFunction[] {\n if (typeof window === 'undefined') {\n return [];\n }\n\n const computed = window.getComputedStyle(element);\n const filter = computed.filter || computed.getPropertyValue('filter');\n\n if (!filter || filter === 'none') {\n return [];\n }\n\n return parseFilter(filter) || [];\n}\n\n/**\n * Get default value for a filter function\n */\nfunction getFilterDefault(name: string): ParsedFilterFunction {\n const def = FILTER_DEFAULTS[name];\n if (def) {\n return { name, value: def.value, unit: def.unit };\n }\n // Unknown filter, default to 0\n return { name, value: 0, unit: '' };\n}\n\n/**\n * Merge two filter arrays, ensuring all functions from both are present\n * Missing functions get their default values\n */\nexport function mergeFilterArrays(\n start: ParsedFilterFunction[],\n end: ParsedFilterFunction[]\n): { start: ParsedFilterFunction[]; end: ParsedFilterFunction[] } {\n // Collect all unique filter names\n const allNames = new Set<string>();\n for (const f of start) allNames.add(f.name);\n for (const f of end) allNames.add(f.name);\n\n // Build merged arrays with same order and length\n const mergedStart: ParsedFilterFunction[] = [];\n const mergedEnd: ParsedFilterFunction[] = [];\n\n for (const name of allNames) {\n const startFn = start.find(f => f.name === name);\n const endFn = end.find(f => f.name === name);\n const defaultFn = getFilterDefault(name);\n\n mergedStart.push(startFn || defaultFn);\n mergedEnd.push(endFn || defaultFn);\n }\n\n return { start: mergedStart, end: mergedEnd };\n}\n\n// Pre-allocated array for interpolateFilters to avoid per-frame allocation\nlet filterResultBuffer: ParsedFilterFunction[] = [];\n\n/**\n * Interpolate between two filter arrays at given progress.\n * Reuses a module-level buffer to avoid allocating a new array every frame.\n * NOTE: The returned array is reused on the next call — consume it immediately.\n */\nexport function interpolateFilters(\n start: ParsedFilterFunction[],\n end: ParsedFilterFunction[],\n progress: number\n): ParsedFilterFunction[] {\n const len = start.length;\n\n // Grow the buffer if needed (shrinking is unnecessary — extra slots are harmless)\n if (filterResultBuffer.length < len) {\n for (let i = filterResultBuffer.length; i < len; i++) {\n filterResultBuffer.push({ name: '', value: 0, unit: '' });\n }\n }\n\n for (let i = 0; i < len; i++) {\n const s = start[i];\n const e = end[i];\n const slot = filterResultBuffer[i];\n\n slot.name = s.name;\n slot.value = s.value + (e.value - s.value) * progress;\n slot.unit = s.unit || e.unit;\n }\n\n // Set length to trim any extra slots from previous larger calls\n filterResultBuffer.length = len;\n\n return filterResultBuffer;\n}\n\n// Register with SDKRegistry for conditional bundling\nimport { SDKRegistry } from '../registries/SDKRegistry';\nSDKRegistry.filter = { parseFilter, getCurrentFilter, mergeFilterArrays, interpolateFilters, filterToString };\n",
|
|
38
38
|
"/**\n * DrawSVGParser - Parse and animate SVG stroke drawing\n *\n * Handles:\n * - Percentage values: \"0% 100%\", \"20% 80%\"\n * - Pixel values: \"100px 500px\"\n * - Object format: { start: 0, end: 100 }\n * - Single values: \"50%\" (draws from 0 to 50%)\n *\n * Uses stroke-dasharray and stroke-dashoffset for performant rendering.\n */\n\n/**\n * Parsed DrawSVG values (normalized to 0-1 range)\n */\nexport interface ParsedDrawSVG {\n start: number; // 0-1 normalized start position\n end: number; // 0-1 normalized end position\n}\n\n/**\n * DrawSVG input value types\n */\ntype DrawSVGValue = string | { start?: number; end?: number };\n\n// Cache for SVG path lengths (expensive to calculate)\nconst pathLengthCache = new WeakMap<Element, number>();\n\n/**\n * Check if an element supports getTotalLength (SVG geometry elements)\n */\nfunction hasTotalLength(element: Element): element is SVGGeometryElement {\n return 'getTotalLength' in element && typeof (element as SVGGeometryElement).getTotalLength === 'function';\n}\n\n/**\n * Get the total length of an SVG element's stroke path.\n * Cached per element for performance.\n */\nexport function getPathLength(element: Element): number {\n // Check cache first\n const cached = pathLengthCache.get(element);\n if (cached !== undefined) {\n return cached;\n }\n\n // Calculate length if element supports it\n if (hasTotalLength(element)) {\n const length = element.getTotalLength();\n pathLengthCache.set(element, length);\n return length;\n }\n\n // Fallback for elements without getTotalLength\n return 0;\n}\n\n/**\n * Clear cached path length for an element.\n * Call when SVG path data changes.\n */\nexport function clearPathLengthCache(element: Element): void {\n pathLengthCache.delete(element);\n}\n\n/**\n * Parse a DrawSVG value string or object into normalized 0-1 values.\n *\n * Supported formats:\n * - \"0% 100%\" - percentage range\n * - \"100px 500px\" - pixel range (requires element for length)\n * - \"50%\" - single percentage (0 to value)\n * - \"50\" - single number treated as percentage\n * - { start: 0, end: 100 } - object with percentages\n */\nexport function parseDrawSVG(value: DrawSVGValue, element?: Element): ParsedDrawSVG | null {\n if (value === null || value === undefined) {\n return null;\n }\n\n // Handle object format { start: number, end: number }\n if (typeof value === 'object') {\n const start = value.start ?? 0;\n const end = value.end ?? 100;\n return {\n start: start / 100,\n end: end / 100,\n };\n }\n\n // Handle string format\n if (typeof value === 'string') {\n const trimmed = value.trim();\n\n // Handle \"true\" keyword (full draw)\n if (trimmed === 'true') {\n return { start: 0, end: 1 };\n }\n\n // Handle \"false\" or empty (no draw)\n if (trimmed === 'false' || trimmed === '' || trimmed === 'none') {\n return { start: 0, end: 0 };\n }\n\n // Try to parse as two values: \"start end\"\n // Matches: \"0% 100%\", \"0 100\", \"100px 500px\", \"0% 100px\"\n const twoValueMatch = trimmed.match(/^([-\\d.]+)(px|%)?\\s+([-\\d.]+)(px|%)?$/);\n\n if (twoValueMatch) {\n const startVal = parseFloat(twoValueMatch[1]);\n const startUnit = twoValueMatch[2] || '%';\n const endVal = parseFloat(twoValueMatch[3]);\n const endUnit = twoValueMatch[4] || '%';\n\n const length = element ? getPathLength(element) : 0;\n\n const start = normalizeValue(startVal, startUnit, length);\n const end = normalizeValue(endVal, endUnit, length);\n\n return { start, end };\n }\n\n // Try to parse as single value: \"50%\" or \"50\"\n const singleValueMatch = trimmed.match(/^([-\\d.]+)(px|%)?$/);\n\n if (singleValueMatch) {\n const val = parseFloat(singleValueMatch[1]);\n const unit = singleValueMatch[2] || '%';\n const length = element ? getPathLength(element) : 0;\n\n const end = normalizeValue(val, unit, length);\n\n return { start: 0, end };\n }\n }\n\n return null;\n}\n\n/**\n * Normalize a value to 0-1 range based on unit\n */\nfunction normalizeValue(value: number, unit: string, pathLength: number): number {\n if (unit === 'px' && pathLength > 0) {\n // Pixel value - convert to ratio of total length\n return value / pathLength;\n }\n // Percentage or unitless - treat as percentage\n return value / 100;\n}\n\n/**\n * Get the current DrawSVG state from an element's computed style.\n * Parses existing stroke-dasharray and stroke-dashoffset.\n */\nexport function getCurrentDrawSVG(element: Element): ParsedDrawSVG {\n const length = getPathLength(element);\n\n if (length === 0) {\n return { start: 0, end: 1 };\n }\n\n try {\n if (typeof window !== 'undefined' && window.getComputedStyle) {\n const computed = window.getComputedStyle(element);\n const dashArray = computed.strokeDasharray;\n const dashOffset = computed.strokeDashoffset;\n\n // If no dash array set, stroke is fully visible\n if (!dashArray || dashArray === 'none') {\n return { start: 0, end: 1 };\n }\n\n // Parse dash array (format: \"drawLength totalLength\" or just \"drawLength\")\n const dashParts = dashArray.split(/[\\s,]+/).map(parseFloat).filter(n => !isNaN(n));\n const offset = parseFloat(dashOffset) || 0;\n\n if (dashParts.length >= 1) {\n const drawLength = dashParts[0];\n // offset is stored as negative, so negate it back to get start position\n const start = -offset / length;\n const end = (-offset + drawLength) / length;\n return { start, end };\n }\n }\n } catch {\n // Fail gracefully in test environments\n }\n\n return { start: 0, end: 1 };\n}\n\n/**\n * Apply DrawSVG values to an element's style.\n * Sets stroke-dasharray and stroke-dashoffset for the draw effect.\n *\n * @param element - Target SVG element\n * @param start - Start position (0-1)\n * @param end - End position (0-1)\n * @param length - Pre-calculated path length\n */\nexport function applyDrawSVG(\n element: Element,\n start: number,\n end: number,\n length: number\n): void {\n if (!(element instanceof SVGElement) || length === 0) {\n return;\n }\n\n const htmlEl = element as unknown as { style: CSSStyleDeclaration };\n\n // Calculate the visible portion length\n const drawLength = Math.abs(end - start) * length;\n\n // Calculate offset (where the visible portion starts)\n const dashOffset = Math.min(start, end) * length;\n\n // Apply stroke-dasharray: [visible-length] [total-length]\n // This creates a single dash of the visible portion followed by a gap\n htmlEl.style.strokeDasharray = `${drawLength} ${length}`;\n\n // Apply stroke-dashoffset to position the dash\n // Negative offset moves the dash forward (start of visible portion)\n htmlEl.style.strokeDashoffset = `${-dashOffset}`;\n}\n\n// Register with SDKRegistry for conditional bundling\nimport { SDKRegistry } from '../registries/SDKRegistry';\nSDKRegistry.drawSVG = { parseDrawSVG, getCurrentDrawSVG, getPathLength, applyDrawSVG };\n",
|
|
39
|
-
"/**\n * TextSplitter - Split text into animatable elements\n *\n * Splits text content into chars, words, or lines wrapped in spans.\n * Supports nested splitting (e.g., 'chars,words' = words containing char spans).\n *\n * Preserves existing element structure — inline elements like\n * `<span class=\"accent\">word</span>` keep their wrapper and styling.\n * Only text nodes are replaced with split spans.\n *\n * Usage:\n * const elements = TextSplitter.split(element, 'chars');\n * const elements = TextSplitter.split(element, 'words');\n * const elements = TextSplitter.split(element, 'chars,words');\n *\n * Revert:\n * TextSplitter.revert(element);\n * TextSplitter.revert(element, consumer); // reference-counted\n */\n\nimport type { SplitType } from '../types';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nexport type { SplitType };\n\nexport interface SplitOptions {\n /** Wrap each split element in a parent with overflow:hidden for clip-reveal effects */\n mask?: boolean;\n /**\n * Opaque token identifying the caller (e.g. an AnimationBuilder instance).\n * When provided, splits are reference-counted: the DOM is only reverted once\n * all registered consumers release their claim via revert(element, consumer).\n * A second consumer on the same element triggers a smart DOM upgrade (nesting)\n * rather than destroying the first consumer's spans.\n */\n consumer?: object;\n}\n\ninterface SplitResult {\n chars: HTMLElement[];\n words: HTMLElement[];\n lines: HTMLElement[];\n elements: HTMLElement[]; // The innermost elements (what gets animated)\n}\n\n/**\n * Per-element split state, held in a module-level WeakMap.\n * Replaces the old originalContentMap + splitResultMap pair.\n */\ninterface ElementSplitState {\n /** The element's innerHTML captured before any split — always the true original. */\n originalHTML: string;\n /** The live split result arrays (mutated in-place on upgrades). */\n result: SplitResult;\n /**\n * Set of consumer tokens currently holding a reference to this split.\n * Empty set means no consumer tracking (single-timeline / legacy callers).\n */\n consumers: Set<object>;\n}\n\n// Single WeakMap that replaces both originalContentMap and splitResultMap\nconst splitStateMap = new WeakMap<Element, ElementSplitState>();\n\nexport class TextSplitter {\n /**\n * Split text content into animatable elements.\n *\n * When `options.consumer` is provided the split is reference-counted.\n * A second consumer on the same element will have the DOM upgraded\n * (chars nested inside existing word spans, or word-wraps promoted to\n * word spans) rather than reverted and rebuilt, so the first consumer's\n * DOM references remain valid.\n *\n * Without a consumer token the function behaves exactly like before:\n * any existing split is reverted and a fresh split is performed.\n */\n static split(element: Element, type: SplitType, options?: SplitOptions): HTMLElement[] {\n if (!(element instanceof HTMLElement)) {\n return [];\n }\n\n const consumer = options?.consumer;\n const existingState = splitStateMap.get(element);\n\n if (existingState) {\n // ── Same consumer, same type ───────────────────────────────────────────\n // No-op: the DOM is already correct for this consumer. Return the spans\n // that match the requested type from the live result.\n if (consumer && existingState.consumers.has(consumer)) {\n const currentType = element.getAttribute('data-split') ?? '';\n if (currentType === type) {\n return this._getSpansForType(existingState.result, type);\n }\n // Same consumer requesting a different type (e.g. rebuildIfPooled with\n // changed config): remove the stale registration and fall through to\n // revert + re-split (handled below as sole consumer).\n existingState.consumers.delete(consumer);\n }\n\n // Register the new consumer so it holds a reference count.\n if (consumer) existingState.consumers.add(consumer);\n\n // ── Multi-consumer: attempt a non-destructive DOM upgrade ─────────────\n // Only attempt upgrade when there are multiple consumers — a second timeline\n // is actively using the existing split DOM and we must not destroy it.\n // For single-consumer or no-consumer callers, fall through to the classic\n // revert + re-split path (preserves the old single-timeline behaviour).\n if (existingState.consumers.size > 1) {\n const upgraded = this._tryUpgrade(element, existingState, type, options);\n if (upgraded !== null) return upgraded;\n\n // Upgrade failed (e.g. lines involved) — cannot safely destroy other\n // consumers' DOM refs, so return the best available spans.\n const types = type.split(',').map(t => t.trim()) as ('chars' | 'words' | 'lines')[];\n if (types.includes('chars') && existingState.result.chars.length > 0)\n return existingState.result.chars.slice();\n if (types.includes('words') && existingState.result.words.length > 0)\n return existingState.result.words.slice();\n return existingState.result.elements.slice();\n }\n\n // ── Single consumer (or no-consumer legacy call) ───────────────────────\n // Classic behaviour: revert to original, then do a fresh split.\n // Remove the consumer we just added — it will be re-registered below.\n if (consumer) existingState.consumers.delete(consumer);\n element.innerHTML = existingState.originalHTML;\n element.removeAttribute('data-split');\n splitStateMap.delete(element);\n // Fall through to fresh split; element.innerHTML is now the true original.\n }\n\n // ── Fresh split (first split, or after a full revert above) ─────────────\n const originalHTML = element.innerHTML;\n\n const result: SplitResult = {\n chars: [],\n words: [],\n lines: [],\n elements: [],\n };\n\n // Parse split type\n const types = type.split(',').map(t => t.trim()) as ('chars' | 'words' | 'lines')[];\n const needsChars = types.includes('chars');\n const needsWords = types.includes('words');\n const needsLines = types.includes('lines');\n\n if (needsLines) {\n // Lines require layout measurement after an initial word split.\n // Use DOM-walking for the initial word pass, then measure and wrap.\n this._splitWithLinesDom(element, needsWords, needsChars, result);\n } else if (needsWords && needsChars) {\n this._splitWordsWithCharsDom(element, result);\n } else if (needsWords) {\n this._splitWordsDom(element, result);\n } else if (needsChars) {\n this._splitCharsDom(element, result);\n }\n\n // Determine innermost elements (what gets animated with stagger)\n if (needsChars) {\n result.elements = result.chars;\n } else if (needsWords) {\n result.elements = result.words;\n } else if (needsLines) {\n result.elements = result.lines;\n }\n\n // Apply mask wrappers (overflow:hidden parents for clip-reveal effects)\n if (options?.mask && result.elements.length > 0) {\n this._applyMaskWrappers(result);\n }\n\n // Propagate gradient text styles (background-clip: text) through the split\n // span chain so gradient headings remain visible after splitting.\n this._propagateGradientText(element, result);\n\n // Store new state in the unified map\n splitStateMap.set(element, {\n originalHTML,\n result,\n consumers: new Set(consumer ? [consumer] : []),\n });\n\n // Mark element as split\n element.setAttribute('data-split', type);\n\n return result.elements;\n }\n\n /* ── Smart upgrade helpers ─────────────────────────────────────────────── */\n\n /**\n * Attempt to upgrade an existing split to satisfy a new type request without\n * destroying existing DOM references.\n *\n * Returns the appropriate span array on success, or null if the upgrade is\n * not possible (caller must fall back to a full revert + re-split).\n */\n private static _tryUpgrade(\n element: HTMLElement,\n state: ElementSplitState,\n type: SplitType,\n options?: SplitOptions\n ): HTMLElement[] | null {\n const existingType = element.getAttribute('data-split') ?? '';\n const existingTypes = existingType.split(',').map(t => t.trim());\n\n const requestedTypes = type.split(',').map(t => t.trim()) as ('chars' | 'words' | 'lines')[];\n const needsChars = requestedTypes.includes('chars');\n const needsWords = requestedTypes.includes('words');\n const needsLines = requestedTypes.includes('lines');\n\n const hasChars = existingTypes.includes('chars');\n const hasWords = existingTypes.includes('words');\n const hasLines = existingTypes.includes('lines');\n\n // ── Existing split already satisfies the entire request ────────────────\n if (\n (!needsChars || hasChars) &&\n (!needsWords || hasWords) &&\n (!needsLines || hasLines)\n ) {\n return this._getSpansForType(state.result, type);\n }\n\n // Lines involved: line splitting requires a full layout re-measurement.\n // Signal to caller that a fresh split is needed.\n if (needsLines || hasLines) {\n return null;\n }\n\n // ── Upgrade: existing 'words', need 'chars' ────────────────────────────\n // Nest char spans inside each existing word span.\n // The word spans remain in the DOM unchanged — the first consumer's refs stay valid.\n if (hasWords && !hasChars && needsChars) {\n const newChars: HTMLElement[] = [];\n for (const wordSpan of state.result.words) {\n this._nestCharsInElement(wordSpan, newChars);\n }\n state.result.chars = newChars;\n state.result.elements = newChars; // chars take priority over words\n\n const combinedType = this._buildTypeString(existingTypes, requestedTypes);\n element.setAttribute('data-split', combinedType);\n\n if (options?.mask) this._applyMaskWrappers(state.result);\n this._propagateGradientText(element, state.result);\n\n return needsChars ? newChars : state.result.words.slice();\n }\n\n // ── Upgrade: existing 'chars' (word-wrap spans), need 'words' ─────────\n // Promote implicit [data-split-word-wrap] spans to proper [data-split-word] spans.\n // The char spans inside them are untouched — the first consumer's refs stay valid.\n if (hasChars && !hasWords && needsWords) {\n const wordWraps = Array.from(\n element.querySelectorAll<HTMLElement>('[data-split-word-wrap]')\n );\n const newWords: HTMLElement[] = [];\n for (let i = 0; i < wordWraps.length; i++) {\n const ww = wordWraps[i];\n ww.removeAttribute('data-split-word-wrap');\n ww.setAttribute('data-split-word', '');\n ww.setAttribute('data-word-index', String(i));\n newWords.push(ww);\n }\n state.result.words = newWords;\n // elements stays as chars (chars have priority)\n\n const combinedType = this._buildTypeString(existingTypes, requestedTypes);\n element.setAttribute('data-split', combinedType);\n\n return needsChars ? state.result.chars.slice() : newWords;\n }\n\n // No upgrade path matched — signal caller to fall back to full revert.\n return null;\n }\n\n /**\n * Return the appropriate span array from a SplitResult for the requested type.\n * Chars have highest priority, then words, then lines.\n */\n private static _getSpansForType(result: SplitResult, type: string): HTMLElement[] {\n const types = type.split(',').map(t => t.trim());\n if (types.includes('chars') && result.chars.length > 0) return result.chars.slice();\n if (types.includes('words') && result.words.length > 0) return result.words.slice();\n if (types.includes('lines') && result.lines.length > 0) return result.lines.slice();\n return result.elements.slice();\n }\n\n /**\n * Build a combined data-split attribute value from existing and requested types,\n * ordered as: lines, words, chars (broadest → most granular).\n */\n private static _buildTypeString(\n existingTypes: string[],\n requestedTypes: string[]\n ): string {\n const all = new Set([...existingTypes, ...requestedTypes]);\n return (['lines', 'words', 'chars'] as const)\n .filter(t => all.has(t))\n .join(',');\n }\n\n /**\n * Walk a word span's text nodes and replace them with individual char spans,\n * appending each new span to the provided `chars` array.\n *\n * Used when upgrading an existing 'words' split to include 'chars'.\n * The word span itself is NOT moved or recreated — only its text node\n * children are swapped out, so existing references to the word span remain valid.\n */\n private static _nestCharsInElement(parent: HTMLElement, chars: HTMLElement[]): void {\n // Collect text nodes first (TreeWalker is invalidated by DOM mutation)\n const walker = document.createTreeWalker(parent, 0x4 /* NodeFilter.SHOW_TEXT */);\n const textNodes: Text[] = [];\n let node = walker.nextNode() as Text | null;\n while (node !== null) {\n textNodes.push(node);\n node = walker.nextNode() as Text | null;\n }\n\n for (const textNode of textNodes) {\n const text = textNode.textContent ?? '';\n if (!text) continue;\n\n const fragment = document.createDocumentFragment();\n for (const char of text) {\n if (/\\s/.test(char)) {\n // Preserve whitespace as a plain text node\n fragment.appendChild(document.createTextNode(char));\n } else {\n const span = document.createElement('span');\n span.setAttribute('data-split-char', '');\n span.setAttribute('data-char-index', String(chars.length));\n span.style.display = 'inline-block';\n span.textContent = char;\n chars.push(span);\n fragment.appendChild(span);\n }\n }\n\n textNode.parentNode?.replaceChild(fragment, textNode);\n }\n }\n\n /* ── DOM-walking split methods ─────────────────────────────────────────── */\n\n /**\n * Split text nodes into word spans, preserving existing element wrappers.\n * Recurses into child elements so `<span class=\"accent\">word</span>` keeps\n * its wrapper and the text inside is split within it.\n */\n private static _splitWordsDom(parent: HTMLElement, result: SplitResult): void {\n const children = Array.from(parent.childNodes);\n\n for (const child of children) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.textContent || '';\n if (!text) continue;\n\n const parts = text.split(/(\\s+)/);\n const fragment = document.createDocumentFragment();\n let hasContent = false;\n\n for (const part of parts) {\n if (/^\\s+$/.test(part)) {\n // Preserve whitespace between words\n fragment.appendChild(document.createTextNode(part.replace(/\\n/g, ' ')));\n hasContent = true;\n } else if (part) {\n const span = document.createElement('span');\n span.setAttribute('data-split-word', '');\n span.setAttribute('data-word-index', String(result.words.length));\n span.style.display = 'inline-block';\n span.textContent = part;\n result.words.push(span);\n fragment.appendChild(span);\n hasContent = true;\n }\n }\n\n if (hasContent) {\n parent.replaceChild(fragment, child);\n }\n } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n const el = child as HTMLElement;\n if (el.tagName === 'BR') continue; // preserve <br> as-is\n // Recurse — preserves wrapper element with all its classes/styles\n this._splitWordsDom(el, result);\n }\n }\n }\n\n /**\n * Split text nodes into character spans, preserving existing element wrappers.\n *\n * Characters are grouped by word inside implicit wrapper spans with\n * `white-space: nowrap` so the browser never breaks a word across lines\n * (matching GSAP SplitText behaviour). Only the char spans are exposed\n * as animatable elements — the word wrappers are transparent to consumers.\n */\n private static _splitCharsDom(parent: HTMLElement, result: SplitResult): void {\n const children = Array.from(parent.childNodes);\n\n for (const child of children) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.textContent || '';\n if (!text) continue;\n\n const parts = text.split(/(\\s+)/);\n const fragment = document.createDocumentFragment();\n\n for (const part of parts) {\n if (/^\\s+$/.test(part)) {\n // Preserve whitespace between words\n fragment.appendChild(document.createTextNode(part.replace(/\\n/g, ' ')));\n } else if (part) {\n // Wrap each word's chars in an implicit container to prevent mid-word breaks\n const wordWrap = document.createElement('span');\n wordWrap.setAttribute('data-split-word-wrap', '');\n wordWrap.style.display = 'inline-block';\n wordWrap.style.whiteSpace = 'nowrap';\n\n for (const char of part) {\n const span = document.createElement('span');\n span.setAttribute('data-split-char', '');\n span.setAttribute('data-char-index', String(result.chars.length));\n span.style.display = 'inline-block';\n span.textContent = char;\n result.chars.push(span);\n wordWrap.appendChild(span);\n }\n\n fragment.appendChild(wordWrap);\n }\n }\n\n parent.replaceChild(fragment, child);\n } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n const el = child as HTMLElement;\n if (el.tagName === 'BR') continue;\n this._splitCharsDom(el, result);\n }\n }\n }\n\n /**\n * Split text nodes into word spans containing character spans.\n * Preserves existing element wrappers.\n */\n private static _splitWordsWithCharsDom(parent: HTMLElement, result: SplitResult): void {\n const children = Array.from(parent.childNodes);\n\n for (const child of children) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.textContent || '';\n if (!text) continue;\n\n const parts = text.split(/(\\s+)/);\n const fragment = document.createDocumentFragment();\n\n for (const part of parts) {\n if (/^\\s+$/.test(part)) {\n fragment.appendChild(document.createTextNode(part.replace(/\\n/g, ' ')));\n } else if (part) {\n const wordSpan = document.createElement('span');\n wordSpan.setAttribute('data-split-word', '');\n wordSpan.setAttribute('data-word-index', String(result.words.length));\n wordSpan.style.display = 'inline-block';\n wordSpan.style.whiteSpace = 'nowrap';\n\n for (const char of part) {\n const charSpan = document.createElement('span');\n charSpan.setAttribute('data-split-char', '');\n charSpan.setAttribute('data-char-index', String(result.chars.length));\n charSpan.style.display = 'inline-block';\n charSpan.textContent = char;\n result.chars.push(charSpan);\n wordSpan.appendChild(charSpan);\n }\n\n result.words.push(wordSpan);\n fragment.appendChild(wordSpan);\n }\n }\n\n parent.replaceChild(fragment, child);\n } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n const el = child as HTMLElement;\n if (el.tagName === 'BR') continue;\n this._splitWordsWithCharsDom(el, result);\n }\n }\n }\n\n /**\n * Split with line detection (requires layout measurement).\n *\n * Strategy:\n * 1. DOM-walk to split into word spans (preserving element wrappers)\n * 2. Measure each word span's vertical position to group into lines\n * 3. Wrap each line group in a line span\n */\n private static _splitWithLinesDom(\n element: HTMLElement,\n needsWords: boolean,\n needsChars: boolean,\n result: SplitResult\n ): void {\n // Step 1: split into words (DOM-walking, preserves element structure)\n const wordResult: SplitResult = { chars: [], words: [], lines: [], elements: [] };\n this._splitWordsDom(element, wordResult);\n\n // Step 2: measure word positions and group into lines\n const lines: HTMLElement[][] = [];\n let currentLine: HTMLElement[] = [];\n let currentTop = -Infinity;\n\n for (const wordSpan of wordResult.words) {\n const rect = wordSpan.getBoundingClientRect();\n if (rect.top > currentTop + 2) {\n // New line (2px tolerance for rounding)\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n currentLine = [wordSpan];\n currentTop = rect.top;\n } else {\n currentLine.push(wordSpan);\n }\n }\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n // Step 3: rebuild with line wrappers, preserving inline element wrappers.\n // For each word, we capture its ancestor chain between it and the root element\n // so wrapper elements like <span class=\"text-primary\"> are serialized into the\n // rebuilt HTML. Wrappers that span multiple lines are cloned into each line.\n\n // Build a map of wrapper chains for each word span\n const wrapperChainMap = new Map<HTMLElement, HTMLElement[]>();\n for (const word of wordResult.words) {\n wrapperChainMap.set(word, this._getWrapperChain(word, element));\n }\n\n let html = '';\n let lineIndex = 0;\n let wordIndex = 0;\n let charIndex = 0;\n\n for (const lineWords of lines) {\n let lineHtml = '';\n let openWrappers: HTMLElement[] = [];\n\n for (let i = 0; i < lineWords.length; i++) {\n const wordSpan = lineWords[i];\n const wordText = wordSpan.textContent || '';\n const wrapperChain = wrapperChainMap.get(wordSpan) || [];\n\n // Find where current and new wrapper chains diverge\n let commonDepth = 0;\n while (\n commonDepth < openWrappers.length &&\n commonDepth < wrapperChain.length &&\n openWrappers[commonDepth] === wrapperChain[commonDepth]\n ) {\n commonDepth++;\n }\n\n // Close wrappers that are no longer active (deepest first)\n for (let j = openWrappers.length - 1; j >= commonDepth; j--) {\n lineHtml += `</${openWrappers[j].tagName.toLowerCase()}>`;\n }\n\n // Space between words (except before first word in line)\n if (i > 0) {\n lineHtml += ' ';\n }\n\n // Open new wrappers\n for (let j = commonDepth; j < wrapperChain.length; j++) {\n lineHtml += this._openTag(wrapperChain[j]);\n }\n\n // Serialize the word content\n if (needsChars) {\n let wordHtml = '';\n for (const char of wordText) {\n wordHtml += `<span data-split-char data-char-index=\"${charIndex}\" style=\"display:inline-block\">${this._escapeHtml(char)}</span>`;\n charIndex++;\n }\n if (needsWords) {\n lineHtml += `<span data-split-word data-word-index=\"${wordIndex}\" style=\"display:inline-block;white-space:nowrap\">${wordHtml}</span>`;\n } else {\n lineHtml += wordHtml;\n }\n } else if (needsWords) {\n lineHtml += `<span data-split-word data-word-index=\"${wordIndex}\" style=\"display:inline-block\">${this._escapeHtml(wordText)}</span>`;\n } else {\n lineHtml += this._escapeHtml(wordText);\n }\n\n wordIndex++;\n openWrappers = wrapperChain;\n }\n\n // Close any remaining open wrappers at end of line\n for (let j = openWrappers.length - 1; j >= 0; j--) {\n lineHtml += `</${openWrappers[j].tagName.toLowerCase()}>`;\n }\n\n html += `<span data-split-line data-line-index=\"${lineIndex}\" style=\"display:block\">${lineHtml}</span>`;\n lineIndex++;\n }\n\n element.innerHTML = html;\n\n // Collect results from the rebuilt DOM\n if (needsChars) {\n result.chars = Array.from(element.querySelectorAll('[data-split-char]')) as HTMLElement[];\n }\n if (needsWords) {\n result.words = Array.from(element.querySelectorAll('[data-split-word]')) as HTMLElement[];\n }\n result.lines = Array.from(element.querySelectorAll('[data-split-line]')) as HTMLElement[];\n }\n\n /* ── Mask wrappers ────────────────────────────────────────────────────── */\n\n /**\n * Wrap each split element in a parent with overflow:hidden for clip-reveal.\n * The wrapper clips content so animating y:'100%' creates a reveal effect.\n */\n private static _applyMaskWrappers(result: SplitResult): void {\n // Determine which level gets masks — the animatable elements\n const elements = result.elements;\n\n for (const el of elements) {\n const isLine = el.hasAttribute('data-split-line');\n const isWord = el.hasAttribute('data-split-word');\n\n // Create mask wrapper\n const mask = document.createElement('span');\n mask.setAttribute('data-split-mask', '');\n mask.style.overflow = 'hidden';\n mask.style.display = isLine ? 'block' : 'inline-block';\n\n // For inline elements, ensure vertical overflow is clipped properly\n if (!isLine) {\n mask.style.verticalAlign = isWord ? 'bottom' : 'bottom';\n }\n\n // Insert mask wrapper in the element's position, then move element inside\n el.parentNode!.insertBefore(mask, el);\n mask.appendChild(el);\n }\n }\n\n /* ── Gradient text propagation ─────────────────────────────────────────── */\n\n /**\n * Detect if the split element uses `background-clip: text` (gradient text)\n * and propagate the gradient through all generated spans so text stays visible.\n *\n * Without this, inline-block split spans inherit `-webkit-text-fill-color: transparent`\n * but NOT the parent's gradient background, making the text invisible.\n */\n private static _propagateGradientText(element: HTMLElement, result: SplitResult): void {\n const computed = window.getComputedStyle(element);\n const bgClip = computed.getPropertyValue('background-clip') ||\n computed.getPropertyValue('-webkit-background-clip');\n\n if (bgClip !== 'text') return;\n\n // Collect all generated spans that need the gradient: masks, words, word-wraps, chars, lines\n // data-split-word-wrap = implicit word wrappers in char-only split that break inheritance\n const targets = element.querySelectorAll<HTMLElement>(\n '[data-split-mask], [data-split-word], [data-split-word-wrap], [data-split-char], [data-split-line]'\n );\n\n for (const target of targets) {\n target.style.background = 'inherit';\n target.style.backgroundClip = 'text';\n target.style.setProperty('-webkit-background-clip', 'text');\n target.style.setProperty('-webkit-text-fill-color', 'transparent');\n }\n }\n\n /* ── Revert / query ───────────────────────────────────────────────────── */\n\n /**\n * Revert element to original content.\n *\n * When `consumer` is provided the revert is reference-counted: the DOM is\n * only restored once all registered consumers have released via this method.\n * If the consumer is the last one (or no consumer tracking is active), the\n * element's innerHTML is restored to the pre-split original immediately.\n *\n * Without `consumer` (legacy / forced revert), the DOM is restored regardless\n * of how many consumers are still registered. All consumer refs are cleared.\n */\n static revert(element: Element, consumer?: object): boolean {\n const state = splitStateMap.get(element);\n if (!state) return false;\n\n if (consumer) {\n state.consumers.delete(consumer);\n\n // Other consumers are still active — leave the DOM intact.\n if (state.consumers.size > 0) {\n return false;\n }\n }\n\n // Last consumer (or forced / no-consumer revert) — fully restore.\n if (!(element instanceof HTMLElement)) return false;\n\n element.innerHTML = state.originalHTML;\n element.removeAttribute('data-split');\n splitStateMap.delete(element);\n return true;\n }\n\n /**\n * Check if element has been split\n */\n static isSplit(element: Element): boolean {\n return element.hasAttribute('data-split');\n }\n\n /**\n * Get split result for an element\n */\n static getResult(element: Element): SplitResult | undefined {\n return splitStateMap.get(element)?.result;\n }\n\n /* ── Utilities ─────────────────────────────────────────────────────────── */\n\n /**\n * Get the chain of wrapper elements between a word span and the root element.\n * Returns outermost-first order (root's direct child → … → word's parent).\n */\n private static _getWrapperChain(word: HTMLElement, root: HTMLElement): HTMLElement[] {\n const chain: HTMLElement[] = [];\n let el = word.parentElement;\n while (el && el !== root) {\n chain.unshift(el);\n el = el.parentElement;\n }\n return chain;\n }\n\n /**\n * Serialize an element's opening tag, preserving all attributes (class, style, data-*, etc).\n */\n private static _openTag(el: HTMLElement): string {\n const tag = el.tagName.toLowerCase();\n let attrs = '';\n for (const attr of Array.from(el.attributes)) {\n attrs += ` ${attr.name}=\"${this._escapeHtml(attr.value)}\"`;\n }\n return `<${tag}${attrs}>`;\n }\n\n /**\n * Escape HTML special characters\n */\n private static _escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n}\n\n// Self-register with the registry\nSDKRegistry.textSplitter = {\n split: (element, type, options) => TextSplitter.split(element, type, options),\n revert: (element, consumer) => TextSplitter.revert(element, consumer),\n isSplit: TextSplitter.isSplit,\n};\n",
|
|
39
|
+
"/**\n * ClipPathParser - Parse and interpolate CSS clip-path shape functions\n *\n * Supports:\n * - circle(<radius> at <x> <y>)\n * - ellipse(<rx> <ry> at <x> <y>)\n * - inset(<top> <right>? <bottom>? <left>? round <radius>?)\n * - polygon(<x1> <y1>, <x2> <y2>, …)\n * - rect(<top> <right> <bottom> <left> round <radius>?)\n * - xywh(<x> <y> <w> <h> round <radius>?)\n *\n * Interpolation rules:\n * - From and To must use the same shape function — otherwise we snap to the\n * end value at progress >= 0.5 (no shape morphing across types).\n * - polygon vertex counts must match — otherwise we snap to the end value.\n * - Each numeric component is lerped while preserving the unit emitted in the\n * end value (start unit kept when end has none).\n *\n * Loaded conditionally through SDKRegistry so the bundler can drop it when\n * no animation uses clip-path.\n */\n\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type ClipPathShape =\n | 'circle'\n | 'ellipse'\n | 'inset'\n | 'polygon'\n | 'rect'\n | 'xywh';\n\n/**\n * A single numeric component with its CSS unit (e.g. 71 + \"%\" or 10 + \"px\").\n */\nexport interface ClipPathComponent {\n value: number;\n unit: string;\n}\n\n/**\n * Parsed clip-path shape function.\n *\n * `components` holds the ordered numeric values that get interpolated. The\n * meaning of each slot is shape-specific — see the assemble* helpers.\n *\n * `extras` carries non-numeric tokens (e.g. the `round` keyword for inset/rect/\n * xywh) that we preserve verbatim when re-serializing.\n */\nexport interface ParsedClipPath {\n shape: ClipPathShape;\n components: ClipPathComponent[];\n extras?: { hasRound?: boolean };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Parsing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Match a single number with optional CSS unit (px, %, em, rem, vw, vh, etc.) */\nconst NUMBER_UNIT_RE = /(-?\\d*\\.?\\d+)([a-z%]*)/gi;\n\nfunction parseNumberToken(token: string): ClipPathComponent | null {\n // Reset lastIndex on the shared regex before reading\n NUMBER_UNIT_RE.lastIndex = 0;\n const match = NUMBER_UNIT_RE.exec(token);\n if (!match) return null;\n const value = parseFloat(match[1]);\n if (!isFinite(value)) return null;\n return { value, unit: match[2] || '' };\n}\n\nfunction parseNumberList(input: string): ClipPathComponent[] {\n const result: ClipPathComponent[] = [];\n // Local regex — global regexes are stateful, don't share with parseNumberToken\n const re = /(-?\\d*\\.?\\d+)([a-z%]*)/gi;\n let m: RegExpExecArray | null;\n while ((m = re.exec(input)) !== null) {\n const value = parseFloat(m[1]);\n if (!isFinite(value)) continue;\n result.push({ value, unit: m[2] || '' });\n }\n return result;\n}\n\n/**\n * Parse a clip-path value string into a structured `ParsedClipPath`.\n * Returns null if the value is `none`, empty, or an unsupported shape.\n */\nexport function parseClipPath(value: string): ParsedClipPath | null {\n if (value === null || value === undefined || typeof value !== 'string') return null;\n const trimmed = value.trim();\n if (trimmed === '' || trimmed === 'none') return null;\n\n // Match `<name>( <args> )` — args can contain commas, spaces, parens (for url() — not supported)\n const fnMatch = trimmed.match(/^([a-z-]+)\\s*\\(\\s*([\\s\\S]*)\\s*\\)\\s*$/i);\n if (!fnMatch) return null;\n\n const name = fnMatch[1].toLowerCase();\n const args = fnMatch[2];\n\n switch (name) {\n case 'circle':\n return parseCircle(args);\n case 'ellipse':\n return parseEllipse(args);\n case 'inset':\n return parseInset(args);\n case 'polygon':\n return parsePolygon(args);\n case 'rect':\n return parseRect(args);\n case 'xywh':\n return parseXywh(args);\n default:\n // Other functions (path(), url(), shape(), etc.) — not interpolatable here.\n return null;\n }\n}\n\n/**\n * circle(<radius> at <x> <y>)\n * Components: [radius, x, y]. Defaults: radius=closest-side→omit, position=50% 50%.\n */\nfunction parseCircle(args: string): ParsedClipPath {\n const [radiusPart, posPart] = splitAtPosition(args);\n\n const radius = parseNumberToken(radiusPart) ?? { value: 50, unit: '%' };\n const [px, py] = parsePosition(posPart);\n\n return {\n shape: 'circle',\n components: [radius, px, py],\n };\n}\n\n/**\n * ellipse(<rx> <ry> at <x> <y>)\n * Components: [rx, ry, x, y].\n */\nfunction parseEllipse(args: string): ParsedClipPath {\n const [radiiPart, posPart] = splitAtPosition(args);\n const radii = parseNumberList(radiiPart);\n const rx = radii[0] ?? { value: 50, unit: '%' };\n const ry = radii[1] ?? rx;\n const [px, py] = parsePosition(posPart);\n\n return {\n shape: 'ellipse',\n components: [rx, ry, px, py],\n };\n}\n\n/**\n * inset(<top> <right>? <bottom>? <left>? round <radius>?)\n * Components: [top, right, bottom, left, ...radii (1–4)].\n * extras.hasRound preserves the `round` keyword.\n */\nfunction parseInset(args: string): ParsedClipPath {\n const { mainPart, roundPart } = splitRound(args);\n const main = parseNumberList(mainPart);\n\n // Apply CSS shorthand expansion (1-4 values → top right bottom left)\n const top = main[0] ?? { value: 0, unit: 'px' };\n const right = main[1] ?? top;\n const bottom = main[2] ?? top;\n const left = main[3] ?? right;\n\n const components: ClipPathComponent[] = [top, right, bottom, left];\n\n if (roundPart !== null) {\n const radii = parseNumberList(roundPart);\n if (radii.length === 0) {\n components.push({ value: 0, unit: 'px' });\n } else {\n for (const r of radii) components.push(r);\n }\n }\n\n return {\n shape: 'inset',\n components,\n extras: roundPart !== null ? { hasRound: true } : undefined,\n };\n}\n\n/**\n * polygon([<fill-rule>,] <x1> <y1>, <x2> <y2>, …)\n * Components: alternating x,y for each vertex.\n *\n * Note: We don't preserve fill-rule (nonzero/evenodd) — uncommon and not\n * meaningfully interpolatable.\n */\nfunction parsePolygon(args: string): ParsedClipPath {\n const components: ClipPathComponent[] = [];\n // Strip an optional fill-rule prefix\n const cleaned = args.replace(/^(nonzero|evenodd)\\s*,\\s*/i, '');\n const vertices = cleaned.split(',');\n for (const v of vertices) {\n const pair = parseNumberList(v);\n if (pair.length >= 2) {\n components.push(pair[0], pair[1]);\n }\n }\n return {\n shape: 'polygon',\n components,\n };\n}\n\n/**\n * rect(<top> <right> <bottom> <left> [round <radius>])\n */\nfunction parseRect(args: string): ParsedClipPath {\n const { mainPart, roundPart } = splitRound(args);\n const main = parseNumberList(mainPart);\n const top = main[0] ?? { value: 0, unit: 'px' };\n const right = main[1] ?? top;\n const bottom = main[2] ?? top;\n const left = main[3] ?? right;\n\n const components: ClipPathComponent[] = [top, right, bottom, left];\n\n if (roundPart !== null) {\n const radii = parseNumberList(roundPart);\n if (radii.length === 0) {\n components.push({ value: 0, unit: 'px' });\n } else {\n for (const r of radii) components.push(r);\n }\n }\n\n return {\n shape: 'rect',\n components,\n extras: roundPart !== null ? { hasRound: true } : undefined,\n };\n}\n\n/**\n * xywh(<x> <y> <w> <h> [round <radius>])\n */\nfunction parseXywh(args: string): ParsedClipPath {\n const { mainPart, roundPart } = splitRound(args);\n const main = parseNumberList(mainPart);\n const x = main[0] ?? { value: 0, unit: 'px' };\n const y = main[1] ?? { value: 0, unit: 'px' };\n const w = main[2] ?? { value: 0, unit: 'px' };\n const h = main[3] ?? { value: 0, unit: 'px' };\n\n const components: ClipPathComponent[] = [x, y, w, h];\n\n if (roundPart !== null) {\n const radii = parseNumberList(roundPart);\n if (radii.length === 0) {\n components.push({ value: 0, unit: 'px' });\n } else {\n for (const r of radii) components.push(r);\n }\n }\n\n return {\n shape: 'xywh',\n components,\n extras: roundPart !== null ? { hasRound: true } : undefined,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-parsers and helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Split on the `at` keyword: `71% at 50% 50%` → [`71%`, `50% 50%`]. */\nfunction splitAtPosition(args: string): [string, string] {\n const idx = args.toLowerCase().search(/\\bat\\b/);\n if (idx === -1) return [args.trim(), ''];\n return [args.slice(0, idx).trim(), args.slice(idx + 2).trim()];\n}\n\n/** Split on the `round` keyword for inset/rect/xywh. */\nfunction splitRound(args: string): { mainPart: string; roundPart: string | null } {\n const idx = args.toLowerCase().search(/\\bround\\b/);\n if (idx === -1) return { mainPart: args.trim(), roundPart: null };\n return {\n mainPart: args.slice(0, idx).trim(),\n roundPart: args.slice(idx + 5).trim(),\n };\n}\n\n/**\n * Parse a position spec: `50% 50%`, `center`, `left top`, etc.\n * Returns `[x, y]` defaulting to 50% 50%.\n */\nfunction parsePosition(input: string): [ClipPathComponent, ClipPathComponent] {\n const fallback: [ClipPathComponent, ClipPathComponent] = [\n { value: 50, unit: '%' },\n { value: 50, unit: '%' },\n ];\n if (!input) return fallback;\n\n // Tokenize: numbers (with units) and CSS keywords\n const tokens = input.trim().split(/\\s+/);\n const result: ClipPathComponent[] = [];\n\n for (const token of tokens) {\n const num = parseNumberToken(token);\n if (num) {\n result.push(num);\n continue;\n }\n // Map keywords to percentages\n const lower = token.toLowerCase();\n if (lower === 'center') result.push({ value: 50, unit: '%' });\n else if (lower === 'left' || lower === 'top') result.push({ value: 0, unit: '%' });\n else if (lower === 'right' || lower === 'bottom') result.push({ value: 100, unit: '%' });\n // Unknown tokens silently ignored — fall back to default for missing axis\n }\n\n const x = result[0] ?? fallback[0];\n const y = result[1] ?? fallback[1];\n return [x, y];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Reading current state\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Read the current computed clip-path on an element.\n * Returns null when there is no clip-path or it's a non-shape value.\n */\nexport function getCurrentClipPath(element: Element): ParsedClipPath | null {\n if (typeof window === 'undefined') return null;\n try {\n const computed = window.getComputedStyle(element);\n const raw =\n computed.clipPath ||\n computed.getPropertyValue('clip-path') ||\n computed.getPropertyValue('-webkit-clip-path');\n if (!raw || raw === 'none') return null;\n return parseClipPath(raw);\n } catch {\n return null;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Interpolation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Returns true when start/end are interpolatable: same shape and component\n * count. Polygon morphing requires identical vertex counts.\n */\nexport function canInterpolate(start: ParsedClipPath, end: ParsedClipPath): boolean {\n if (start.shape !== end.shape) return false;\n if (start.components.length !== end.components.length) return false;\n return true;\n}\n\n// Module-level scratch buffer reused across frames to avoid GC churn\nlet _interpBuffer: ClipPathComponent[] = [];\n\n/**\n * Interpolate two parsed clip-paths at `progress` (0-1).\n *\n * If shapes/lengths don't match, falls back to a hard swap at progress >= 0.5\n * — no smooth morphing is possible across different shape functions.\n *\n * Returns a `ParsedClipPath` whose `components` array is reused on subsequent\n * calls — consume it (e.g. via `clipPathToString`) before the next invocation.\n */\nexport function interpolateClipPaths(\n start: ParsedClipPath,\n end: ParsedClipPath,\n progress: number,\n): ParsedClipPath {\n if (!canInterpolate(start, end)) {\n return progress >= 0.5 ? end : start;\n }\n\n const len = start.components.length;\n if (_interpBuffer.length < len) {\n for (let i = _interpBuffer.length; i < len; i++) {\n _interpBuffer.push({ value: 0, unit: '' });\n }\n }\n _interpBuffer.length = len;\n\n for (let i = 0; i < len; i++) {\n const s = start.components[i];\n const e = end.components[i];\n const slot = _interpBuffer[i];\n slot.value = s.value + (e.value - s.value) * progress;\n // Prefer end unit (matches the user's stated target unit), fall back to start.\n slot.unit = e.unit || s.unit;\n }\n\n return {\n shape: end.shape,\n components: _interpBuffer,\n extras: end.extras ?? start.extras,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Serialization\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction fmt(c: ClipPathComponent): string {\n // Round to 4 decimal places to keep CSS strings small at runtime\n const v = Math.round(c.value * 10000) / 10000;\n return `${v}${c.unit}`;\n}\n\n/**\n * Serialize a parsed clip-path back into a valid CSS clip-path string.\n */\nexport function clipPathToString(parsed: ParsedClipPath): string {\n const c = parsed.components;\n switch (parsed.shape) {\n case 'circle':\n // circle(<radius> at <x> <y>)\n return `circle(${fmt(c[0])} at ${fmt(c[1])} ${fmt(c[2])})`;\n case 'ellipse':\n // ellipse(<rx> <ry> at <x> <y>)\n return `ellipse(${fmt(c[0])} ${fmt(c[1])} at ${fmt(c[2])} ${fmt(c[3])})`;\n case 'inset': {\n // inset(<top> <right> <bottom> <left> [round <r1> <r2>? …])\n const main = `${fmt(c[0])} ${fmt(c[1])} ${fmt(c[2])} ${fmt(c[3])}`;\n if (parsed.extras?.hasRound && c.length > 4) {\n const radii = c.slice(4).map(fmt).join(' ');\n return `inset(${main} round ${radii})`;\n }\n return `inset(${main})`;\n }\n case 'polygon': {\n const points: string[] = [];\n for (let i = 0; i < c.length; i += 2) {\n points.push(`${fmt(c[i])} ${fmt(c[i + 1])}`);\n }\n return `polygon(${points.join(', ')})`;\n }\n case 'rect': {\n const main = `${fmt(c[0])} ${fmt(c[1])} ${fmt(c[2])} ${fmt(c[3])}`;\n if (parsed.extras?.hasRound && c.length > 4) {\n const radii = c.slice(4).map(fmt).join(' ');\n return `rect(${main} round ${radii})`;\n }\n return `rect(${main})`;\n }\n case 'xywh': {\n const main = `${fmt(c[0])} ${fmt(c[1])} ${fmt(c[2])} ${fmt(c[3])}`;\n if (parsed.extras?.hasRound && c.length > 4) {\n const radii = c.slice(4).map(fmt).join(' ');\n return `xywh(${main} round ${radii})`;\n }\n return `xywh(${main})`;\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Self-registration with SDKRegistry (enables conditional bundling)\n// ─────────────────────────────────────────────────────────────────────────────\n\nSDKRegistry.clipPath = {\n parseClipPath,\n getCurrentClipPath,\n interpolateClipPaths,\n clipPathToString,\n canInterpolate,\n};\n",
|
|
40
|
+
"/**\n * TextSplitter - Split text into animatable elements\n *\n * Splits text content into chars, words, or lines wrapped in spans.\n * Supports nested splitting (e.g., 'chars,words' = words containing char spans).\n *\n * Preserves existing element structure — inline elements like\n * `<span class=\"accent\">word</span>` keep their wrapper and styling.\n * Only text nodes are replaced with split spans.\n *\n * Usage:\n * const elements = TextSplitter.split(element, 'chars');\n * const elements = TextSplitter.split(element, 'words');\n * const elements = TextSplitter.split(element, 'chars,words');\n *\n * Revert:\n * TextSplitter.revert(element);\n * TextSplitter.revert(element, consumer); // reference-counted\n */\n\nimport type { SplitType } from '../types';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nexport type { SplitType };\n\nexport interface SplitOptions {\n /** Wrap each split element in a parent with overflow:hidden for clip-reveal effects */\n mask?: boolean;\n /**\n * Opaque token identifying the caller (e.g. an AnimationBuilder instance).\n * When provided, splits are reference-counted: the DOM is only reverted once\n * all registered consumers release their claim via revert(element, consumer).\n * A second consumer on the same element triggers a smart DOM upgrade (nesting)\n * rather than destroying the first consumer's spans.\n */\n consumer?: object;\n}\n\ninterface SplitResult {\n chars: HTMLElement[];\n words: HTMLElement[];\n lines: HTMLElement[];\n elements: HTMLElement[]; // The innermost elements (what gets animated)\n}\n\n/**\n * Per-element split state, held in a module-level WeakMap.\n * Replaces the old originalContentMap + splitResultMap pair.\n */\ninterface ElementSplitState {\n /** The element's innerHTML captured before any split — always the true original. */\n originalHTML: string;\n /** The live split result arrays (mutated in-place on upgrades). */\n result: SplitResult;\n /**\n * Set of consumer tokens currently holding a reference to this split.\n * Empty set means no consumer tracking (single-timeline / legacy callers).\n */\n consumers: Set<object>;\n}\n\n// Single WeakMap that replaces both originalContentMap and splitResultMap\nconst splitStateMap = new WeakMap<Element, ElementSplitState>();\n\nexport class TextSplitter {\n /**\n * Split text content into animatable elements.\n *\n * When `options.consumer` is provided the split is reference-counted.\n * A second consumer on the same element will have the DOM upgraded\n * (chars nested inside existing word spans, or word-wraps promoted to\n * word spans) rather than reverted and rebuilt, so the first consumer's\n * DOM references remain valid.\n *\n * Without a consumer token the function behaves exactly like before:\n * any existing split is reverted and a fresh split is performed.\n */\n static split(element: Element, type: SplitType, options?: SplitOptions): HTMLElement[] {\n if (!(element instanceof HTMLElement)) {\n return [];\n }\n\n const consumer = options?.consumer;\n const existingState = splitStateMap.get(element);\n\n if (existingState) {\n // ── Same consumer, same type ───────────────────────────────────────────\n // No-op: the DOM is already correct for this consumer. Return the spans\n // that match the requested type from the live result.\n if (consumer && existingState.consumers.has(consumer)) {\n const currentType = element.getAttribute('data-split') ?? '';\n if (currentType === type) {\n return this._getSpansForType(existingState.result, type);\n }\n // Same consumer requesting a different type (e.g. rebuildIfPooled with\n // changed config): remove the stale registration and fall through to\n // revert + re-split (handled below as sole consumer).\n existingState.consumers.delete(consumer);\n }\n\n // Register the new consumer so it holds a reference count.\n if (consumer) existingState.consumers.add(consumer);\n\n // ── Multi-consumer: attempt a non-destructive DOM upgrade ─────────────\n // Only attempt upgrade when there are multiple consumers — a second timeline\n // is actively using the existing split DOM and we must not destroy it.\n // For single-consumer or no-consumer callers, fall through to the classic\n // revert + re-split path (preserves the old single-timeline behaviour).\n if (existingState.consumers.size > 1) {\n const upgraded = this._tryUpgrade(element, existingState, type, options);\n if (upgraded !== null) return upgraded;\n\n // Upgrade failed (e.g. lines involved) — cannot safely destroy other\n // consumers' DOM refs, so return the best available spans.\n const types = type.split(',').map(t => t.trim()) as ('chars' | 'words' | 'lines')[];\n if (types.includes('chars') && existingState.result.chars.length > 0)\n return existingState.result.chars.slice();\n if (types.includes('words') && existingState.result.words.length > 0)\n return existingState.result.words.slice();\n return existingState.result.elements.slice();\n }\n\n // ── Single consumer (or no-consumer legacy call) ───────────────────────\n // Classic behaviour: revert to original, then do a fresh split.\n // Remove the consumer we just added — it will be re-registered below.\n if (consumer) existingState.consumers.delete(consumer);\n element.innerHTML = existingState.originalHTML;\n element.removeAttribute('data-split');\n splitStateMap.delete(element);\n // Fall through to fresh split; element.innerHTML is now the true original.\n }\n\n // ── Fresh split (first split, or after a full revert above) ─────────────\n const originalHTML = element.innerHTML;\n\n const result: SplitResult = {\n chars: [],\n words: [],\n lines: [],\n elements: [],\n };\n\n // Parse split type\n const types = type.split(',').map(t => t.trim()) as ('chars' | 'words' | 'lines')[];\n const needsChars = types.includes('chars');\n const needsWords = types.includes('words');\n const needsLines = types.includes('lines');\n\n if (needsLines) {\n // Lines require layout measurement after an initial word split.\n // Use DOM-walking for the initial word pass, then measure and wrap.\n this._splitWithLinesDom(element, needsWords, needsChars, result);\n } else if (needsWords && needsChars) {\n this._splitWordsWithCharsDom(element, result);\n } else if (needsWords) {\n this._splitWordsDom(element, result);\n } else if (needsChars) {\n this._splitCharsDom(element, result);\n }\n\n // Determine innermost elements (what gets animated with stagger)\n if (needsChars) {\n result.elements = result.chars;\n } else if (needsWords) {\n result.elements = result.words;\n } else if (needsLines) {\n result.elements = result.lines;\n }\n\n // Apply mask wrappers (overflow:hidden parents for clip-reveal effects)\n if (options?.mask && result.elements.length > 0) {\n this._applyMaskWrappers(result);\n }\n\n // Propagate gradient text styles (background-clip: text) through the split\n // span chain so gradient headings remain visible after splitting.\n this._propagateGradientText(element, result);\n\n // Store new state in the unified map\n splitStateMap.set(element, {\n originalHTML,\n result,\n consumers: new Set(consumer ? [consumer] : []),\n });\n\n // Mark element as split\n element.setAttribute('data-split', type);\n\n return result.elements;\n }\n\n /* ── Smart upgrade helpers ─────────────────────────────────────────────── */\n\n /**\n * Attempt to upgrade an existing split to satisfy a new type request without\n * destroying existing DOM references.\n *\n * Returns the appropriate span array on success, or null if the upgrade is\n * not possible (caller must fall back to a full revert + re-split).\n */\n private static _tryUpgrade(\n element: HTMLElement,\n state: ElementSplitState,\n type: SplitType,\n options?: SplitOptions\n ): HTMLElement[] | null {\n const existingType = element.getAttribute('data-split') ?? '';\n const existingTypes = existingType.split(',').map(t => t.trim());\n\n const requestedTypes = type.split(',').map(t => t.trim()) as ('chars' | 'words' | 'lines')[];\n const needsChars = requestedTypes.includes('chars');\n const needsWords = requestedTypes.includes('words');\n const needsLines = requestedTypes.includes('lines');\n\n const hasChars = existingTypes.includes('chars');\n const hasWords = existingTypes.includes('words');\n const hasLines = existingTypes.includes('lines');\n\n // ── Existing split already satisfies the entire request ────────────────\n if (\n (!needsChars || hasChars) &&\n (!needsWords || hasWords) &&\n (!needsLines || hasLines)\n ) {\n return this._getSpansForType(state.result, type);\n }\n\n // Lines involved: line splitting requires a full layout re-measurement.\n // Signal to caller that a fresh split is needed.\n if (needsLines || hasLines) {\n return null;\n }\n\n // ── Upgrade: existing 'words', need 'chars' ────────────────────────────\n // Nest char spans inside each existing word span.\n // The word spans remain in the DOM unchanged — the first consumer's refs stay valid.\n if (hasWords && !hasChars && needsChars) {\n const newChars: HTMLElement[] = [];\n for (const wordSpan of state.result.words) {\n this._nestCharsInElement(wordSpan, newChars);\n }\n state.result.chars = newChars;\n state.result.elements = newChars; // chars take priority over words\n\n const combinedType = this._buildTypeString(existingTypes, requestedTypes);\n element.setAttribute('data-split', combinedType);\n\n if (options?.mask) this._applyMaskWrappers(state.result);\n this._propagateGradientText(element, state.result);\n\n return needsChars ? newChars : state.result.words.slice();\n }\n\n // ── Upgrade: existing 'chars' (word-wrap spans), need 'words' ─────────\n // Promote implicit [data-split-word-wrap] spans to proper [data-split-word] spans.\n // The char spans inside them are untouched — the first consumer's refs stay valid.\n if (hasChars && !hasWords && needsWords) {\n const wordWraps = Array.from(\n element.querySelectorAll<HTMLElement>('[data-split-word-wrap]')\n );\n const newWords: HTMLElement[] = [];\n for (let i = 0; i < wordWraps.length; i++) {\n const ww = wordWraps[i];\n ww.removeAttribute('data-split-word-wrap');\n ww.setAttribute('data-split-word', '');\n ww.setAttribute('data-word-index', String(i));\n newWords.push(ww);\n }\n state.result.words = newWords;\n // elements stays as chars (chars have priority)\n\n const combinedType = this._buildTypeString(existingTypes, requestedTypes);\n element.setAttribute('data-split', combinedType);\n\n return needsChars ? state.result.chars.slice() : newWords;\n }\n\n // No upgrade path matched — signal caller to fall back to full revert.\n return null;\n }\n\n /**\n * Return the appropriate span array from a SplitResult for the requested type.\n * Chars have highest priority, then words, then lines.\n */\n private static _getSpansForType(result: SplitResult, type: string): HTMLElement[] {\n const types = type.split(',').map(t => t.trim());\n if (types.includes('chars') && result.chars.length > 0) return result.chars.slice();\n if (types.includes('words') && result.words.length > 0) return result.words.slice();\n if (types.includes('lines') && result.lines.length > 0) return result.lines.slice();\n return result.elements.slice();\n }\n\n /**\n * Build a combined data-split attribute value from existing and requested types,\n * ordered as: lines, words, chars (broadest → most granular).\n */\n private static _buildTypeString(\n existingTypes: string[],\n requestedTypes: string[]\n ): string {\n const all = new Set([...existingTypes, ...requestedTypes]);\n return (['lines', 'words', 'chars'] as const)\n .filter(t => all.has(t))\n .join(',');\n }\n\n /**\n * Walk a word span's text nodes and replace them with individual char spans,\n * appending each new span to the provided `chars` array.\n *\n * Used when upgrading an existing 'words' split to include 'chars'.\n * The word span itself is NOT moved or recreated — only its text node\n * children are swapped out, so existing references to the word span remain valid.\n */\n private static _nestCharsInElement(parent: HTMLElement, chars: HTMLElement[]): void {\n // Collect text nodes first (TreeWalker is invalidated by DOM mutation)\n const walker = document.createTreeWalker(parent, 0x4 /* NodeFilter.SHOW_TEXT */);\n const textNodes: Text[] = [];\n let node = walker.nextNode() as Text | null;\n while (node !== null) {\n textNodes.push(node);\n node = walker.nextNode() as Text | null;\n }\n\n for (const textNode of textNodes) {\n const text = textNode.textContent ?? '';\n if (!text) continue;\n\n const fragment = document.createDocumentFragment();\n for (const char of text) {\n if (/\\s/.test(char)) {\n // Preserve whitespace as a plain text node\n fragment.appendChild(document.createTextNode(char));\n } else {\n const span = document.createElement('span');\n span.setAttribute('data-split-char', '');\n span.setAttribute('data-char-index', String(chars.length));\n span.style.display = 'inline-block';\n span.textContent = char;\n chars.push(span);\n fragment.appendChild(span);\n }\n }\n\n textNode.parentNode?.replaceChild(fragment, textNode);\n }\n }\n\n /* ── DOM-walking split methods ─────────────────────────────────────────── */\n\n /**\n * Split text nodes into word spans, preserving existing element wrappers.\n * Recurses into child elements so `<span class=\"accent\">word</span>` keeps\n * its wrapper and the text inside is split within it.\n */\n private static _splitWordsDom(parent: HTMLElement, result: SplitResult): void {\n const children = Array.from(parent.childNodes);\n\n for (const child of children) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.textContent || '';\n if (!text) continue;\n\n const parts = text.split(/(\\s+)/);\n const fragment = document.createDocumentFragment();\n let hasContent = false;\n\n for (const part of parts) {\n if (/^\\s+$/.test(part)) {\n // Preserve whitespace between words\n fragment.appendChild(document.createTextNode(part.replace(/\\n/g, ' ')));\n hasContent = true;\n } else if (part) {\n const span = document.createElement('span');\n span.setAttribute('data-split-word', '');\n span.setAttribute('data-word-index', String(result.words.length));\n span.style.display = 'inline-block';\n span.textContent = part;\n result.words.push(span);\n fragment.appendChild(span);\n hasContent = true;\n }\n }\n\n if (hasContent) {\n parent.replaceChild(fragment, child);\n }\n } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n const el = child as HTMLElement;\n if (el.tagName === 'BR') continue; // preserve <br> as-is\n // Recurse — preserves wrapper element with all its classes/styles\n this._splitWordsDom(el, result);\n }\n }\n }\n\n /**\n * Split text nodes into character spans, preserving existing element wrappers.\n *\n * Characters are grouped by word inside implicit wrapper spans with\n * `white-space: nowrap` so the browser never breaks a word across lines\n * (matching GSAP SplitText behaviour). Only the char spans are exposed\n * as animatable elements — the word wrappers are transparent to consumers.\n */\n private static _splitCharsDom(parent: HTMLElement, result: SplitResult): void {\n const children = Array.from(parent.childNodes);\n\n for (const child of children) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.textContent || '';\n if (!text) continue;\n\n const parts = text.split(/(\\s+)/);\n const fragment = document.createDocumentFragment();\n\n for (const part of parts) {\n if (/^\\s+$/.test(part)) {\n // Preserve whitespace between words\n fragment.appendChild(document.createTextNode(part.replace(/\\n/g, ' ')));\n } else if (part) {\n // Wrap each word's chars in an implicit container to prevent mid-word breaks\n const wordWrap = document.createElement('span');\n wordWrap.setAttribute('data-split-word-wrap', '');\n wordWrap.style.display = 'inline-block';\n wordWrap.style.whiteSpace = 'nowrap';\n\n for (const char of part) {\n const span = document.createElement('span');\n span.setAttribute('data-split-char', '');\n span.setAttribute('data-char-index', String(result.chars.length));\n span.style.display = 'inline-block';\n span.textContent = char;\n result.chars.push(span);\n wordWrap.appendChild(span);\n }\n\n fragment.appendChild(wordWrap);\n }\n }\n\n parent.replaceChild(fragment, child);\n } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n const el = child as HTMLElement;\n if (el.tagName === 'BR') continue;\n this._splitCharsDom(el, result);\n }\n }\n }\n\n /**\n * Split text nodes into word spans containing character spans.\n * Preserves existing element wrappers.\n */\n private static _splitWordsWithCharsDom(parent: HTMLElement, result: SplitResult): void {\n const children = Array.from(parent.childNodes);\n\n for (const child of children) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.textContent || '';\n if (!text) continue;\n\n const parts = text.split(/(\\s+)/);\n const fragment = document.createDocumentFragment();\n\n for (const part of parts) {\n if (/^\\s+$/.test(part)) {\n fragment.appendChild(document.createTextNode(part.replace(/\\n/g, ' ')));\n } else if (part) {\n const wordSpan = document.createElement('span');\n wordSpan.setAttribute('data-split-word', '');\n wordSpan.setAttribute('data-word-index', String(result.words.length));\n wordSpan.style.display = 'inline-block';\n wordSpan.style.whiteSpace = 'nowrap';\n\n for (const char of part) {\n const charSpan = document.createElement('span');\n charSpan.setAttribute('data-split-char', '');\n charSpan.setAttribute('data-char-index', String(result.chars.length));\n charSpan.style.display = 'inline-block';\n charSpan.textContent = char;\n result.chars.push(charSpan);\n wordSpan.appendChild(charSpan);\n }\n\n result.words.push(wordSpan);\n fragment.appendChild(wordSpan);\n }\n }\n\n parent.replaceChild(fragment, child);\n } else if (child.nodeType === 1 /* ELEMENT_NODE */) {\n const el = child as HTMLElement;\n if (el.tagName === 'BR') continue;\n this._splitWordsWithCharsDom(el, result);\n }\n }\n }\n\n /**\n * Split with line detection (requires layout measurement).\n *\n * Strategy:\n * 1. DOM-walk to split into word spans (preserving element wrappers)\n * 2. Measure each word span's vertical position to group into lines\n * 3. Wrap each line group in a line span\n */\n private static _splitWithLinesDom(\n element: HTMLElement,\n needsWords: boolean,\n needsChars: boolean,\n result: SplitResult\n ): void {\n // Step 1: split into words (DOM-walking, preserves element structure)\n const wordResult: SplitResult = { chars: [], words: [], lines: [], elements: [] };\n this._splitWordsDom(element, wordResult);\n\n // Step 2: measure word positions and group into lines\n const lines: HTMLElement[][] = [];\n let currentLine: HTMLElement[] = [];\n let currentTop = -Infinity;\n\n for (const wordSpan of wordResult.words) {\n const rect = wordSpan.getBoundingClientRect();\n if (rect.top > currentTop + 2) {\n // New line (2px tolerance for rounding)\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n currentLine = [wordSpan];\n currentTop = rect.top;\n } else {\n currentLine.push(wordSpan);\n }\n }\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n // Step 3: rebuild with line wrappers, preserving inline element wrappers.\n // For each word, we capture its ancestor chain between it and the root element\n // so wrapper elements like <span class=\"text-primary\"> are serialized into the\n // rebuilt HTML. Wrappers that span multiple lines are cloned into each line.\n\n // Build a map of wrapper chains for each word span\n const wrapperChainMap = new Map<HTMLElement, HTMLElement[]>();\n for (const word of wordResult.words) {\n wrapperChainMap.set(word, this._getWrapperChain(word, element));\n }\n\n let html = '';\n let lineIndex = 0;\n let wordIndex = 0;\n let charIndex = 0;\n\n for (const lineWords of lines) {\n let lineHtml = '';\n let openWrappers: HTMLElement[] = [];\n\n for (let i = 0; i < lineWords.length; i++) {\n const wordSpan = lineWords[i];\n const wordText = wordSpan.textContent || '';\n const wrapperChain = wrapperChainMap.get(wordSpan) || [];\n\n // Find where current and new wrapper chains diverge\n let commonDepth = 0;\n while (\n commonDepth < openWrappers.length &&\n commonDepth < wrapperChain.length &&\n openWrappers[commonDepth] === wrapperChain[commonDepth]\n ) {\n commonDepth++;\n }\n\n // Close wrappers that are no longer active (deepest first)\n for (let j = openWrappers.length - 1; j >= commonDepth; j--) {\n lineHtml += `</${openWrappers[j].tagName.toLowerCase()}>`;\n }\n\n // Space between words (except before first word in line)\n if (i > 0) {\n lineHtml += ' ';\n }\n\n // Open new wrappers\n for (let j = commonDepth; j < wrapperChain.length; j++) {\n lineHtml += this._openTag(wrapperChain[j]);\n }\n\n // Serialize the word content\n if (needsChars) {\n let wordHtml = '';\n for (const char of wordText) {\n wordHtml += `<span data-split-char data-char-index=\"${charIndex}\" style=\"display:inline-block\">${this._escapeHtml(char)}</span>`;\n charIndex++;\n }\n if (needsWords) {\n lineHtml += `<span data-split-word data-word-index=\"${wordIndex}\" style=\"display:inline-block;white-space:nowrap\">${wordHtml}</span>`;\n } else {\n lineHtml += wordHtml;\n }\n } else if (needsWords) {\n lineHtml += `<span data-split-word data-word-index=\"${wordIndex}\" style=\"display:inline-block\">${this._escapeHtml(wordText)}</span>`;\n } else {\n lineHtml += this._escapeHtml(wordText);\n }\n\n wordIndex++;\n openWrappers = wrapperChain;\n }\n\n // Close any remaining open wrappers at end of line\n for (let j = openWrappers.length - 1; j >= 0; j--) {\n lineHtml += `</${openWrappers[j].tagName.toLowerCase()}>`;\n }\n\n html += `<span data-split-line data-line-index=\"${lineIndex}\" style=\"display:block\">${lineHtml}</span>`;\n lineIndex++;\n }\n\n element.innerHTML = html;\n\n // Collect results from the rebuilt DOM\n if (needsChars) {\n result.chars = Array.from(element.querySelectorAll('[data-split-char]')) as HTMLElement[];\n }\n if (needsWords) {\n result.words = Array.from(element.querySelectorAll('[data-split-word]')) as HTMLElement[];\n }\n result.lines = Array.from(element.querySelectorAll('[data-split-line]')) as HTMLElement[];\n }\n\n /* ── Mask wrappers ────────────────────────────────────────────────────── */\n\n /**\n * Wrap each split element in a parent with overflow:hidden for clip-reveal.\n * The wrapper clips content so animating y:'100%' creates a reveal effect.\n */\n private static _applyMaskWrappers(result: SplitResult): void {\n // Determine which level gets masks — the animatable elements\n const elements = result.elements;\n\n for (const el of elements) {\n const isLine = el.hasAttribute('data-split-line');\n const isWord = el.hasAttribute('data-split-word');\n\n // Create mask wrapper\n const mask = document.createElement('span');\n mask.setAttribute('data-split-mask', '');\n mask.style.overflow = 'hidden';\n mask.style.display = isLine ? 'block' : 'inline-block';\n\n // For inline elements, ensure vertical overflow is clipped properly\n if (!isLine) {\n mask.style.verticalAlign = isWord ? 'bottom' : 'bottom';\n }\n\n // Insert mask wrapper in the element's position, then move element inside\n el.parentNode!.insertBefore(mask, el);\n mask.appendChild(el);\n }\n }\n\n /* ── Gradient text propagation ─────────────────────────────────────────── */\n\n /**\n * Detect if the split element uses `background-clip: text` (gradient text)\n * and propagate the gradient through all generated spans so text stays visible.\n *\n * Without this, inline-block split spans inherit `-webkit-text-fill-color: transparent`\n * but NOT the parent's gradient background, making the text invisible.\n *\n * IMPORTANT: We copy the computed background-image directly instead of using\n * `background: inherit` because `background` is NOT an inherited CSS property.\n * When the split element contains intermediate wrapper elements (e.g.\n * `<h1><span class=\"line\"><span class=\"accent\">word</span></span></h1>`),\n * `inherit` resolves to `none` at each intermediate element, breaking the chain.\n * Copying the resolved value ensures the gradient reaches every split span\n * regardless of DOM depth.\n */\n private static _propagateGradientText(element: HTMLElement, result: SplitResult): void {\n const computed = window.getComputedStyle(element);\n const bgClip = computed.getPropertyValue('background-clip') ||\n computed.getPropertyValue('-webkit-background-clip');\n\n if (bgClip !== 'text') return;\n\n // Read the resolved gradient from the root element — this is the actual\n // CSS value we need on every split span (e.g. \"radial-gradient(...)\").\n const bgImage = computed.getPropertyValue('background-image');\n\n // Collect all generated spans that need the gradient: masks, words, word-wraps, chars, lines\n // data-split-word-wrap = implicit word wrappers in char-only split that break inheritance\n const targets = element.querySelectorAll<HTMLElement>(\n '[data-split-mask], [data-split-word], [data-split-word-wrap], [data-split-char], [data-split-line]'\n );\n\n for (const target of targets) {\n target.style.backgroundImage = bgImage;\n target.style.backgroundClip = 'text';\n target.style.setProperty('-webkit-background-clip', 'text');\n target.style.setProperty('-webkit-text-fill-color', 'transparent');\n }\n }\n\n /* ── Revert / query ───────────────────────────────────────────────────── */\n\n /**\n * Revert element to original content.\n *\n * When `consumer` is provided the revert is reference-counted: the DOM is\n * only restored once all registered consumers have released via this method.\n * If the consumer is the last one (or no consumer tracking is active), the\n * element's innerHTML is restored to the pre-split original immediately.\n *\n * Without `consumer` (legacy / forced revert), the DOM is restored regardless\n * of how many consumers are still registered. All consumer refs are cleared.\n */\n static revert(element: Element, consumer?: object): boolean {\n const state = splitStateMap.get(element);\n if (!state) return false;\n\n if (consumer) {\n state.consumers.delete(consumer);\n\n // Other consumers are still active — leave the DOM intact.\n if (state.consumers.size > 0) {\n return false;\n }\n }\n\n // Last consumer (or forced / no-consumer revert) — fully restore.\n if (!(element instanceof HTMLElement)) return false;\n\n element.innerHTML = state.originalHTML;\n element.removeAttribute('data-split');\n splitStateMap.delete(element);\n return true;\n }\n\n /**\n * Check if element has been split\n */\n static isSplit(element: Element): boolean {\n return element.hasAttribute('data-split');\n }\n\n /**\n * Get split result for an element\n */\n static getResult(element: Element): SplitResult | undefined {\n return splitStateMap.get(element)?.result;\n }\n\n /* ── Utilities ─────────────────────────────────────────────────────────── */\n\n /**\n * Get the chain of wrapper elements between a word span and the root element.\n * Returns outermost-first order (root's direct child → … → word's parent).\n */\n private static _getWrapperChain(word: HTMLElement, root: HTMLElement): HTMLElement[] {\n const chain: HTMLElement[] = [];\n let el = word.parentElement;\n while (el && el !== root) {\n chain.unshift(el);\n el = el.parentElement;\n }\n return chain;\n }\n\n /**\n * Serialize an element's opening tag, preserving all attributes (class, style, data-*, etc).\n */\n private static _openTag(el: HTMLElement): string {\n const tag = el.tagName.toLowerCase();\n let attrs = '';\n for (const attr of Array.from(el.attributes)) {\n attrs += ` ${attr.name}=\"${this._escapeHtml(attr.value)}\"`;\n }\n return `<${tag}${attrs}>`;\n }\n\n /**\n * Escape HTML special characters\n */\n private static _escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n}\n\n// Self-register with the registry\nSDKRegistry.textSplitter = {\n split: (element, type, options) => TextSplitter.split(element, type, options),\n revert: (element, consumer) => TextSplitter.revert(element, consumer),\n isSplit: TextSplitter.isSplit,\n};\n",
|
|
41
|
+
"/**\n * TextFlapper - Split-flap display animation utility\n *\n * Produces per-character render drivers that animate text through random\n * character cycling before settling on the target character — inspired by\n * split-flap departure boards.\n *\n * Seven visual modes:\n * - 'flip' : 3D rotateX (default)\n * - 'fade' : opacity fade\n * - 'slide' : translateY slide\n * - 'blur' : filter blur out/in\n * - 'scale' : scale pop/shrink\n * - 'board' : mechanical split-flap (departure board)\n * - 'none' : instant text swap\n *\n * Integration paths:\n * 1. SDK pipeline — `prepare()` returns drivers consumed by AnimationBuilder,\n * which creates real Animation objects managed by Engine/Timeline.\n * 2. Standalone — `flap()` drives the animation via requestAnimationFrame\n * for direct usage outside the SDK pipeline (e.g. ConnectAI button).\n *\n * Usage (standalone):\n * const ctrl = TextFlapper.flap(charEls, { type: 'flip' });\n * ctrl.kill();\n *\n * Revert:\n * TextFlapper.revert(charEls);\n */\n\nimport type { FlapConfig } from '../types';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { setTransformValue, clearTransformCache } from '../render/TransformCache';\nimport { queueTransform } from '../render/RenderBatch';\n\n// =============================================================================\n// Public types\n// =============================================================================\n\n/**\n * A single numeric property to interpolate per flap cycle.\n * AnimationBuilder populates these from user-provided from/to vars so that\n * CSS properties animate once per character-swap cycle instead of once over\n * the full animation duration.\n */\nexport interface UserPropEntry {\n /** CSS property name or transform shorthand (e.g. 'y', 'opacity', 'rotateX') */\n prop: string;\n /** Start value (from) */\n from: number;\n /** End value (to) */\n to: number;\n /** CSS unit string: 'px', 'deg', '%', or '' for unitless */\n unit: string;\n /** True when prop is a TransformCache property (routes through setTransformValue) */\n isTransform: boolean;\n}\n\n/** Lightweight controller for standalone flap() usage (ConnectAI, etc.) */\nexport interface FlapController {\n kill(): void;\n readonly finished: Promise<void>;\n readonly isComplete: boolean;\n}\n\n/**\n * Per-character render driver returned by `prepare()`.\n * AnimationBuilder uses these to create real Animation objects via Engine.\n */\nexport interface FlapCharDriver {\n /** The character element */\n readonly el: HTMLElement;\n /** Called each frame with progress (0-1). Drives text swaps + visual effects. */\n readonly render: (progress: number) => void;\n /** Snap to resolved final state. Called on animation complete. */\n readonly finalize: () => void;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst CHARSETS: Record<string, string> = {\n alphanumeric: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',\n alpha: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n numeric: '0123456789',\n hex: '0123456789ABCDEF',\n binary: '01',\n katakana: 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン',\n symbols: '!@#$%^&*+-=<>?/|~',\n blocks: '░▒▓█▄▀■□▪▫',\n};\n\nconst DEFAULT_SPEED = 80;\nconst DEFAULT_PERSPECTIVE = 400;\nconst DEFAULT_CYCLES: [number, number] = [2, 5];\n\nconst originalContentMap = new WeakMap<HTMLElement, string>();\n\n/**\n * Board type DOM structure — real Solari departure board effect.\n *\n * A single two-sided flap rotates 0° → -180° around the split line:\n * FRONT = OLD char's top half (visible 0° to ~-90°)\n * BACK = NEW char's bottom half (visible ~-90° to -180°, lands at bottom)\n *\n * Two static halves sit behind the flap:\n * staticTop = NEW char's top (revealed as flap falls away)\n * staticBottom = OLD char's bottom (covered by flapBack when it lands)\n *\n * The flap uses a wrapper with transform-style: preserve-3d (no overflow).\n * Front and back children each have overflow:hidden + backface-visibility:hidden.\n *\n * A persistent split line sits at z:3 above everything for realism.\n * A dynamic shadow overlay on the bottom half adds depth during rotation.\n *\n * Layout:\n * container (el, perspective)\n * ├─ staticTop (top:0, h:50%, overflow:hidden, z:1) → text (NEW top)\n * ├─ staticBottom (top:50%, h:50%, overflow:hidden, z:1) → text (OLD→NEW bottom)\n * ├─ flapWrap (top:0, h:50%, transformOrigin:center bottom, preserve-3d, z:2)\n * │ ├─ flapFront (h:100%, overflow:hidden, backface:hidden) → text (OLD top)\n * │ └─ flapBack (h:100%, overflow:hidden, backface:hidden, rotateX:180°) → text (NEW bottom)\n * └─ splitLine (top:50%, h:1px, z:3) — persistent center divider\n */\ninterface BoardParts {\n /** Static NEW char top half (background, revealed as flap falls) */\n staticTop: HTMLElement;\n /** Static bottom half (OLD char initially, switches to NEW at midpoint) */\n staticBottom: HTMLElement;\n /** Flap wrapper — rotates 0°→-180°, holds front/back faces */\n flapWrap: HTMLElement;\n /** Front face — OLD char top (visible 0° to -90°) */\n flapFront: HTMLElement;\n /** Back face — NEW char bottom (visible -90° to -180°) */\n flapBack: HTMLElement;\n /** Persistent split line across the center */\n splitLine: HTMLElement;\n /** Dynamic shadow overlay on the bottom half — opacity driven by flap angle */\n shadow: HTMLElement;\n /** Inner text spans */\n staticTopText: HTMLElement;\n staticBottomText: HTMLElement;\n flapFrontText: HTMLElement;\n flapBackText: HTMLElement;\n}\nconst boardPartsMap = new WeakMap<HTMLElement, BoardParts>();\n\n/**\n * Tracks elements pinned by `stableWidth: 'container'` so revert() can\n * restore their original `display` and `width` inline styles without\n * clobbering unrelated user-set inline styles.\n */\nconst containerPinnedMap = new WeakMap<HTMLElement, { display: string; width: string }>();\n\n/**\n * Walk upward from a character span past any implicit word-wrap spans\n * inserted by TextSplitter ([data-split-word-wrap]). The returned element\n * is the split ROOT — the user-facing container that was passed to split()\n * (e.g. the <a>, <h1>, etc.). This is the element we want to pin in\n * `stableWidth: 'container'` mode, not the intermediate per-word wrappers.\n */\nfunction findSplitRootContainer(charEl: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = charEl.parentElement;\n while (\n current &&\n typeof current.hasAttribute === 'function' &&\n current.hasAttribute('data-split-word-wrap')\n ) {\n current = current.parentElement;\n }\n return current;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction resolveCharset(charset: string | undefined): string {\n if (!charset) return CHARSETS.alphanumeric;\n return Object.prototype.hasOwnProperty.call(CHARSETS, charset) ? CHARSETS[charset] : charset;\n}\n\nfunction getRandomChar(charset: string): string {\n return charset[Math.floor(Math.random() * charset.length)];\n}\n\nfunction resolveCycleCount(cycles: FlapConfig['cycles']): number {\n if (cycles === undefined) {\n const [min, max] = DEFAULT_CYCLES;\n return min + Math.floor(Math.random() * (max - min + 1));\n }\n if (typeof cycles === 'number') return Math.max(1, cycles);\n const [min, max] = cycles;\n return min + Math.floor(Math.random() * (max - min + 1));\n}\n\nfunction isWhitespaceChar(char: string): boolean {\n return char === '' || char === ' ' || char === '\\u00a0' || char === '\\n' || char === '\\t';\n}\n\n/**\n * Record original textContent for revert, return normalised target char.\n */\nfunction recordAndNormalise(el: HTMLElement): string {\n const raw = el.textContent ?? '';\n if (!originalContentMap.has(el)) {\n originalContentMap.set(el, raw);\n }\n return raw === ' ' ? '\\u00a0' : raw;\n}\n\n/**\n * One-time setup styles required by each animation type.\n */\n/**\n * Measure the widest glyph in `chars` using the same rendering context as\n * `referenceEl` (font, size, weight, letter-spacing etc. all inherit).\n *\n * Used by `stableWidth` to size each char cell (or container) wide enough\n * for BOTH the target text AND the random charset pool. Without this,\n * cycling from a narrow Latin target (\"Getting started\") through a wider\n * charset (katakana, blocks, CJK) causes per-cycle width jumps since the\n * measurement was only sized for the narrow target.\n *\n * Returns the widest measured width AND the character that produced it —\n * the character is used by `stableWidth: 'container'` to simulate the\n * widest flap state in-place for a precise (subpixel-aware) measurement\n * of the ROOT container's full flap width.\n *\n * Measurement strategy: one absolutely-positioned invisible wrapper holds\n * one inline-block child per unique char. Appended once, all widths are\n * read with getBoundingClientRect() after a single reflow, then the\n * wrapper is removed.\n */\nfunction measureMaxCharsetWidth(\n referenceEl: HTMLElement,\n chars: string,\n): { width: number; char: string } {\n if (!chars || chars.length === 0) return { width: 0, char: '' };\n const parent = referenceEl.parentElement;\n if (!parent) return { width: 0, char: '' };\n\n const measure = document.createElement('span');\n const ms = measure.style;\n ms.position = 'absolute';\n ms.left = '-99999px';\n ms.top = '0';\n ms.visibility = 'hidden';\n ms.whiteSpace = 'pre';\n ms.display = 'inline-block';\n ms.pointerEvents = 'none';\n\n // Build one child span per unique char so we can read all widths after a\n // single reflow. De-dupe since width is a pure function of glyph identity.\n const children: { el: HTMLSpanElement; c: string }[] = [];\n const seen = new Set<string>();\n for (const c of chars) {\n if (seen.has(c)) continue;\n seen.add(c);\n const child = document.createElement('span');\n child.style.display = 'inline-block';\n child.textContent = c;\n measure.appendChild(child);\n children.push({ el: child, c });\n }\n\n parent.appendChild(measure);\n let maxW = 0;\n let maxChar = '';\n try {\n for (const { el, c } of children) {\n // getBoundingClientRect() returns subpixel widths; offsetWidth rounds to\n // integers and cumulative rounding across many chars can cause wrap.\n const w = el.getBoundingClientRect().width;\n if (w > maxW) {\n maxW = w;\n maxChar = c;\n }\n }\n } finally {\n parent.removeChild(measure);\n }\n return { width: maxW, char: maxChar };\n}\n\n/** Normalised internal form of FlapConfig.stableWidth */\ntype StableWidthMode = 'none' | 'cells' | 'container';\n\nfunction applyTypeSetup(\n charElements: HTMLElement[],\n type: FlapConfig['type'],\n perspectivePx = DEFAULT_PERSPECTIVE,\n styledBoard = true,\n stableWidth: StableWidthMode = 'none',\n preserveWhitespaceCells = false,\n charset = '',\n): void {\n // Measure max character width for stable-width 'cells' mode (non-board types).\n // Considers BOTH the target chars (already in the DOM) AND the widest glyph\n // in the charset pool — otherwise a wide charset (katakana, blocks) cycled\n // through a narrow target (\"Getting started\") would overflow the pinned cell\n // and cause per-cycle width jumps.\n let monoWidth = 0;\n let charsetMaxWidth = 0;\n let charsetWidestChar = '';\n if (stableWidth !== 'none' && type !== 'board') {\n // Widest existing target char in the DOM\n let referenceEl: HTMLElement | null = null;\n for (const el of charElements) {\n if (!isWhitespaceChar(el.textContent ?? '')) {\n monoWidth = Math.max(monoWidth, el.offsetWidth);\n referenceEl ??= el;\n }\n }\n // Widest charset glyph (measured in the element's inherited font context)\n if (referenceEl && charset) {\n const measured = measureMaxCharsetWidth(referenceEl, charset);\n charsetMaxWidth = measured.width;\n charsetWidestChar = measured.char;\n if (charsetMaxWidth > monoWidth) monoWidth = charsetMaxWidth;\n }\n monoWidth = Math.max(monoWidth, 8) + 4;\n }\n\n // Container-mode: pin the user-facing split-root element (the <a>, <h1>,\n // <span>, etc. that split() was called on) to a width large enough to hold\n // the text at its widest possible flap state. Characters inside — including\n // the implicit word-wrap spans inserted by TextSplitter — flow naturally,\n // so Latin letters keep their natural spacing AND the word-gap stays as\n // one normal space width. Only the split root's OUTER box is reserved,\n // which prevents downstream layout shift (rows, columns, grids) during\n // the flap. Best for inline hover flappers on nav links.\n if (stableWidth === 'container' && type !== 'board' && charsetWidestChar) {\n // Group char spans by their SPLIT-ROOT element (skips implicit word-wrap\n // spans so multi-word targets pin the <a>, not each word individually —\n // otherwise trailing empty space inside each per-word wrapper inflates\n // the gap between words).\n const rootGroups = new Map<HTMLElement, HTMLElement[]>();\n for (const el of charElements) {\n const root = findSplitRootContainer(el);\n if (!root) continue;\n let group = rootGroups.get(root);\n if (!group) { group = []; rootGroups.set(root, group); }\n group.push(el);\n }\n\n for (const [root, chars] of rootGroups) {\n // Skip if already pinned (e.g. revert+re-prepare without a full reset)\n if (containerPinnedMap.has(root)) continue;\n\n // Subpixel-precise additive measurement: start from the root's natural\n // rendered width, then add the extra space each non-whitespace char\n // would need to show the widest charset glyph. Every read uses\n // getBoundingClientRect().width so cumulative rounding doesn't chip\n // away pixels from long strings. This approach measures each link's\n // actual per-char widths (not aggregated), so longer links get larger\n // pin widths — unlike an off-screen simulation which can be distorted\n // by flex/grid constraints and return the same width for every root.\n const currentWidth = root.getBoundingClientRect().width;\n\n let extraWidth = 0;\n for (const el of chars) {\n if (isWhitespaceChar(el.textContent ?? '')) continue;\n const naturalWidth = el.getBoundingClientRect().width;\n if (charsetMaxWidth > naturalWidth) {\n extraWidth += (charsetMaxWidth - naturalWidth);\n }\n }\n\n // +2px safety buffer absorbs any remaining subpixel-rounding drift\n // from the browser's line-box shaping (kerning, letter-spacing). Ceil\n // to an integer so the final `width: Xpx` is clean.\n const pinnedWidth = Math.ceil(currentWidth + extraWidth + 2);\n\n // Stash originals so revert() can restore them (important: we don't\n // want to clobber user-authored inline display/width on the target).\n containerPinnedMap.set(root, {\n display: root.style.display,\n width: root.style.width,\n });\n\n // `display: inline-block` so that `width` actually takes effect on\n // elements that are inline by default (e.g. <a>, <span>).\n root.style.display = 'inline-block';\n root.style.width = `${pinnedWidth}px`;\n }\n }\n\n if (type === 'flip') {\n // Pin parent containers (word-wrap spans) to their current height before\n // transforms are applied. This keeps the layout stable — the 3D perspective\n // projection is still fully visible (no overflow clipping) but the line box\n // height won't fluctuate and push content below.\n const pinnedParents = new Set<HTMLElement>();\n for (const el of charElements) {\n el.style.display = 'inline-block';\n el.style.backfaceVisibility = 'hidden';\n // Prevent baseline shifts during 3D rotation from affecting line-box height.\n el.style.verticalAlign = 'top';\n if (stableWidth === 'cells' && !isWhitespaceChar(el.textContent ?? '')) {\n el.style.width = `${monoWidth}px`;\n el.style.textAlign = 'center';\n }\n // Pin parent word-wrap height (measured once before any transforms)\n if (el.parentElement && !pinnedParents.has(el.parentElement)) {\n pinnedParents.add(el.parentElement);\n el.parentElement.style.height = `${el.parentElement.offsetHeight}px`;\n }\n }\n } else if (type === 'slide') {\n for (const el of charElements) {\n el.style.display = 'inline-block';\n // Overflow must be on the parent so it clips the sliding child\n if (el.parentElement) {\n el.parentElement.style.overflow = 'hidden';\n }\n if (stableWidth === 'cells' && !isWhitespaceChar(el.textContent ?? '')) {\n el.style.width = `${monoWidth}px`;\n el.style.textAlign = 'center';\n }\n }\n } else if (type === 'blur' || type === 'scale' || type === 'fade' || type === 'none') {\n for (const el of charElements) {\n el.style.display = 'inline-block';\n if (stableWidth === 'cells' && !isWhitespaceChar(el.textContent ?? '')) {\n el.style.width = `${monoWidth}px`;\n el.style.textAlign = 'center';\n }\n }\n } else if (type === 'board') {\n // Measure max character width across all elements for monospace when styled\n let monoWidth = 0;\n if (styledBoard) {\n for (const el of charElements) {\n if (!isWhitespaceChar(el.textContent ?? '')) {\n monoWidth = Math.max(monoWidth, el.offsetWidth);\n }\n }\n monoWidth = Math.max(monoWidth, 8) + 4;\n }\n\n for (const el of charElements) {\n if (boardPartsMap.has(el)) continue;\n const rawChar = el.textContent ?? '';\n const isWs = isWhitespaceChar(rawChar);\n // Skip whitespace cells unless preserveWhitespaceCells is enabled\n if (isWs && !preserveWhitespaceCells) continue;\n // For preserved blank cells, render with a non-breaking space\n const char = isWs ? '\\u00a0' : rawChar;\n\n const computed = window.getComputedStyle(el);\n const w = styledBoard ? monoWidth : el.offsetWidth;\n const h = el.offsetHeight;\n\n /** Create a full-height text span styled to match the original character */\n function createTextSpan(top: string): HTMLElement {\n const text = document.createElement('span');\n const ts = text.style;\n ts.display = 'block';\n ts.position = 'absolute';\n ts.left = '0';\n ts.width = '100%';\n ts.height = '200%';\n ts.fontSize = computed.fontSize;\n ts.fontFamily = computed.fontFamily;\n ts.fontWeight = computed.fontWeight;\n ts.color = computed.color;\n ts.lineHeight = `${h}px`;\n ts.textAlign = 'center';\n ts.setProperty('-webkit-text-fill-color', computed.color);\n ts.top = top;\n return text;\n }\n\n /** Create a half-height wrapper with overflow:hidden containing a text span */\n function createStaticHalf(isBottom: boolean): [HTMLElement, HTMLElement] {\n const wrapper = document.createElement('div');\n const ws = wrapper.style;\n ws.position = 'absolute';\n ws.left = '0';\n ws.width = '100%';\n ws.height = '50%';\n ws.overflow = 'hidden';\n ws.top = isBottom ? '50%' : '0';\n ws.zIndex = '1';\n\n const text = createTextSpan(isBottom ? '-100%' : '0');\n wrapper.appendChild(text);\n return [wrapper, text];\n }\n\n /** Create a flap face (front or back) — lives inside flapWrap */\n function createFlapFace(top = '0'): [HTMLElement, HTMLElement] {\n const face = document.createElement('div');\n const fs = face.style;\n fs.position = 'absolute';\n fs.left = '0';\n fs.top = '0';\n fs.width = '100%';\n fs.height = '100%';\n fs.overflow = 'hidden';\n fs.backfaceVisibility = 'hidden';\n\n const text = createTextSpan(top);\n face.appendChild(text);\n return [face, text];\n }\n\n // Clear original text — container now owns all rendering\n el.textContent = '';\n el.style.display = 'inline-block';\n el.style.position = 'relative';\n el.style.width = `${w}px`;\n el.style.height = `${h}px`;\n el.style.perspective = `${perspectivePx}px`;\n\n // 1. Static top — NEW char top half (background, revealed as flap falls)\n const [staticTop, staticTopText] = createStaticHalf(false);\n staticTopText.textContent = char;\n\n // 2. Static bottom — OLD char bottom (switches to NEW at midpoint)\n const [staticBottom, staticBottomText] = createStaticHalf(true);\n staticBottomText.textContent = char;\n\n // 3. Flap wrapper — two-sided card, rotates 0°→-180° around split line\n const flapWrap = document.createElement('div');\n const fws = flapWrap.style;\n fws.position = 'absolute';\n fws.left = '0';\n fws.top = '0';\n fws.width = '100%';\n fws.height = '50%';\n fws.transformOrigin = 'center bottom';\n fws.transformStyle = 'preserve-3d';\n fws.zIndex = '2';\n\n // 3a. Front face — OLD char's top half (visible 0° to ~-90°)\n const [flapFront, flapFrontText] = createFlapFace();\n flapFrontText.textContent = char;\n\n // 3b. Back face — NEW char's bottom half (visible ~-90° to -180°)\n // rotateX(180deg) makes this the back of the two-sided card.\n // At -180° total rotation the face is right-side-up in the bottom position.\n const [flapBack, flapBackText] = createFlapFace('-100%');\n flapBack.style.transform = 'rotateX(180deg)';\n flapBackText.textContent = char;\n\n flapWrap.appendChild(flapFront);\n flapWrap.appendChild(flapBack);\n\n // 4. Persistent split line — always visible above everything\n const splitLine = document.createElement('div');\n const sls = splitLine.style;\n sls.position = 'absolute';\n sls.left = '0';\n sls.width = '100%';\n sls.height = '1px';\n sls.top = 'calc(50% - 0.5px)';\n sls.zIndex = '3';\n sls.pointerEvents = 'none';\n\n // 5. Dynamic shadow overlay — covers bottom half, opacity driven by flap angle\n const shadow = document.createElement('div');\n const shs = shadow.style;\n shs.position = 'absolute';\n shs.left = '0';\n shs.width = '100%';\n shs.height = '50%';\n shs.top = '50%';\n shs.zIndex = '2';\n shs.pointerEvents = 'none';\n shs.opacity = '0';\n shs.background = 'linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0) 100%)';\n\n el.appendChild(staticTop);\n el.appendChild(staticBottom);\n el.appendChild(flapWrap);\n el.appendChild(shadow);\n el.appendChild(splitLine);\n\n // No gap between halves — the 1px split line at z:3 is the only separator.\n // A real gap would expose the page background, creating ugly white lines.\n splitLine.style.background = styledBoard\n ? 'rgba(0, 0, 0, 0.6)'\n : 'rgba(0, 0, 0, 0.15)';\n\n // Letter gap between board tiles\n el.style.marginRight = styledBoard ? '2px' : '1px';\n\n // Styled board: dark gradient background, rounded corners\n if (styledBoard) {\n const radius = '4px';\n const bgTop = 'linear-gradient(180deg, #2a2a2e 0%, #1e1e22 100%)';\n const bgBottom = 'linear-gradient(180deg, #1e1e22 0%, #16161a 100%)';\n\n staticTop.style.background = bgTop;\n staticTop.style.borderRadius = `${radius} ${radius} 0 0`;\n\n staticBottom.style.background = bgBottom;\n staticBottom.style.borderRadius = `0 0 ${radius} ${radius}`;\n\n flapFront.style.background = bgTop;\n flapFront.style.borderRadius = `${radius} ${radius} 0 0`;\n flapBack.style.background = bgBottom;\n flapBack.style.borderRadius = `0 0 ${radius} ${radius}`;\n\n for (const text of [staticTopText, staticBottomText, flapFrontText, flapBackText]) {\n text.style.color = '#e8e8ec';\n text.style.setProperty('-webkit-text-fill-color', '#e8e8ec');\n }\n }\n\n boardPartsMap.set(el, {\n staticTop, staticBottom, flapWrap, flapFront, flapBack, splitLine, shadow,\n staticTopText, staticBottomText, flapFrontText, flapBackText,\n });\n }\n }\n}\n\n// =============================================================================\n// Per-char render function builders\n// =============================================================================\n\n/**\n * Build the render callback for a single character.\n *\n * The render function maps a linear progress (0-1) to the current visual state:\n * which cycle, which phase within the cycle, and applies the corresponding\n * CSS properties + text swap.\n *\n * Pre-generates random characters so the animation is deterministic when\n * scrubbed (seek forward/backward produces consistent results).\n */\nfunction buildCharRender(\n el: HTMLElement,\n target: string,\n cycles: number,\n charset: string,\n type: NonNullable<FlapConfig['type']>,\n perspectivePx: number,\n continuous: boolean,\n userProps?: UserPropEntry[],\n): (progress: number) => void {\n // Pre-generate one random char per cycle.\n // In normal mode the last cycle lands on the target character.\n // In continuous mode ALL cycles use random chars (no landing).\n const chars: string[] = [];\n for (let i = 0; i < cycles; i++) {\n const c = !continuous && i === cycles - 1 ? target : getRandomChar(charset);\n chars.push(c);\n }\n\n // Track which display slot was last rendered to avoid redundant text swaps.\n // Display key encodes both cycle index and half (0 = first half, 1 = second half).\n let lastDisplayKey = -1;\n\n return (progress: number) => {\n // Map progress to cycle index (0..cycles-1) and phase within cycle (0..1)\n const scaled = Math.min(progress, 0.9999) * cycles;\n const cycleIdx = Math.floor(scaled);\n const phase = scaled - cycleIdx;\n\n // Swap text at the MIDPOINT of each cycle (phase=0.5) — this is when the\n // element is visually hidden (opacity 0, rotated 90°, off-screen, etc.),\n // making the character change invisible to the user.\n //\n // First half (phase 0→0.5): show PREVIOUS cycle's char (or original target\n // for cycle 0). The visual \"hides\" this char (fade out, flip away, etc.)\n // Second half (phase 0.5→1): show CURRENT cycle's char. The visual \"reveals\"\n // this char (fade in, flip in, etc.)\n const inSecondHalf = phase >= 0.5;\n const displayKey = cycleIdx * 2 + (inSecondHalf ? 1 : 0);\n\n // Swap text at midpoint — skip for 'board' which manages its own DOM parts\n if (type !== 'board' && displayKey !== lastDisplayKey) {\n if (inSecondHalf) {\n // Reveal the new char for this cycle\n el.textContent = continuous ? getRandomChar(charset) : (chars[cycleIdx] ?? target);\n } else {\n // Still showing the previous cycle's char while it animates out\n el.textContent = cycleIdx === 0\n ? target // first half of first cycle: show original character\n : (continuous ? getRandomChar(charset) : (chars[cycleIdx - 1] ?? target));\n }\n lastDisplayKey = displayKey;\n }\n\n // Apply visual effect based on type and phase\n switch (type) {\n case 'flip': {\n // Phase 0-0.5: rotate 0→-90 (flip away)\n // Phase 0.5-1: rotate 90→0 (flip in)\n // Routes through TransformCache so the engine can compose additional\n // transform properties (x, y, scale, etc.) from the main animation\n // without conflict — RenderBatch.flush() merges them all into one\n // el.style.transform write.\n const angle = phase < 0.5\n ? -90 * (phase / 0.5)\n : 90 * (1 - (phase - 0.5) / 0.5);\n setTransformValue(el, 'perspective', perspectivePx, 'px');\n setTransformValue(el, 'rotateX', angle, 'deg');\n queueTransform(el);\n break;\n }\n case 'fade': {\n // Phase 0-0.5: opacity 1→0; Phase 0.5-1: opacity 0→1\n const opacity = phase < 0.5\n ? 1 - phase / 0.5\n : (phase - 0.5) / 0.5;\n el.style.opacity = String(opacity);\n break;\n }\n case 'slide': {\n // Phase 0-0.5: translateY 0→-100% + opacity 1→0\n // Phase 0.5-1: translateY 100→0% + opacity 0→1\n // translateY routes through TransformCache for composability with other\n // transform props; opacity is written directly (no TransformCache equivalent).\n const ty = phase < 0.5\n ? -100 * (phase / 0.5)\n : 100 * (1 - (phase - 0.5) / 0.5);\n const slideOpacity = phase < 0.5\n ? 1 - phase / 0.5\n : (phase - 0.5) / 0.5;\n // 'y' is the TransformCache property name for the Y-axis translation;\n // buildTransformString emits it as translateY() in the final string.\n setTransformValue(el, 'y', ty, '%');\n queueTransform(el);\n el.style.opacity = String(slideOpacity);\n break;\n }\n case 'blur': {\n // Phase 0-0.5: blur 0→8px; Phase 0.5-1: blur 8→0px\n const blurPx = phase < 0.5\n ? 8 * (phase / 0.5)\n : 8 * (1 - (phase - 0.5) / 0.5);\n el.style.filter = `blur(${blurPx}px)`;\n break;\n }\n case 'scale': {\n // Phase 0-0.5: scale 1→0; Phase 0.5-1: scale 0→1\n // Routes through TransformCache for composability with other transform props.\n const s = phase < 0.5\n ? 1 - phase / 0.5\n : (phase - 0.5) / 0.5;\n setTransformValue(el, 'scale', s);\n queueTransform(el);\n break;\n }\n case 'board': {\n // Solari departure board — single two-sided flap rotates 0° → -180°.\n //\n // flapWrap rotates around its bottom edge (the split line).\n // FRONT face (flapFront): OLD char's top half — visible 0° to ~-90°\n // BACK face (flapBack): NEW char's bottom half — visible ~-90° to -180°\n //\n // Static background:\n // staticTop = NEW char top (revealed as flapFront falls away)\n // staticBottom = OLD char bottom (first half) → NEW char bottom (at midpoint,\n // then covered by flapBack landing)\n //\n const parts = boardPartsMap.get(el);\n if (!parts) break;\n\n const oldChar = cycleIdx === 0\n ? target\n : (continuous ? getRandomChar(charset) : (chars[cycleIdx - 1] ?? target));\n const newChar = continuous ? getRandomChar(charset) : (chars[cycleIdx] ?? target);\n\n // Static top always shows NEW char (revealed behind falling flap front)\n parts.staticTopText.textContent = newChar;\n\n // Flap front shows OLD char top half\n parts.flapFrontText.textContent = oldChar;\n\n // Flap back shows NEW char bottom half\n parts.flapBackText.textContent = newChar;\n\n // Static bottom always shows OLD char — flapBack covers it as it lands\n parts.staticBottomText.textContent = oldChar;\n\n // Rotate the flap wrapper 0° → -180° over the full phase\n const angle = -180 * phase;\n parts.flapWrap.style.transform = `rotateX(${angle}deg)`;\n\n // Dynamic shadow: peaks at -90° (phase 0.5), zero at 0° and -180°\n // sin(phase * π) gives a smooth 0→1→0 curve\n const shadowOpacity = Math.sin(phase * Math.PI);\n parts.shadow.style.opacity = String(shadowOpacity);\n break;\n }\n default:\n // 'none': text is already swapped above, nothing else to do\n break;\n }\n\n // Interpolate user-provided from/to properties once per cycle using `phase`\n // (the 0–1 progress within the current cycle). This makes e.g. opacity or\n // translateY animate once per character-swap cycle rather than just once\n // across the whole animation duration.\n //\n // Transform props route through TransformCache (setTransformValue) so they\n // compose correctly with the flap effect's own transform writes.\n // Non-transform props are written directly to el.style.\n //\n // queueTransform is called if any user transform prop was set AND the flap\n // type did not already enqueue it (flip/scale/slide already call it above).\n if (userProps && userProps.length > 0) {\n let hasUserTransform = false;\n for (const up of userProps) {\n // V-shaped: each cycle starts at `to`, dips to `from` at midpoint (when\n // the text swaps and the char is visually hidden), then returns to `to`.\n // This mirrors the built-in flap effects (flip/fade/scale/slide) which all\n // hide at phase=0.5 and reveal in the second half.\n const t = phase < 0.5 ? 1 - (phase / 0.5) : (phase - 0.5) / 0.5;\n const value = up.from + (up.to - up.from) * t;\n if (up.isTransform) {\n setTransformValue(el, up.prop, value, up.unit || undefined);\n hasUserTransform = true;\n } else {\n (el.style as unknown as Record<string, string>)[up.prop] = up.unit ? `${value}${up.unit}` : String(value);\n }\n }\n // queueTransform uses a Set so calling it again for types that already\n // called it (flip/scale/slide) is safe — the element is only flushed once.\n if (hasUserTransform) {\n queueTransform(el);\n }\n }\n };\n}\n\nfunction buildCharFinalize(\n el: HTMLElement,\n target: string,\n type: NonNullable<FlapConfig['type']>,\n userProps?: UserPropEntry[],\n): () => void {\n return () => {\n if (type === 'board') {\n // Final state: all parts show target char, flap reset to 0° (ready for next cycle)\n const parts = boardPartsMap.get(el);\n if (parts) {\n parts.staticTopText.textContent = target;\n parts.staticBottomText.textContent = target;\n parts.flapFrontText.textContent = target;\n parts.flapBackText.textContent = target;\n parts.flapWrap.style.transform = '';\n parts.shadow.style.opacity = '0';\n }\n // Set user non-transform props to their `to` value; transform cache not\n // used by board for `el` so transform user props are left as-is here.\n if (userProps && userProps.length > 0) {\n for (const up of userProps) {\n if (!up.isTransform) {\n (el.style as unknown as Record<string, string>)[up.prop] = up.unit ? `${up.to}${up.unit}` : String(up.to);\n }\n }\n }\n return;\n }\n el.textContent = target;\n if (type === 'flip' || type === 'scale') {\n // Clear TransformCache entry and the inline style it wrote.\n // The second arg (true) also sets el.style.transform = '' so no stale\n // transform string is left behind after the animation completes.\n clearTransformCache(el, true);\n } else if (type === 'slide') {\n clearTransformCache(el, true);\n el.style.opacity = '';\n } else if (type === 'fade') {\n el.style.opacity = '';\n } else if (type === 'blur') {\n el.style.filter = '';\n }\n\n // Set user-provided properties to their `to` value so the final resting state\n // matches what the V-curve ends at (phase=1 → t=1 → value=to).\n // Transform props go through TransformCache + queueTransform so they compose\n // correctly; non-transform props are written directly to el.style.\n if (userProps && userProps.length > 0) {\n for (const up of userProps) {\n if (up.isTransform) {\n setTransformValue(el, up.prop, up.to, up.unit || undefined);\n } else {\n (el.style as unknown as Record<string, string>)[up.prop] = up.unit ? `${up.to}${up.unit}` : String(up.to);\n }\n }\n // If there are user transform props, queue a final flush so the to-value is\n // materialised into el.style.transform when the engine next calls flush().\n if (userProps.some(p => p.isTransform)) {\n queueTransform(el);\n }\n }\n };\n}\n\n// =============================================================================\n// Main class\n// =============================================================================\n\nexport class TextFlapper {\n /**\n * Prepare per-character animation drivers for the split-flap effect.\n *\n * Returns one FlapCharDriver per non-whitespace character. Each driver\n * provides `duration`, `render(progress)`, and `finalize()` — intended to\n * be consumed by AnimationBuilder, which creates real Engine-managed\n * Animation objects from them.\n *\n * This method handles:\n * - Recording original textContent for revert\n * - Applying one-time type-specific setup styles\n * - Pre-generating random character sequences\n *\n * @param charElements - Pre-split character span elements\n * @param config - Flap animation configuration\n */\n static prepare(\n charElements: HTMLElement[],\n config: FlapConfig,\n continuous = false,\n userProps?: UserPropEntry[],\n ): FlapCharDriver[] {\n const type = config.type ?? 'flip';\n const charset = resolveCharset(config.charset);\n const perspectivePx = config.perspective ?? DEFAULT_PERSPECTIVE;\n const styledBoard = config.styledBoard ?? true;\n // Normalise stableWidth to internal enum:\n // false / undefined → 'none'\n // true → 'cells' (backward-compatible)\n // 'cells' → 'cells'\n // 'container' → 'container'\n const stableWidthRaw = config.stableWidth ?? false;\n const stableWidth: StableWidthMode =\n stableWidthRaw === 'container' ? 'container'\n : stableWidthRaw === 'cells' || stableWidthRaw === true ? 'cells'\n : 'none';\n const preserveWhitespaceCells = config.preserveWhitespaceCells ?? false;\n\n // Record original text and resolve targets BEFORE type setup, because\n // board type clears el.textContent during DOM restructuring.\n // recordAndNormalise is idempotent (won't overwrite existing entry), so calling\n // it for ALL elements ensures revert() can restore even whitespace elements.\n const charTargets: { el: HTMLElement; target: string; isBlankCell: boolean }[] = [];\n for (const el of charElements) {\n // Record original text for ALL elements before any DOM mutation (normalised form returned).\n const normalisedTarget = recordAndNormalise(el);\n const original = el.textContent ?? '';\n const isWs = isWhitespaceChar(original);\n const preserveAsBlank = isWs && preserveWhitespaceCells && type === 'board';\n if (isWs && !preserveAsBlank) continue;\n // For blank cells, use '\\u00a0' as the driver target (the board face shows a non-breaking space).\n // For non-whitespace chars, use the normalised value from recordAndNormalise.\n const target = preserveAsBlank ? '\\u00a0' : normalisedTarget;\n charTargets.push({ el, target, isBlankCell: preserveAsBlank });\n }\n\n applyTypeSetup(charElements, type, perspectivePx, styledBoard, stableWidth, preserveWhitespaceCells, charset);\n\n const drivers: FlapCharDriver[] = [];\n\n for (const { el, target, isBlankCell } of charTargets) {\n if (isBlankCell) {\n // No-op driver for static blank board cells — frame is built but no animation runs.\n // cycles = 0: the cell shows a fixed frame with no flap animation.\n drivers.push({\n el,\n render: () => { /* static blank cell — no animation */ },\n finalize: () => { /* static blank cell — no animation */ },\n });\n } else {\n const cycles = resolveCycleCount(config.cycles);\n drivers.push({\n el,\n render: buildCharRender(el, target, cycles, charset, type, perspectivePx, continuous, userProps),\n finalize: buildCharFinalize(el, target, type, userProps),\n });\n }\n }\n\n if (drivers.length === 0 && charElements.length > 0) {\n const hasEmptyChars = charElements.some(el => el.textContent === '');\n if (hasEmptyChars) {\n console.warn(\n '[Motion.page SDK] TextFlapper produced zero drivers.',\n 'The element likely contains detached char spans from a previous split',\n '(textContent was cleared but spans remain registered).',\n `Effect type: '${type}'.`,\n 'Fix: call Motion(name).kill() and Motion.reset(element) before re-animating, or mutate innerHTML/textContent on the ORIGINAL container (not the split fragments).',\n { charElements, type }\n );\n }\n }\n\n return drivers;\n }\n\n /**\n * Standalone split-flap animation using requestAnimationFrame.\n *\n * For use outside the SDK pipeline (e.g. ConnectAI button) where Engine\n * may not be initialised. Returns a lightweight FlapController.\n *\n * Duration is computed internally from cycles and a default speed (80ms).\n * Stagger defaults to sequential character start with 2× speed gap.\n *\n * @param charElements - Pre-split character span elements\n * @param config - Flap animation configuration\n * @param staggerDelays - Optional per-element start delays in milliseconds\n * @param continuous - When true, characters cycle indefinitely (never land on target)\n */\n static flap(\n charElements: HTMLElement[],\n config: FlapConfig,\n staggerDelays?: number[],\n continuous = false,\n ): FlapController {\n const drivers = TextFlapper.prepare(charElements, config, continuous);\n if (drivers.length === 0) {\n return { kill() {}, finished: Promise.resolve(), isComplete: true };\n }\n\n // Compute per-char duration from default speed for standalone usage\n const cycles = resolveCycleCount(config.cycles);\n const durationMs = cycles * 2 * DEFAULT_SPEED + Math.max(0, cycles - 1) * 0.5 * DEFAULT_SPEED;\n\n let resolveFinished: () => void = () => {};\n const finished = new Promise<void>((r) => { resolveFinished = r; });\n let killed = false;\n let completedCount = 0;\n let rafId = 0;\n\n // Map drivers to their elements for stagger delay lookup\n const driverStarts: number[] = [];\n let driverIdx = 0;\n for (let i = 0; i < charElements.length; i++) {\n const raw = charElements[i].textContent ?? '';\n if (isWhitespaceChar(raw)) continue;\n driverStarts.push(staggerDelays?.[i] ?? driverIdx * DEFAULT_SPEED * 2);\n driverIdx++;\n }\n\n const startTime = performance.now();\n\n function tick() {\n if (killed) return;\n const now = performance.now();\n let allDone = true;\n\n for (let i = 0; i < drivers.length; i++) {\n const driver = drivers[i];\n const elapsed = now - startTime - driverStarts[i];\n if (elapsed < 0) {\n allDone = false;\n continue;\n }\n\n if (continuous) {\n const progress = (elapsed % durationMs) / durationMs;\n driver.render(progress);\n allDone = false;\n } else {\n const progress = Math.min(elapsed / durationMs, 1);\n driver.render(progress);\n if (progress >= 1) {\n driver.finalize();\n } else {\n allDone = false;\n }\n }\n }\n\n if (allDone && !continuous) {\n completedCount = drivers.length;\n resolveFinished();\n } else {\n rafId = requestAnimationFrame(tick);\n }\n }\n\n rafId = requestAnimationFrame(tick);\n if (continuous) resolveFinished(); // continuous never \"finishes\"\n\n return {\n kill() {\n if (killed) return;\n killed = true;\n cancelAnimationFrame(rafId);\n for (const driver of drivers) driver.finalize();\n resolveFinished();\n },\n get finished() { return finished; },\n get isComplete() { return killed || completedCount === drivers.length; },\n };\n }\n\n /**\n * Revert character elements to their original text content and remove\n * all inline styles applied by TextFlapper.\n */\n static revert(charElements: HTMLElement[]): void {\n for (const el of charElements) {\n // Clean up board DOM parts if present\n const parts = boardPartsMap.get(el);\n if (parts) {\n parts.staticTop.remove();\n parts.staticBottom.remove();\n parts.flapWrap.remove();\n parts.splitLine.remove();\n parts.shadow.remove();\n boardPartsMap.delete(el);\n el.style.perspective = '';\n el.style.position = '';\n el.style.width = '';\n el.style.height = '';\n el.style.marginRight = '';\n }\n\n const original = originalContentMap.get(el);\n if (original !== undefined) {\n el.textContent = original;\n originalContentMap.delete(el);\n }\n el.style.display = '';\n el.style.backfaceVisibility = '';\n el.style.overflow = '';\n el.style.transform = '';\n el.style.transition = '';\n el.style.opacity = '';\n el.style.filter = '';\n el.style.width = '';\n el.style.textAlign = '';\n el.style.verticalAlign = '';\n // Clean up parent styles set by slide (overflow) and flip (height).\n if (el.parentElement) {\n el.parentElement.style.overflow = '';\n el.parentElement.style.height = '';\n }\n\n // Restore split-root element's display + width if it was pinned by\n // container-mode stableWidth. Walks past implicit word-wrap spans to\n // find the root (matches applyTypeSetup's grouping logic), then looks\n // up original styles from containerPinnedMap so we don't overwrite\n // unrelated inline styles the user may have set themselves.\n const splitRoot = findSplitRootContainer(el);\n if (splitRoot) {\n const originals = containerPinnedMap.get(splitRoot);\n if (originals) {\n splitRoot.style.display = originals.display;\n splitRoot.style.width = originals.width;\n containerPinnedMap.delete(splitRoot);\n }\n }\n }\n }\n}\n\n// =============================================================================\n// Self-registration\n// =============================================================================\n\nSDKRegistry.textFlapper = {\n prepare: (charElements, config, continuous, userProps) => TextFlapper.prepare(charElements, config, continuous, userProps),\n flap: (charElements, config, staggerDelays, continuous) => TextFlapper.flap(charElements, config, staggerDelays, continuous),\n revert: (charElements) => TextFlapper.revert(charElements),\n};\n",
|
|
40
42
|
"/**\n * StaggerResolver - Calculates stagger delays for multiple targets\n */\n\nimport type { StaggerVars, AnimationTarget } from '../types';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\nexport class StaggerResolver {\n /**\n * Calculate stagger delays for an array of targets\n * Works with both DOM elements and plain objects\n */\n static resolve(\n targets: AnimationTarget[],\n stagger: number | StaggerVars\n ): number[] {\n if (targets.length === 0) {\n return [];\n }\n\n if (targets.length === 1) {\n return [0];\n }\n\n // Simple numeric stagger\n if (typeof stagger === 'number') {\n return targets.map((_, index) => index * stagger);\n }\n\n // Complex stagger configuration\n return this.resolveComplexStagger(targets, stagger);\n }\n\n /**\n * Resolve complex stagger configuration with from, grid, etc.\n */\n private static resolveComplexStagger(\n targets: AnimationTarget[],\n config: StaggerVars\n ): number[] {\n const count = targets.length;\n\n // Single target has no stagger delay\n if (count === 1) {\n return [0];\n }\n\n // Calculate base delay per element\n let delayPerElement: number;\n if (config.each !== undefined) {\n delayPerElement = config.each;\n } else if (config.amount !== undefined) {\n // Total time distributed across all elements (count > 1 guaranteed here)\n delayPerElement = config.amount / (count - 1);\n } else {\n delayPerElement = 0.1; // Default\n }\n\n // Get ordering based on 'from' parameter\n const ordering = this.getOrdering(targets, config);\n\n // Calculate delays based on ordering\n const delays: number[] = new Array(count);\n for (let i = 0; i < count; i++) {\n delays[ordering[i].originalIndex] = ordering[i].order * delayPerElement;\n }\n\n return delays;\n }\n\n /**\n * Get target ordering based on 'from' parameter\n */\n private static getOrdering(\n targets: AnimationTarget[],\n config: StaggerVars\n ): Array<{ originalIndex: number; order: number }> {\n const count = targets.length;\n const from = config.from ?? 'start';\n\n // Handle grid stagger\n if (config.grid) {\n return this.getGridOrdering(targets, config);\n }\n\n // Handle 1D ordering\n switch (from) {\n case 'start':\n return targets.map((_, i) => ({ originalIndex: i, order: i }));\n\n case 'end':\n return targets.map((_, i) => ({\n originalIndex: i,\n order: count - 1 - i,\n }));\n\n case 'center': {\n const center = Math.floor(count / 2);\n return targets.map((_, i) => {\n const distanceFromCenter = Math.abs(i - center);\n return { originalIndex: i, order: distanceFromCenter };\n });\n }\n\n case 'edges': {\n // Edges animate first, center animates last\n return targets.map((_, i) => {\n const distanceFromEdge = Math.min(i, count - 1 - i);\n return {\n originalIndex: i,\n order: distanceFromEdge,\n };\n });\n }\n\n case 'random': {\n // Create random ordering\n const ordering = targets.map((_, i) => ({\n originalIndex: i,\n order: Math.random(),\n }));\n // Sort by random order\n ordering.sort((a, b) => a.order - b.order);\n // Reassign sequential order values\n ordering.forEach((item, index) => {\n item.order = index;\n });\n return ordering;\n }\n\n default:\n // Numeric index\n if (typeof from === 'number') {\n const fromIndex = Math.max(0, Math.min(count - 1, from));\n return targets.map((_, i) => {\n const distance = Math.abs(i - fromIndex);\n return { originalIndex: i, order: distance };\n });\n }\n // Default to start\n return targets.map((_, i) => ({ originalIndex: i, order: i }));\n }\n }\n\n /**\n * Get ordering for grid-based stagger\n */\n private static getGridOrdering(\n targets: AnimationTarget[],\n config: StaggerVars\n ): Array<{ originalIndex: number; order: number }> {\n let cols: number, rows: number;\n\n if (config.grid === 'auto') {\n // Auto-detect grid from element positions (DOM only)\n const gridDimensions = this.detectGrid(targets);\n cols = gridDimensions.cols;\n rows = gridDimensions.rows;\n } else {\n // Use specified grid dimensions\n [cols, rows] = config.grid as [number, number];\n }\n\n const from = config.from ?? 'start';\n const axis = config.axis ?? 'x';\n\n // Calculate grid position for each target\n const gridPositions = targets.map((_, index) => {\n const col = index % cols;\n const row = Math.floor(index / cols);\n return { originalIndex: index, col, row };\n });\n\n // Handle random separately - needs to shuffle all grid positions\n if (from === 'random') {\n const ordering = gridPositions.map((pos) => ({\n originalIndex: pos.originalIndex,\n order: Math.random(),\n }));\n ordering.sort((a, b) => a.order - b.order);\n ordering.forEach((item, index) => {\n item.order = index;\n });\n return ordering;\n }\n\n // Calculate order based on from position and axis\n return gridPositions.map((pos) => {\n let order: number;\n\n if (typeof from === 'number') {\n // From specific index in grid\n const fromCol = from % cols;\n const fromRow = Math.floor(from / cols);\n const distance =\n axis === 'x'\n ? Math.abs(pos.col - fromCol) + Math.abs(pos.row - fromRow) * 0.5\n : Math.abs(pos.row - fromRow) + Math.abs(pos.col - fromCol) * 0.5;\n order = distance;\n } else if (from === 'center') {\n const centerCol = (cols - 1) / 2;\n const centerRow = (rows - 1) / 2;\n const distance =\n Math.abs(pos.col - centerCol) + Math.abs(pos.row - centerRow);\n order = distance;\n } else if (from === 'edges') {\n const distanceFromEdgeCol = Math.min(pos.col, cols - 1 - pos.col);\n const distanceFromEdgeRow = Math.min(pos.row, rows - 1 - pos.row);\n const distanceFromEdge = Math.min(\n distanceFromEdgeCol,\n distanceFromEdgeRow\n );\n order = distanceFromEdge;\n } else if (from === 'end') {\n // From bottom-right, proceed based on axis\n order = axis === 'x'\n ? (rows - 1 - pos.row) * cols + (cols - 1 - pos.col)\n : (cols - 1 - pos.col) * rows + (rows - 1 - pos.row);\n } else {\n // Default (start): from top-left, proceed based on axis\n order = axis === 'x' ? pos.row * cols + pos.col : pos.col * rows + pos.row;\n }\n\n return { originalIndex: pos.originalIndex, order };\n });\n }\n\n /**\n * Auto-detect grid dimensions from element positions\n * Only works with DOM elements. For plain objects, falls back to linear arrangement.\n */\n private static detectGrid(targets: AnimationTarget[]): { cols: number; rows: number } {\n if (targets.length === 0) {\n return { cols: 0, rows: 0 };\n }\n\n // Filter to only DOM elements that have getBoundingClientRect\n const elements = targets.filter((t): t is Element => t instanceof Element);\n\n // If no DOM elements, fall back to single row\n if (elements.length === 0) {\n return { cols: targets.length, rows: 1 };\n }\n\n // Get all unique Y positions (rows)\n const yPositions = new Map<number, number>();\n elements.forEach((el) => {\n const rect = el.getBoundingClientRect();\n const y = Math.round(rect.top);\n yPositions.set(y, (yPositions.get(y) || 0) + 1);\n });\n\n const rows = yPositions.size;\n const cols = Math.ceil(targets.length / rows);\n\n return { cols, rows };\n }\n}\n\n// Self-register with the registry\nSDKRegistry.stagger = { resolve: (targets, stagger) => StaggerResolver.resolve(targets, stagger) };\n",
|
|
41
|
-
"/**\n * FitResolver — SDK-native Fit animation geometry resolution\n *\n * Captures source and target element bounding rects at play time,\n * computes position and scale deltas, and creates PropTweens that\n * animate the source element toward the target's geometry.\n *\n * Non-absolute mode (default): animates CSS transforms (x, y, scaleX, scaleY)\n * Resize mode: animates CSS transforms (x, y) + width/height properties (no content distortion)\n * Absolute mode: animates CSS left/top/width/height properties\n */\n\nimport type { FitConfig } from '../types';\nimport type { Animation } from '../core/Animation';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { clearTransformCache } from '../render/TransformCache';\nimport { getCurrentValue } from '../utils/PropertyParser';\n\n// ─────────────────────────────────────────────\n// Geometry types\n// ─────────────────────────────────────────────\n\ninterface FitGeometry {\n dx: number;\n dy: number;\n sx: number;\n sy: number;\n sourceWidth: number;\n sourceHeight: number;\n targetWidth: number;\n targetHeight: number;\n /** Only present when absolute === true */\n sourceLeft?: number;\n /** Only present when absolute === true */\n sourceTop?: number;\n /** Only present when absolute === true */\n targetLeft?: number;\n /** Only present when absolute === true */\n targetTop?: number;\n}\n\n// ─────────────────────────────────────────────\n// Geometry capture\n// ─────────────────────────────────────────────\n\nfunction captureGeometry(\n source: Element,\n target: string,\n absolute: boolean\n): FitGeometry | null {\n // Issue #4: Guard against SVG and other non-HTML elements.\n // offsetLeft/offsetTop are unsafe on SVG elements; bail out early.\n if (!(source instanceof HTMLElement)) {\n console.warn('[Motion Fit] Fit animations only support HTML elements. SVG elements are not supported.');\n return null;\n }\n\n const targetEl = document.querySelector(target);\n if (!targetEl) {\n console.warn(`[Motion Fit] Target element not found: \"${target}\". Animation skipped.`);\n return null;\n }\n\n // Issue #4: Same HTMLElement guard for the target.\n if (!(targetEl instanceof HTMLElement)) {\n console.warn('[Motion Fit] Fit animations only support HTML elements. SVG elements are not supported.');\n return null;\n }\n\n if (targetEl === source) {\n console.warn('[Motion Fit] Target element is the same as source element. Animation skipped.');\n return null;\n }\n\n // Always needed: bounding rects for delta and scale computation.\n const sourceRect = source.getBoundingClientRect();\n const targetRect = targetEl.getBoundingClientRect();\n\n const sourceW = sourceRect.width === 0 ? 1 : sourceRect.width;\n const sourceH = sourceRect.height === 0 ? 1 : sourceRect.height;\n\n // Use center-to-center deltas because CSS transform-origin defaults to 50% 50%.\n // Scale expands from center, so translating between centers ensures the final\n // geometry lands exactly on the target.\n const sourceCenterX = sourceRect.left + sourceW / 2;\n const sourceCenterY = sourceRect.top + sourceH / 2;\n const targetCenterX = targetRect.left + targetRect.width / 2;\n const targetCenterY = targetRect.top + targetRect.height / 2;\n const dx = targetCenterX - sourceCenterX;\n const dy = targetCenterY - sourceCenterY;\n const sx = targetRect.width / sourceW;\n const sy = targetRect.height / sourceH;\n\n if (!absolute) {\n // Issue #2: Transform mode only needs dx/dy/sx/sy — skip all offset-parent DOM reads.\n return {\n dx,\n dy,\n sx,\n sy,\n sourceWidth: sourceRect.width,\n sourceHeight: sourceRect.height,\n targetWidth: targetRect.width,\n targetHeight: targetRect.height,\n };\n }\n\n // Issue #2: Absolute mode only — compute offset-parent-relative positions for left/top tweens.\n // After the instanceof HTMLElement guard above, no casts are needed for source or targetEl.\n const offsetParent = source.offsetParent ?? document.documentElement;\n const parentRect = offsetParent.getBoundingClientRect();\n const parentScrollLeft = (offsetParent as HTMLElement).scrollLeft ?? 0;\n const parentScrollTop = (offsetParent as HTMLElement).scrollTop ?? 0;\n\n return {\n dx,\n dy,\n sx,\n sy,\n sourceWidth: sourceRect.width,\n sourceHeight: sourceRect.height,\n targetWidth: targetRect.width,\n targetHeight: targetRect.height,\n sourceLeft: source.offsetLeft,\n sourceTop: source.offsetTop,\n targetLeft: targetRect.left - parentRect.left + parentScrollLeft,\n targetTop: targetRect.top - parentRect.top + parentScrollTop,\n };\n}\n\n// ─────────────────────────────────────────────\n// PropTween creation\n// ─────────────────────────────────────────────\n\nconst DELTA_EPSILON = 0.01;\nconst SCALE_EPSILON = 0.001;\n\nfunction createPropTweens(\n animation: Animation,\n source: Element,\n fitConfig: FitConfig,\n pool: typeof PropTweenPool\n): void {\n // Destructure absolute/scale/resize before calling captureGeometry so flags can be forwarded.\n const { absolute = false, scale = true, resize = false } = fitConfig;\n\n // Clear the transform cache WeakMap entry so captureGeometry reads fresh\n // getBoundingClientRect() values. Do NOT clear inline styles — when multiple\n // fit entries are chained on the same element, the previous fit's end-state\n // inline styles define the correct source position for the next fit.\n if (source instanceof HTMLElement) {\n clearTransformCache(source, false); // cache only — preserve inline styles\n }\n\n const geometry = captureGeometry(source, fitConfig.target, absolute);\n if (!geometry) return;\n\n if (!absolute) {\n if (resize) {\n // Resize mode: animate width/height (no content distortion, but triggers layout reflow).\n // width/height grow from the top-left corner, so the translation delta must be adjusted\n // from center-to-center (dx/dy) to top-left-to-top-left:\n // dxTopLeft = dxCenter - (targetWidth - sourceWidth) / 2\n const dxTopLeft = geometry.dx - (geometry.targetWidth - geometry.sourceWidth) / 2;\n const dyTopLeft = geometry.dy - (geometry.targetHeight - geometry.sourceHeight) / 2;\n\n if (Math.abs(dxTopLeft) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'x', 0, dxTopLeft, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(dyTopLeft) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'y', 0, dyTopLeft, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(geometry.targetWidth - geometry.sourceWidth) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'width', geometry.sourceWidth, geometry.targetWidth, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(geometry.targetHeight - geometry.sourceHeight) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'height', geometry.sourceHeight, geometry.targetHeight, 'px');\n animation.addPropTween(pt);\n }\n } else {\n // Transform mode: translate + optional scale (scaleX/scaleY).\n //\n // For chained fits on the same element, the previous fit's end-state\n // transforms are still applied (inline styles preserved). We read the\n // current transform values so this fit starts from where the previous\n // one ended, rather than snapping back to 0/1.\n //\n // dx/dy from captureGeometry are visual deltas (current rect → target rect).\n // The end value = currentTransformValue + visualDelta.\n const curX = getCurrentValue(source, 'x');\n const curY = getCurrentValue(source, 'y');\n const endX = curX + geometry.dx;\n const endY = curY + geometry.dy;\n\n if (Math.abs(endX - curX) > DELTA_EPSILON || Math.abs(curX) > DELTA_EPSILON || Math.abs(endX) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'x', curX, endX, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(endY - curY) > DELTA_EPSILON || Math.abs(curY) > DELTA_EPSILON || Math.abs(endY) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'y', curY, endY, 'px');\n animation.addPropTween(pt);\n }\n if (scale) {\n const curSX = getCurrentValue(source, 'scaleX');\n const curSY = getCurrentValue(source, 'scaleY');\n const endSX = curSX * geometry.sx;\n const endSY = curSY * geometry.sy;\n\n if (Math.abs(endSX - curSX) > SCALE_EPSILON || Math.abs(curSX - 1) > SCALE_EPSILON || Math.abs(endSX - 1) > SCALE_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'scaleX', curSX, endSX, '');\n animation.addPropTween(pt);\n }\n if (Math.abs(endSY - curSY) > SCALE_EPSILON || Math.abs(curSY - 1) > SCALE_EPSILON || Math.abs(endSY - 1) > SCALE_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'scaleY', curSY, endSY, '');\n animation.addPropTween(pt);\n }\n }\n }\n } else {\n const el = source as HTMLElement;\n\n // KNOWN LIMITATION (v1): Absolute mode permanently sets position: absolute on the element.\n // Future versions should store the original position value and revert it on animation complete.\n // See fit-sdk-v1.md spec for details.\n const currentPosition = window.getComputedStyle(el).position;\n if (currentPosition === 'static') {\n el.style.position = 'absolute';\n }\n\n // sourceLeft/sourceTop/targetLeft/targetTop are always defined when absolute === true\n // because captureGeometry only populates them in the absolute branch.\n const sourceLeft = geometry.sourceLeft!;\n const sourceTop = geometry.sourceTop!;\n const targetLeft = geometry.targetLeft!;\n const targetTop = geometry.targetTop!;\n\n if (Math.abs(targetLeft - sourceLeft) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'left', sourceLeft, targetLeft, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(targetTop - sourceTop) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'top', sourceTop, targetTop, 'px');\n animation.addPropTween(pt);\n }\n if (scale) {\n if (Math.abs(geometry.targetWidth - geometry.sourceWidth) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'width', geometry.sourceWidth, geometry.targetWidth, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(geometry.targetHeight - geometry.sourceHeight) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'height', geometry.sourceHeight, geometry.targetHeight, 'px');\n animation.addPropTween(pt);\n }\n }\n }\n}\n\n// ─────────────────────────────────────────────\n// Public API\n// ─────────────────────────────────────────────\n\nfunction registerPendingSetup(\n animation: Animation,\n element: Element,\n fitConfig: FitConfig,\n // pool is a protocol parameter required by the SDKRegistry.fit interface but is unused here.\n // The actual pool instance is provided at call time via the closure argument `p` in\n // setPendingFitSetup, not at registration time.\n pool: typeof PropTweenPool\n): void {\n animation.setPendingFitSetup((p) => createPropTweens(animation, element, fitConfig, p));\n animation.setLazyStartCapture(true);\n\n // Register cleanup for replay: resets inline styles so chained fits\n // re-measure from the element's natural CSS position on timeline restart.\n const { absolute = false, resize = false } = fitConfig;\n animation.setFitCleanup(() => {\n if (element instanceof HTMLElement) {\n clearTransformCache(element, true); // cache + inline transform\n if (resize) {\n element.style.width = '';\n element.style.height = '';\n }\n if (absolute) {\n element.style.width = '';\n element.style.height = '';\n element.style.left = '';\n element.style.top = '';\n }\n }\n });\n}\n\n// ─────────────────────────────────────────────\n// Self-registration\n// ─────────────────────────────────────────────\n\nSDKRegistry.fit = { registerPendingSetup };\n",
|
|
43
|
+
"/**\n * FitResolver — SDK-native Fit animation geometry resolution\n *\n * Captures source and target element bounding rects at play time,\n * computes position and scale deltas, and creates PropTweens that\n * animate the source element toward the target's geometry.\n *\n * Non-absolute mode (default): animates CSS transforms (x, y, scaleX, scaleY)\n * Resize mode: animates CSS transforms (x, y) + width/height properties (no content distortion)\n * Absolute mode: animates CSS left/top/width/height properties\n */\n\nimport type { FitConfig } from '../types';\nimport type { Animation } from '../core/Animation';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { clearTransformCache, getTransformUnit } from '../render/TransformCache';\nimport { getCurrentValue } from '../utils/PropertyParser';\n\n// ─────────────────────────────────────────────\n// Geometry types\n// ─────────────────────────────────────────────\n\ninterface FitGeometry {\n dx: number;\n dy: number;\n sx: number;\n sy: number;\n sourceWidth: number;\n sourceHeight: number;\n targetWidth: number;\n targetHeight: number;\n /** Only present when absolute === true */\n sourceLeft?: number;\n /** Only present when absolute === true */\n sourceTop?: number;\n /** Only present when absolute === true */\n targetLeft?: number;\n /** Only present when absolute === true */\n targetTop?: number;\n}\n\n// ─────────────────────────────────────────────\n// Geometry capture\n// ─────────────────────────────────────────────\n\nfunction captureGeometry(\n source: Element,\n target: string,\n absolute: boolean\n): FitGeometry | null {\n // Issue #4: Guard against SVG and other non-HTML elements.\n // offsetLeft/offsetTop are unsafe on SVG elements; bail out early.\n if (!(source instanceof HTMLElement)) {\n console.warn('[Motion Fit] Fit animations only support HTML elements. SVG elements are not supported.');\n return null;\n }\n\n const targetEl = document.querySelector(target);\n if (!targetEl) {\n console.warn(`[Motion Fit] Target element not found: \"${target}\". Animation skipped.`);\n return null;\n }\n\n // Issue #4: Same HTMLElement guard for the target.\n if (!(targetEl instanceof HTMLElement)) {\n console.warn('[Motion Fit] Fit animations only support HTML elements. SVG elements are not supported.');\n return null;\n }\n\n if (targetEl === source) {\n console.warn('[Motion Fit] Target element is the same as source element. Animation skipped.');\n return null;\n }\n\n // Always needed: bounding rects for delta and scale computation.\n const sourceRect = source.getBoundingClientRect();\n const targetRect = targetEl.getBoundingClientRect();\n\n const sourceW = sourceRect.width === 0 ? 1 : sourceRect.width;\n const sourceH = sourceRect.height === 0 ? 1 : sourceRect.height;\n\n // Use center-to-center deltas because CSS transform-origin defaults to 50% 50%.\n // Scale expands from center, so translating between centers ensures the final\n // geometry lands exactly on the target.\n const sourceCenterX = sourceRect.left + sourceW / 2;\n const sourceCenterY = sourceRect.top + sourceH / 2;\n const targetCenterX = targetRect.left + targetRect.width / 2;\n const targetCenterY = targetRect.top + targetRect.height / 2;\n const dx = targetCenterX - sourceCenterX;\n const dy = targetCenterY - sourceCenterY;\n const sx = targetRect.width / sourceW;\n const sy = targetRect.height / sourceH;\n\n if (!absolute) {\n // Issue #2: Transform mode only needs dx/dy/sx/sy — skip all offset-parent DOM reads.\n return {\n dx,\n dy,\n sx,\n sy,\n sourceWidth: sourceRect.width,\n sourceHeight: sourceRect.height,\n targetWidth: targetRect.width,\n targetHeight: targetRect.height,\n };\n }\n\n // Issue #2: Absolute mode only — compute offset-parent-relative positions for left/top tweens.\n // After the instanceof HTMLElement guard above, no casts are needed for source or targetEl.\n const offsetParent = source.offsetParent ?? document.documentElement;\n const parentRect = offsetParent.getBoundingClientRect();\n const parentScrollLeft = (offsetParent as HTMLElement).scrollLeft ?? 0;\n const parentScrollTop = (offsetParent as HTMLElement).scrollTop ?? 0;\n\n return {\n dx,\n dy,\n sx,\n sy,\n sourceWidth: sourceRect.width,\n sourceHeight: sourceRect.height,\n targetWidth: targetRect.width,\n targetHeight: targetRect.height,\n sourceLeft: source.offsetLeft,\n sourceTop: source.offsetTop,\n targetLeft: targetRect.left - parentRect.left + parentScrollLeft,\n targetTop: targetRect.top - parentRect.top + parentScrollTop,\n };\n}\n\n// ─────────────────────────────────────────────\n// PropTween creation\n// ─────────────────────────────────────────────\n\n// ─────────────────────────────────────────────\n// Unit conversion for translate values\n// ─────────────────────────────────────────────\n\n/**\n * Convert a translate x/y value from its current CSS unit to pixels.\n *\n * CSS translate percentages are relative to the element's own dimensions\n * (offsetWidth for x, offsetHeight for y). Other relative units (vw, vh,\n * em, rem, etc.) are resolved against their respective references.\n *\n * Returns the value unchanged when the unit is already 'px' or empty.\n */\nfunction resolveTranslateToPx(\n element: Element,\n axis: 'x' | 'y',\n value: number,\n unit: string\n): number {\n if (!unit || unit === 'px') return value;\n\n if (unit === '%') {\n // CSS translate percentage is relative to the element's own box size\n const el = element as HTMLElement;\n const refSize = axis === 'x' ? el.offsetWidth : el.offsetHeight;\n return (value / 100) * refSize;\n }\n\n // Viewport units\n if (unit === 'vw') return (value / 100) * window.innerWidth;\n if (unit === 'vh') return (value / 100) * window.innerHeight;\n if (unit === 'vmin') return (value / 100) * Math.min(window.innerWidth, window.innerHeight);\n if (unit === 'vmax') return (value / 100) * Math.max(window.innerWidth, window.innerHeight);\n\n // Font-relative units\n if (unit === 'em') {\n const fontSize = parseFloat(window.getComputedStyle(element).fontSize);\n return value * fontSize;\n }\n if (unit === 'rem') {\n const rootFontSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);\n return value * rootFontSize;\n }\n\n // Unknown unit — return as-is (best effort)\n return value;\n}\n\n// ─────────────────────────────────────────────\n// PropTween creation\n// ─────────────────────────────────────────────\n\nconst DELTA_EPSILON = 0.01;\nconst SCALE_EPSILON = 0.001;\n\nfunction createPropTweens(\n animation: Animation,\n source: Element,\n fitConfig: FitConfig,\n pool: typeof PropTweenPool\n): void {\n // Destructure absolute/scale/resize before calling captureGeometry so flags can be forwarded.\n const { absolute = false, scale = true, resize = false } = fitConfig;\n\n // Clear the transform cache WeakMap entry so captureGeometry reads fresh\n // getBoundingClientRect() values. Do NOT clear inline styles — when multiple\n // fit entries are chained on the same element, the previous fit's end-state\n // inline styles define the correct source position for the next fit.\n if (source instanceof HTMLElement) {\n clearTransformCache(source, false); // cache only — preserve inline styles\n }\n\n const geometry = captureGeometry(source, fitConfig.target, absolute);\n if (!geometry) return;\n\n if (!absolute) {\n if (resize) {\n // Resize mode: animate width/height (no content distortion, but triggers layout reflow).\n // width/height grow from the top-left corner, so the translation delta must be adjusted\n // from center-to-center (dx/dy) to top-left-to-top-left:\n // dxTopLeft = dxCenter - (targetWidth - sourceWidth) / 2\n const dxTopLeft = geometry.dx - (geometry.targetWidth - geometry.sourceWidth) / 2;\n const dyTopLeft = geometry.dy - (geometry.targetHeight - geometry.sourceHeight) / 2;\n\n if (Math.abs(dxTopLeft) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'x', 0, dxTopLeft, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(dyTopLeft) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'y', 0, dyTopLeft, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(geometry.targetWidth - geometry.sourceWidth) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'width', geometry.sourceWidth, geometry.targetWidth, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(geometry.targetHeight - geometry.sourceHeight) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'height', geometry.sourceHeight, geometry.targetHeight, 'px');\n animation.addPropTween(pt);\n }\n } else {\n // Transform mode: translate + optional scale (scaleX/scaleY).\n //\n // For chained fits on the same element, the previous fit's end-state\n // transforms are still applied (inline styles preserved). We read the\n // current transform values so this fit starts from where the previous\n // one ended, rather than snapping back to 0/1.\n //\n // dx/dy from captureGeometry are visual deltas in **pixels** (from\n // getBoundingClientRect). The cached transform value may be in a\n // different unit (%, vw, em, etc.) — we must convert to pixels first\n // so that start + delta produces a correct pixel end value.\n const rawX = getCurrentValue(source, 'x');\n const rawY = getCurrentValue(source, 'y');\n const unitX = getTransformUnit(source, 'x');\n const unitY = getTransformUnit(source, 'y');\n const curX = resolveTranslateToPx(source, 'x', rawX, unitX);\n const curY = resolveTranslateToPx(source, 'y', rawY, unitY);\n const endX = curX + geometry.dx;\n const endY = curY + geometry.dy;\n\n if (Math.abs(endX - curX) > DELTA_EPSILON || Math.abs(curX) > DELTA_EPSILON || Math.abs(endX) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'x', curX, endX, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(endY - curY) > DELTA_EPSILON || Math.abs(curY) > DELTA_EPSILON || Math.abs(endY) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'y', curY, endY, 'px');\n animation.addPropTween(pt);\n }\n if (scale) {\n const curSX = getCurrentValue(source, 'scaleX');\n const curSY = getCurrentValue(source, 'scaleY');\n const endSX = curSX * geometry.sx;\n const endSY = curSY * geometry.sy;\n\n if (Math.abs(endSX - curSX) > SCALE_EPSILON || Math.abs(curSX - 1) > SCALE_EPSILON || Math.abs(endSX - 1) > SCALE_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'scaleX', curSX, endSX, '');\n animation.addPropTween(pt);\n }\n if (Math.abs(endSY - curSY) > SCALE_EPSILON || Math.abs(curSY - 1) > SCALE_EPSILON || Math.abs(endSY - 1) > SCALE_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'scaleY', curSY, endSY, '');\n animation.addPropTween(pt);\n }\n }\n }\n } else {\n const el = source as HTMLElement;\n\n // KNOWN LIMITATION (v1): Absolute mode permanently sets position: absolute on the element.\n // Future versions should store the original position value and revert it on animation complete.\n // See fit-sdk-v1.md spec for details.\n const currentPosition = window.getComputedStyle(el).position;\n if (currentPosition === 'static') {\n el.style.position = 'absolute';\n }\n\n // sourceLeft/sourceTop/targetLeft/targetTop are always defined when absolute === true\n // because captureGeometry only populates them in the absolute branch.\n const sourceLeft = geometry.sourceLeft!;\n const sourceTop = geometry.sourceTop!;\n const targetLeft = geometry.targetLeft!;\n const targetTop = geometry.targetTop!;\n\n if (Math.abs(targetLeft - sourceLeft) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'left', sourceLeft, targetLeft, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(targetTop - sourceTop) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'top', sourceTop, targetTop, 'px');\n animation.addPropTween(pt);\n }\n if (scale) {\n if (Math.abs(geometry.targetWidth - geometry.sourceWidth) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'width', geometry.sourceWidth, geometry.targetWidth, 'px');\n animation.addPropTween(pt);\n }\n if (Math.abs(geometry.targetHeight - geometry.sourceHeight) > DELTA_EPSILON) {\n const pt = pool.acquire();\n pt.init(source, 'height', geometry.sourceHeight, geometry.targetHeight, 'px');\n animation.addPropTween(pt);\n }\n }\n }\n}\n\n// ─────────────────────────────────────────────\n// Public API\n// ─────────────────────────────────────────────\n\nfunction registerPendingSetup(\n animation: Animation,\n element: Element,\n fitConfig: FitConfig,\n // pool is a protocol parameter required by the SDKRegistry.fit interface but is unused here.\n // The actual pool instance is provided at call time via the closure argument `p` in\n // setPendingFitSetup, not at registration time.\n pool: typeof PropTweenPool\n): void {\n animation.setPendingFitSetup((p) => createPropTweens(animation, element, fitConfig, p));\n animation.setLazyStartCapture(true);\n\n // Register cleanup for replay: resets inline styles so chained fits\n // re-measure from the element's natural CSS position on timeline restart.\n const { absolute = false, resize = false } = fitConfig;\n animation.setFitCleanup(() => {\n if (element instanceof HTMLElement) {\n clearTransformCache(element, true); // cache + inline transform\n if (resize) {\n element.style.width = '';\n element.style.height = '';\n }\n if (absolute) {\n element.style.width = '';\n element.style.height = '';\n element.style.left = '';\n element.style.top = '';\n }\n }\n });\n}\n\n// ─────────────────────────────────────────────\n// Self-registration\n// ─────────────────────────────────────────────\n\nSDKRegistry.fit = { registerPendingSetup };\n",
|
|
42
44
|
"/**\n * Easing Functions\n *\n * All easings are based on Robert Penner's easing equations\n * and normalized to accept/return values in the 0-1 range.\n *\n * Includes GSAP-compatible aliases (quad, cubic, quart, quint, strong)\n * and special easings (slow, rough) for migration compatibility.\n */\n\nimport type { EasingFunction } from '../types';\n\n// Linear\nexport const linear = (t: number): number => t;\nexport const none = linear;\n\n// Power easings (Quad, Cubic, Quart, Quint)\nexport const power1 = {\n in: (t: number): number => t * t,\n out: (t: number): number => t * (2 - t),\n inOut: (t: number): number => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),\n};\n\nexport const power2 = {\n in: (t: number): number => t * t * t,\n out: (t: number): number => --t * t * t + 1,\n inOut: (t: number): number => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),\n};\n\nexport const power3 = {\n in: (t: number): number => t * t * t * t,\n out: (t: number): number => 1 - --t * t * t * t,\n inOut: (t: number): number => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),\n};\n\nexport const power4 = {\n in: (t: number): number => t * t * t * t * t,\n out: (t: number): number => 1 + --t * t * t * t * t,\n inOut: (t: number): number => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t),\n};\n\n// GSAP-compatible aliases\nexport const quad = power1;\nexport const cubic = power2;\nexport const quart = power3;\nexport const quint = power4;\nexport const strong = power4;\n\n// Sine\nexport const sine = {\n in: (t: number): number => 1 - Math.cos((t * Math.PI) / 2),\n out: (t: number): number => Math.sin((t * Math.PI) / 2),\n inOut: (t: number): number => -(Math.cos(Math.PI * t) - 1) / 2,\n};\n\n// Exponential\n// GSAP uses a blended formula for easeIn to land precisely at the right value:\n// easeIn = p => (2^(10*(p-1))) * p + p^6 * (1-p)\n// easeOut and easeInOut are derived via the standard _insertEase pattern.\nexport const expo = {\n in: (t: number): number =>\n (2 ** (10 * (t - 1))) * t + t * t * t * t * t * t * (1 - t),\n out: (t: number): number =>\n 1 - ((2 ** (10 * (-t))) * (1 - t) + (1 - t) * (1 - t) * (1 - t) * (1 - t) * (1 - t) * (1 - t) * t),\n inOut: (t: number): number => {\n if (t < 0.5) {\n // easeIn(t * 2) / 2\n const p = t * 2;\n return ((2 ** (10 * (p - 1))) * p + p * p * p * p * p * p * (1 - p)) / 2;\n }\n // 1 - easeIn((1 - t) * 2) / 2\n const p = (1 - t) * 2;\n return 1 - ((2 ** (10 * (p - 1))) * p + p * p * p * p * p * p * (1 - p)) / 2;\n },\n};\n\n// Circular\nexport const circ = {\n in: (t: number): number => 1 - Math.sqrt(1 - t * t),\n out: (t: number): number => Math.sqrt(1 - --t * t),\n inOut: (t: number): number => {\n if ((t *= 2) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1);\n return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);\n },\n};\n\n// Back (overshoots then returns)\n// GSAP defines easeOut as the base: p => p ? ((--p) * p * ((s+1) * p + s) + 1) : 0\n// easeIn and easeInOut are derived via standard patterns.\n// Supports parameterized overshoot: back.out(1.4) — default overshoot is 1.70158.\nconst DEFAULT_BACK_OVERSHOOT = 1.70158;\nconst createBackEase = (overshoot = DEFAULT_BACK_OVERSHOOT) => {\n const easeOut = (t: number): number => t ? (--t * t * ((overshoot + 1) * t + overshoot) + 1) : 0;\n return {\n in: (t: number): number => 1 - easeOut(1 - t),\n out: easeOut,\n inOut: (t: number): number => {\n // GSAP's _easeInOutFromOut pattern\n if (t < 0.5) return (1 - easeOut(1 - t * 2)) / 2;\n return 0.5 + easeOut((t - 0.5) * 2) / 2;\n },\n };\n};\nexport const back = createBackEase();\n\n// Elastic (spring-like oscillation)\n// Supports parameterized amplitude and period: elastic.out(1, 0.3) — GSAP compatible.\nconst DEFAULT_ELASTIC_AMPLITUDE = 1;\nconst DEFAULT_ELASTIC_PERIOD = 0.3;\nconst createElasticEase = (amplitude = DEFAULT_ELASTIC_AMPLITUDE, period = DEFAULT_ELASTIC_PERIOD) => {\n // Clamp amplitude to at least 1 (matching GSAP behavior)\n const a = Math.max(1, amplitude);\n const s = period / (2 * Math.PI) * Math.asin(1 / a);\n const pInOut = period * 1.5;\n const sInOut = pInOut / (2 * Math.PI) * Math.asin(1 / a);\n return {\n in: (t: number): number => {\n if (t === 0) return 0;\n if (t === 1) return 1;\n return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t - s) * (2 * Math.PI)) / period));\n },\n out: (t: number): number => {\n if (t === 0) return 0;\n if (t === 1) return 1;\n return a * Math.pow(2, -10 * t) * Math.sin(((t - s) * (2 * Math.PI)) / period) + 1;\n },\n inOut: (t: number): number => {\n if (t === 0) return 0;\n if (t === 1) return 1;\n if ((t *= 2) < 1) {\n return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t - sInOut) * (2 * Math.PI)) / pInOut));\n }\n return a * Math.pow(2, -10 * (t -= 1)) * Math.sin(((t - sInOut) * (2 * Math.PI)) / pInOut) * 0.5 + 1;\n },\n };\n};\nexport const elastic = createElasticEase();\n\n// Bounce (bouncing ball effect)\nconst bounceOut = (t: number): number => {\n if (t < 1 / 2.75) {\n return 7.5625 * t * t;\n } else if (t < 2 / 2.75) {\n return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;\n } else if (t < 2.5 / 2.75) {\n return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;\n } else {\n return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;\n }\n};\n\nexport const bounce = {\n in: (t: number): number => 1 - bounceOut(1 - t),\n out: bounceOut,\n inOut: (t: number): number => {\n if (t < 0.5) return bounce.in(t * 2) * 0.5;\n return bounce.out(t * 2 - 1) * 0.5 + 0.5;\n },\n};\n\n// Slow (SlowMo-compatible easing)\n// Creates a slow-motion effect in the middle portion of the animation.\n// Equivalent to GSAP's SlowMo ease with default parameters (0.7 linearRatio, 0.7 power).\n// Algorithm matches GSAP's SlowMo.getRatio exactly (EasePack.js _createSlowMo):\n// Normal mode:\n// - ease-in (t < p1): r - (1 - t/p1)^4 * r (quartic)\n// - linear (p1..p3): r\n// - ease-out (t > p3): r + (t - r) * ((t-p3)/p1)^4 (quartic)\n// Yoyo mode (0→1→0):\n// - ease-in (t < p1): 1 - (1 - t/p1)^2 (quadratic)\n// - plateau (p1..p3): 1\n// - ease-out (t > p3): 1 - ((t-p3)/p1)^2 (quadratic)\nconst createSlowEase = (linearRatio = 0.7, power = 0.7, yoyo = false): EasingFunction => {\n const p = linearRatio !== 1 ? power : 0;\n const p1 = (1 - linearRatio) / 2;\n const p3 = p1 + linearRatio;\n const calcEnd = yoyo;\n\n return (t: number): number => {\n const r = t + (0.5 - t) * p;\n if (t < p1) {\n const inv = 1 - t / p1;\n if (calcEnd) {\n // Yoyo ease-in — GSAP uses quadratic: 1 - (1 - t/p1)^2\n return 1 - inv * inv;\n }\n // Normal ease-in — GSAP uses quartic: r - (1 - t/p1)^4 * r\n return r - inv * inv * inv * inv * r;\n } else if (t > p3) {\n const ratio = (t - p3) / p1;\n if (calcEnd) {\n // Yoyo ease-out — GSAP uses quadratic: 1 - ((t-p3)/p1)^2\n return t === 1 ? 0 : 1 - ratio * ratio;\n }\n // Normal ease-out — GSAP uses quartic: r + (t - r) * ((t-p3)/p1)^4\n return r + (t - r) * ratio * ratio * ratio * ratio;\n }\n\n return calcEnd ? 1 : r;\n };\n};\n\nexport const slow = {\n in: createSlowEase(0.7, 0.7, false),\n out: createSlowEase(0.7, 0.7, false),\n inOut: createSlowEase(0.7, 0.7, false),\n};\n\n// Rough (creates a randomized, jittery easing effect)\n// Uses deterministic noise based on position for consistency across renders.\n// Equivalent to GSAP's RoughEase with default parameters.\nconst createRoughEase = (strength = 1, points = 20, clamp = false): EasingFunction => {\n // Pre-generate deterministic random points using a simple hash\n const segments: Array<{ x: number; y: number }> = [];\n const inc = 1 / points;\n\n for (let i = 0; i < points; i++) {\n const x = i * inc;\n // Deterministic pseudo-random based on position (consistent across renders)\n const seed = (i * 1327 + 531) % 1000 / 1000;\n const randomOffset = (seed - 0.5) * strength * 0.4;\n // Base value follows linear progression with random displacement\n const y = x + randomOffset;\n segments.push({ x, y: clamp ? Math.max(0, Math.min(1, y)) : y });\n }\n // Ensure endpoints\n segments[0] = { x: 0, y: 0 };\n segments.push({ x: 1, y: 1 });\n\n return (t: number): number => {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n\n // Find the two surrounding segments\n let low = 0;\n let high = segments.length - 1;\n while (low < high - 1) {\n const mid = (low + high) >> 1;\n if (segments[mid].x <= t) {\n low = mid;\n } else {\n high = mid;\n }\n }\n\n const seg0 = segments[low];\n const seg1 = segments[high];\n const localT = (t - seg0.x) / (seg1.x - seg0.x);\n return seg0.y + (seg1.y - seg0.y) * localT;\n };\n};\n\nexport const rough = {\n in: createRoughEase(1, 20, true),\n out: createRoughEase(1, 20, true),\n inOut: createRoughEase(1, 20, true),\n};\n\n// Steps easing (discrete step function, like CSS steps())\nconst createStepsEase = (numSteps: number): EasingFunction => {\n return (t: number): number => {\n if (t >= 1) return 1;\n return Math.floor(t * numSteps) / numSteps;\n };\n};\n\nexport const steps = {\n in: createStepsEase(12),\n out: createStepsEase(12),\n inOut: createStepsEase(12),\n};\n\n// Easing lookup map\nconst easingMap: Record<string, EasingFunction> = {\n // Linear\n linear,\n none,\n\n // Power1 (= Quad)\n 'power1': power1.out,\n 'power1.in': power1.in,\n 'power1.out': power1.out,\n 'power1.inout': power1.inOut,\n\n // Power2 (= Cubic)\n 'power2': power2.out,\n 'power2.in': power2.in,\n 'power2.out': power2.out,\n 'power2.inout': power2.inOut,\n\n // Power3 (= Quart)\n 'power3': power3.out,\n 'power3.in': power3.in,\n 'power3.out': power3.out,\n 'power3.inout': power3.inOut,\n\n // Power4 (= Quint/Strong)\n 'power4': power4.out,\n 'power4.in': power4.in,\n 'power4.out': power4.out,\n 'power4.inout': power4.inOut,\n\n // GSAP-compatible aliases: Quad = Power1\n 'quad': power1.out,\n 'quad.in': power1.in,\n 'quad.out': power1.out,\n 'quad.inout': power1.inOut,\n\n // GSAP-compatible aliases: Cubic = Power2\n 'cubic': power2.out,\n 'cubic.in': power2.in,\n 'cubic.out': power2.out,\n 'cubic.inout': power2.inOut,\n\n // GSAP-compatible aliases: Quart = Power3\n 'quart': power3.out,\n 'quart.in': power3.in,\n 'quart.out': power3.out,\n 'quart.inout': power3.inOut,\n\n // GSAP-compatible aliases: Quint = Power4\n 'quint': power4.out,\n 'quint.in': power4.in,\n 'quint.out': power4.out,\n 'quint.inout': power4.inOut,\n\n // GSAP-compatible aliases: Strong = Power4\n 'strong': power4.out,\n 'strong.in': power4.in,\n 'strong.out': power4.out,\n 'strong.inout': power4.inOut,\n\n // Sine\n 'sine': sine.out,\n 'sine.in': sine.in,\n 'sine.out': sine.out,\n 'sine.inout': sine.inOut,\n\n // Expo\n 'expo': expo.out,\n 'expo.in': expo.in,\n 'expo.out': expo.out,\n 'expo.inout': expo.inOut,\n\n // Circ\n 'circ': circ.out,\n 'circ.in': circ.in,\n 'circ.out': circ.out,\n 'circ.inout': circ.inOut,\n\n // Back\n 'back': back.out,\n 'back.in': back.in,\n 'back.out': back.out,\n 'back.inout': back.inOut,\n\n // Elastic\n 'elastic': elastic.out,\n 'elastic.in': elastic.in,\n 'elastic.out': elastic.out,\n 'elastic.inout': elastic.inOut,\n\n // Bounce\n 'bounce': bounce.out,\n 'bounce.in': bounce.in,\n 'bounce.out': bounce.out,\n 'bounce.inout': bounce.inOut,\n\n // Slow (SlowMo) — GSAP only registers the bare \"slow\" name via _registerEase.\n // Direction-suffixed variants (slow.in, slow.out, slow.inout) are NOT registered\n // in GSAP and silently fall back to the default ease (power1.out).\n // We match this behavior for migration compatibility.\n 'slow': slow.inOut,\n 'slow.in': power1.out,\n 'slow.out': power1.out,\n 'slow.inout': power1.out,\n 'slowmo': slow.inOut,\n 'slowmo.in': power1.out,\n 'slowmo.out': power1.out,\n 'slowmo.inout': power1.out,\n\n // Rough — same as Slow: GSAP only registers the bare \"rough\" name.\n // Direction-suffixed variants fall back to default ease in GSAP.\n 'rough': rough.inOut,\n 'rough.in': power1.out,\n 'rough.out': power1.out,\n 'rough.inout': power1.out,\n\n // Steps\n 'steps': steps.inOut,\n 'steps.in': steps.in,\n 'steps.out': steps.out,\n 'steps.inout': steps.inOut,\n};\n\n// Parameterized easing factories\n// Maps easing name → factory that takes parsed params and returns { in, out, inOut }\ntype EasingSet = { in: EasingFunction; out: EasingFunction; inOut: EasingFunction };\nconst parameterizedFactories: Record<string, (...args: number[]) => EasingSet> = {\n back: (overshoot?: number) => createBackEase(overshoot),\n elastic: (amplitude?: number, period?: number) => createElasticEase(amplitude, period),\n slow: (linearRatio?: number, power?: number, yoyo?: number) => {\n const ease = createSlowEase(linearRatio, power, yoyo === 1);\n return { in: ease, out: ease, inOut: ease };\n },\n slowmo: (linearRatio?: number, power?: number, yoyo?: number) => {\n const ease = createSlowEase(linearRatio, power, yoyo === 1);\n return { in: ease, out: ease, inOut: ease };\n },\n rough: (strength?: number, points?: number, clamp?: number) => {\n const ease = createRoughEase(strength, points, clamp === 1);\n return { in: ease, out: ease, inOut: ease };\n },\n steps: (numSteps?: number) => {\n const ease = createStepsEase(numSteps ?? 12);\n return { in: ease, out: ease, inOut: ease };\n },\n};\n\n// Regex to match parameterized easing strings: \"name.direction(params)\" or \"name(params)\"\n// Examples: \"back.out(1.4)\", \"elastic.inOut(1, 0.5)\", \"steps(24)\"\nconst PARAM_EASING_RE = /^(\\w+)(?:\\.(\\w+))?\\(([^)]*)\\)$/;\n\n/**\n * Get an easing function by string name\n * Supports both simple names (\"back.out\") and parameterized (\"back.out(1.4)\")\n * @param name Easing name (case insensitive)\n * @returns Easing function, or power1.out if not found\n */\nexport function getEasing(name: string): EasingFunction {\n const key = name.toLowerCase();\n\n // Fast path: direct lookup for non-parameterized easings\n if (easingMap[key]) {\n return easingMap[key];\n }\n\n // Try parameterized easing: \"name.direction(params)\" or \"name(params)\"\n const match = PARAM_EASING_RE.exec(key);\n if (match) {\n const [, easeName, direction, paramsStr] = match;\n const factory = parameterizedFactories[easeName];\n if (factory) {\n const params = paramsStr\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n .map(Number);\n\n // Validate all params are valid numbers\n if (params.every((n) => !isNaN(n))) {\n const easeSet = factory(...params);\n // Normalize direction: \"inout\" → \"inOut\" (input is lowercased)\n const rawDir = direction || 'out';\n const dir = (rawDir === 'inout' ? 'inOut' : rawDir) as keyof EasingSet;\n const fn = easeSet[dir];\n if (fn) return fn;\n }\n }\n }\n\n console.warn(\n `[Motion] Unknown easing \"${name}\". Falling back to \"power1.out\". ` +\n `Valid easings: linear, power1-4.in/out/inOut, quad, cubic, quart, quint, strong, ` +\n `sine, expo, circ, back, elastic, bounce, slow, rough, steps. ` +\n `Parameterized: back.out(overshoot), elastic.out(amplitude, period), steps(n), slow(ratio, power, yoyo), rough(strength, points, clamp)`\n );\n return power1.out;\n}\n",
|
|
43
|
-
"/**\n * Animation - Individual animation unit\n *\n * Manages interpolation of properties over time with easing.\n * Pooled for performance.\n */\n\nimport type { AnimationTarget, StaggerVars } from '../types';\n\n/**\n * Internal animation configuration (not exported from SDK)\n */\nexport interface InternalAnimationConfig {\n repeat?: number;\n repeatDelay?: number;\n yoyo?: boolean;\n stagger?: number | StaggerVars;\n onStart?: () => void;\n onUpdate?: (progress: number) => void;\n onComplete?: () => void;\n onRepeat?: (repeatCount: number) => void;\n onReverseComplete?: () => void;\n /**\n * When true, Engine skips the immediate render(0) for FROM animations.\n * Used by Timeline._addBuilder so it can capture initial values BEFORE\n * FROM values are applied to the DOM, then render FROM itself afterward.\n */\n _skipInitialRender?: boolean;\n}\nimport type { Timeline } from './Timeline';\nimport { PropTween } from './PropTween';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { getEasing } from '../easing';\nimport { getCurrentValue, isColorProp, isFilterProp } from '../utils/PropertyParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\nexport class Animation {\n // Animation state\n private _id: number = 0;\n private _targets: AnimationTarget[] = [];\n private _duration: number = 0.5;\n private _delay: number = 0;\n private _time: number = 0;\n private _progress: number = 0;\n private _timeScale: number = 1;\n private _isActive: boolean = false;\n private _isReversed: boolean = false;\n\n // Property tweens (linked list head + tail for O(1) append)\n private _propTweens: PropTween | null = null;\n private _lastPropTween: PropTween | null = null;\n\n // Animation config\n private _easingFn: ((t: number) => number) | null = null;\n private _repeat: number = 0;\n private _repeatDelay: number = 0;\n private _yoyo: boolean = false;\n private _currentRepeat: number = 0;\n\n // Callbacks (properly typed)\n private _onStart?: () => void;\n private _onUpdate?: (progress: number) => void;\n private _onComplete?: () => void;\n private _onRepeat?: (repeatCount: number) => void;\n private _onReverseComplete?: () => void;\n private _startFired: boolean = false;\n private _completeFired: boolean = false;\n\n // Flag to indicate this animation is controlled by a Timeline\n // When true, Engine should NOT update this animation directly\n // and should NOT auto-pool this animation on completion\n private _timelineChild: boolean = false;\n\n // Flag for lazy start value capture (for timeline chaining)\n private _needsStartCapture: boolean = false;\n private _startCaptured: boolean = false;\n\n // Fit animation support: setup function that captures geometry and creates PropTweens at play time\n private _fitSetupFn: ((pool: typeof PropTweenPool) => void) | null = null;\n private _pendingFitSetup: ((pool: typeof PropTweenPool) => void) | null = null;\n // Fit cleanup: resets inline styles set by a previous fit so replay starts from natural position\n private _fitCleanupFn: (() => void) | null = null;\n\n // Callback for unregistering from Engine (avoids circular dependency)\n private _unregisterCallback: (() => void) | null = null;\n\n /**\n * Initialize the animation\n */\n init(\n id: number,\n targets: AnimationTarget[],\n duration: number,\n delay: number,\n ease: string,\n config?: InternalAnimationConfig\n ): this {\n this._id = id;\n this._targets = targets;\n this._duration = duration;\n this._delay = delay;\n this._easingFn = getEasing(ease);\n this._time = 0;\n this._progress = 0;\n this._timeScale = 1;\n this._isActive = false;\n this._isReversed = false;\n this._startFired = false;\n this._completeFired = false;\n this._currentRepeat = 0;\n\n // Apply config\n this._repeat = config?.repeat ?? 0;\n this._repeatDelay = config?.repeatDelay ?? 0;\n this._yoyo = config?.yoyo ?? false;\n this._onStart = config?.onStart;\n this._onUpdate = config?.onUpdate;\n this._onComplete = config?.onComplete;\n this._onRepeat = config?.onRepeat;\n this._onReverseComplete = config?.onReverseComplete;\n\n return this;\n }\n\n /**\n * Update animation by delta time\n */\n update(deltaTime: number): void {\n if (!this._isActive) return;\n\n // Apply time scale\n const scaledDelta = deltaTime * this._timeScale;\n\n // Update time\n this._time += this._isReversed ? -scaledDelta : scaledDelta;\n\n // Handle delays for forward playback\n if (!this._isReversed && this._time < 0) {\n this._time = 0;\n return;\n }\n\n // Calculate progress\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n\n // Fire onStart callback on first update\n if (!this._startFired && this._progress > 0) {\n this._startFired = true;\n this._onStart?.();\n }\n\n // Render at current progress\n this.render(this._progress);\n\n // Fire onUpdate callback with current progress\n this._onUpdate?.(this._progress);\n\n // Check if complete\n if (this._isReversed) {\n // When reversing, check if we've reached the beginning\n if (this._time <= 0) {\n this.handleComplete();\n }\n } else {\n // When playing forward, check if we've reached the end\n if (this._progress >= 1) {\n this.handleComplete();\n }\n }\n }\n\n /**\n * Render animation at specific progress (0-1)\n */\n render(progress: number): void {\n // Apply easing\n const easedProgress = this.applyEasing(progress);\n\n // Render all property tweens\n let propTween = this._propTweens;\n while (propTween) {\n propTween.render(easedProgress);\n propTween = propTween.next;\n }\n }\n\n /**\n * Render animation at an absolute time position (seconds)\n * Handles repeat, repeatDelay, and yoyo for timeline-driven scrubbing.\n * When driven by a Timeline (_isActive = true), also fires lifecycle callbacks.\n */\n renderAtTime(totalTime: number): void {\n const clampedTime = Math.max(0, totalTime);\n const duration = this._duration;\n const delay = this._delay;\n // Save repeat count before any update so we can detect cycle advances\n const prevRepeat = this._currentRepeat;\n\n if (duration === 0) {\n const progress = clampedTime >= delay ? 1 : 0;\n this._time = clampedTime;\n this._progress = progress;\n this.render(progress);\n } else {\n const activeTime = Math.max(0, clampedTime - delay);\n\n if (this._repeat === 0) {\n const progress = Math.min(1, activeTime / duration);\n this._time = Math.min(duration, activeTime);\n this._progress = progress;\n this.render(progress);\n } else {\n const repeatDelay = this._repeatDelay;\n const cycleDuration = duration + repeatDelay;\n const totalActiveDuration = this._repeat === -1\n ? Infinity\n : duration * (this._repeat + 1) + repeatDelay * this._repeat;\n\n const cappedActiveTime = this._repeat === -1\n ? activeTime\n : Math.min(activeTime, totalActiveDuration);\n\n const cycleIndex = cycleDuration > 0\n ? Math.floor(cappedActiveTime / cycleDuration)\n : 0;\n const cycleTime = cappedActiveTime - cycleIndex * cycleDuration;\n const inRepeatDelay = cycleTime > duration;\n\n let cycleProgress = duration > 0 ? Math.min(1, cycleTime / duration) : 1;\n const isYoyoCycle = this._yoyo && cycleIndex % 2 === 1;\n\n if (inRepeatDelay) {\n cycleProgress = isYoyoCycle ? 0 : 1;\n } else if (isYoyoCycle) {\n cycleProgress = 1 - cycleProgress;\n }\n\n this._currentRepeat = this._repeat === -1 ? cycleIndex : Math.min(cycleIndex, this._repeat);\n this._isReversed = isYoyoCycle;\n this._progress = cycleProgress;\n this._time = duration * cycleProgress;\n this.render(cycleProgress);\n }\n }\n\n // Fire lifecycle callbacks when driven by Timeline.\n // The Timeline sets _isActive = true via play() before the first active renderAtTime call\n // and _isActive = false via pause() when exiting the active window.\n // This guard ensures callbacks never fire during scrubbing (seek/progress ops).\n if (this._isActive) {\n // onStart — fires once on first active render\n if (!this._startFired) {\n this._startFired = true;\n try { this._onStart?.(); } catch (e) { console.error('[Motion] onStart callback error:', e); }\n }\n\n // onUpdate — fires every active frame\n try { this._onUpdate?.(this._progress); } catch (e) { console.error('[Motion] onUpdate callback error:', e); }\n\n // onRepeat — fires when the repeat cycle advances\n if (this._currentRepeat > prevRepeat) {\n try { this._onRepeat?.(this._currentRepeat); } catch (e) { console.error('[Motion] onRepeat callback error:', e); }\n }\n\n // onComplete — fires once when reaching total duration (not for infinite repeats)\n if (!this._completeFired && this._repeat !== -1) {\n if (clampedTime >= this.totalDuration()) {\n this._completeFired = true;\n try { this._onComplete?.(); } catch (e) { console.error('[Motion] onComplete callback error:', e); }\n }\n }\n }\n }\n\n /**\n * Apply easing function to progress\n */\n private applyEasing(progress: number): number {\n // Guard against null (fallback to linear if called before init)\n if (!this._easingFn) return progress;\n return this._easingFn(progress);\n }\n\n /**\n * Handle animation completion\n */\n private handleComplete(): void {\n // Check if we should repeat\n if (this._repeat === -1 || this._currentRepeat < this._repeat) {\n this._currentRepeat++;\n\n // Fire onRepeat callback with current repeat count\n this._onRepeat?.(this._currentRepeat);\n\n // Reset time for repeat\n if (this._yoyo) {\n this._isReversed = !this._isReversed;\n this._time = this._isReversed ? this._duration + this._delay : 0;\n } else {\n this._time = 0;\n this._startFired = false;\n }\n\n // Apply repeat delay\n this._time -= this._repeatDelay;\n\n return;\n }\n\n // Fire completion callback\n if (this._isReversed) {\n this._onReverseComplete?.();\n } else {\n this._onComplete?.();\n }\n\n // Animation is complete\n this._isActive = false;\n }\n\n /**\n * Play the animation\n * - If already playing forward, does nothing\n * - If paused, resumes from current position\n */\n play(from?: number): this {\n if (from !== undefined) {\n this.seek(from);\n this._isActive = true;\n this._isReversed = false;\n return this;\n }\n\n // If already playing forward, do nothing\n if (this._isActive && !this._isReversed) {\n return this;\n }\n\n this._isActive = true;\n this._isReversed = false;\n return this;\n }\n\n /**\n * Pause the animation\n */\n pause(atTime?: number): this {\n if (atTime !== undefined) {\n this._time = atTime;\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n this.render(this._progress);\n }\n this._isActive = false;\n return this;\n }\n\n /**\n * Reverse the animation\n */\n reverse(from?: number): this {\n if (from !== undefined) {\n this.seek(from);\n }\n this._isActive = true;\n this._isReversed = true;\n return this;\n }\n\n /**\n * Restart the animation\n */\n restart(includeDelay: boolean = true): this {\n this._time = includeDelay ? 0 : this._delay;\n this._progress = 0;\n this._isActive = true;\n this._isReversed = false;\n this._startFired = false;\n this._currentRepeat = 0;\n this.render(0);\n return this;\n }\n\n /**\n * Seek to specific position\n */\n seek(position: number): this {\n this._time = position;\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n this.render(this._progress);\n return this;\n }\n\n /**\n * Get/set progress (0-1)\n */\n progress(): number;\n progress(value: number): this;\n progress(value?: number): number | this {\n if (value === undefined) {\n return this._progress;\n }\n this._progress = Math.max(0, Math.min(1, value));\n // When setting progress to 0, set time to 0 so stagger delays work properly\n // Otherwise, calculate time from progress (positioned after delay)\n if (this._progress === 0) {\n this._time = 0;\n } else {\n this._time = this._progress * this._duration + this._delay;\n }\n this.render(this._progress);\n return this;\n }\n\n /**\n * Get/set time in seconds\n */\n time(): number;\n time(value: number): this;\n time(value?: number): number | this {\n if (value === undefined) {\n return this._time;\n }\n this._time = value;\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n this.render(this._progress);\n return this;\n }\n\n /**\n * Get/set timeScale multiplier\n */\n timeScale(): number;\n timeScale(value: number): this;\n timeScale(value?: number): number | this {\n if (value === undefined) {\n return this._timeScale;\n }\n this._timeScale = value;\n return this;\n }\n\n /**\n * Get timeScale multiplier (typed getter)\n */\n getTimeScale(): number {\n return this._timeScale;\n }\n\n /**\n * Kill the animation\n */\n kill(): void {\n this._isActive = false;\n this._unregisterCallback?.();\n }\n\n /**\n * Check if animation is active\n */\n isActive(): boolean {\n return this._isActive;\n }\n\n /**\n * Check if this animation is a child of a Timeline\n * When true, Engine won't update or auto-pool this animation\n */\n isTimelineChild(): boolean {\n return this._timelineChild;\n }\n\n /**\n * Set whether this animation is a child of a Timeline\n * Timeline children are controlled by their parent timeline and\n * won't be auto-pooled until the timeline is killed\n */\n setTimelineChild(isChild: boolean): void {\n this._timelineChild = isChild;\n }\n\n /**\n * Enable lazy start value capture (for timeline chaining)\n * When enabled, start values are captured on first render instead of at creation\n */\n setLazyStartCapture(enabled: boolean): void {\n this._needsStartCapture = enabled;\n this._startCaptured = false;\n }\n\n /**\n * Register a Fit setup function to execute at play time.\n * Stores both a persistent copy (_fitSetupFn) for replay and an active\n * copy (_pendingFitSetup) that is consumed by captureStartValues().\n */\n setPendingFitSetup(fn: (pool: typeof PropTweenPool) => void): void {\n this._fitSetupFn = fn;\n this._pendingFitSetup = fn;\n }\n\n /**\n * Register a cleanup function that resets inline styles written by a Fit animation.\n * Called during resetForReplay() so chained fits start from a clean slate on replay.\n */\n setFitCleanup(fn: () => void): void {\n this._fitCleanupFn = fn;\n }\n\n /**\n * Reset for timeline replay\n * Resets callback state for clean replay but KEEPS captured start values\n * so TO-only animations replay from their original start position.\n * Fit animations re-enable their setup so geometry is re-captured on the\n * next play (picking up any intervening DOM changes).\n */\n resetForReplay(): void {\n // Re-enable Fit setup so geometry is re-captured on the next play.\n // Run cleanup first to clear inline styles from the previous play cycle,\n // ensuring the first fit in a chain measures from the element's natural position.\n if (this._fitSetupFn) {\n this._fitCleanupFn?.();\n this._pendingFitSetup = this._fitSetupFn;\n this._startCaptured = false;\n }\n // Don't reset _startCaptured for normal animations - we want to KEEP the\n // originally captured start values so the animation replays from the same\n // starting point\n this._startFired = false;\n this._completeFired = false;\n }\n\n /**\n * Check if this animation needs lazy start capture but hasn't captured yet\n * Used by Timeline to trigger capture when animation starts\n */\n needsStartCapture(): boolean {\n return this._needsStartCapture && !this._startCaptured;\n }\n\n /**\n * Check if this animation uses lazy start capture (TO-only animation)\n * These animations should NEVER render before their start time,\n * even after values have been captured, to avoid overwriting earlier animations\n */\n isLazyCapture(): boolean {\n return this._needsStartCapture;\n }\n\n /**\n * Fire the onReverseComplete callback and reset start/complete flags.\n * Called by Timeline when an active animation exits the start boundary (going in reverse).\n * Resetting flags allows onStart and onComplete to fire correctly on the next forward play.\n */\n fireReverseComplete(): void {\n try { this._onReverseComplete?.(); } catch (e) { console.error('[Motion] onReverseComplete callback error:', e); }\n // Reset flags so callbacks fire correctly on next forward play\n this._startFired = false;\n this._completeFired = false;\n }\n\n /**\n * Set callback for unregistering from Engine (called on kill)\n * This avoids circular dependency with Engine\n */\n setUnregisterCallback(callback: () => void): void {\n this._unregisterCallback = callback;\n }\n\n /**\n * Capture current element values as start values for all PropTweens\n * This enables proper chaining in timelines\n */\n captureStartValues(): void {\n if (!this._needsStartCapture || this._startCaptured) return;\n\n // Fit handling — executes after the guard passes.\n // Releases any placeholder PropTweens created at build time, then\n // invokes the setup function which captures source and target element\n // geometry and builds PropTweens from the source→target delta.\n if (this._pendingFitSetup) {\n let propTween = this._propTweens;\n while (propTween) {\n const next = propTween.next;\n PropTweenPool.release(propTween);\n propTween = next;\n }\n this._propTweens = null;\n this._lastPropTween = null;\n\n this._pendingFitSetup(PropTweenPool);\n this._pendingFitSetup = null;\n this._startCaptured = true;\n return;\n }\n\n let propTween = this._propTweens;\n while (propTween) {\n // Only capture start values for DOM elements (not plain objects)\n if (propTween.target instanceof Element) {\n const element = propTween.target;\n const property = propTween.property;\n\n if (propTween.valueType === 'color' && isColorProp(property)) {\n // Capture current color value\n const currentColor = SDKRegistry.color?.getCurrentColor(element, property);\n if (currentColor && propTween.endColor) {\n propTween.startColor = currentColor;\n // Recalculate change for each channel\n propTween.changeColor = new Float32Array([\n propTween.endColor[0] - currentColor[0],\n propTween.endColor[1] - currentColor[1],\n propTween.endColor[2] - currentColor[2],\n propTween.endColor[3] - currentColor[3]\n ]);\n }\n } else if (propTween.valueType === 'filter' && isFilterProp(property)) {\n // Capture current filter value\n const currentFilters = SDKRegistry.filter?.getCurrentFilter(element);\n if (currentFilters && propTween.endFilters) {\n // Merge to ensure consistent array structure\n const merged = SDKRegistry.filter!.mergeFilterArrays(currentFilters, propTween.endFilters);\n propTween.startFilters = merged.start;\n propTween.endFilters = merged.end;\n }\n } else {\n // Scalar value - get current numeric value\n const currentValue = getCurrentValue(element, property);\n propTween.startValue = currentValue;\n propTween.change = propTween.endValue - propTween.startValue;\n }\n }\n propTween = propTween.next;\n }\n\n this._startCaptured = true;\n }\n\n /**\n * Get animation ID\n */\n getId(): number {\n return this._id;\n }\n\n /**\n * Get animation targets\n */\n getTargets(): AnimationTarget[] {\n return this._targets;\n }\n\n /**\n * Get duration\n */\n getDuration(): number {\n return this._duration;\n }\n\n /**\n * Get delay (used by Timeline for stagger positioning)\n */\n getDelay(): number {\n return this._delay;\n }\n\n /**\n * Clear delay (used by Timeline after extracting delay for positioning)\n */\n clearDelay(): void {\n this._delay = 0;\n }\n\n /**\n * Get the head of the PropTween linked list.\n * @internal - Use only within sdk package for property inspection (e.g. captureStartValues).\n */\n getFirstPropTween(): PropTween | null {\n return this._propTweens;\n }\n\n /**\n * Add a property tween to the linked list\n */\n addPropTween(propTween: PropTween): void {\n if (!this._lastPropTween) {\n this._propTweens = propTween;\n } else {\n this._lastPropTween.next = propTween;\n }\n this._lastPropTween = propTween;\n }\n\n /**\n * Get animation duration (including delay and repeats)\n * Returns Infinity for infinitely repeating animations (_repeat === -1).\n */\n totalDuration(): number {\n if (this._repeat === -1) return Infinity;\n const baseDuration = this._duration;\n const total = baseDuration * (this._repeat + 1) + this._repeatDelay * this._repeat;\n return this._delay + total;\n }\n\n /**\n * Reset for object pooling\n */\n reset(): void {\n this._id = 0;\n this._targets = [];\n this._duration = 0.5;\n this._delay = 0;\n this._time = 0;\n this._progress = 0;\n this._timeScale = 1;\n this._isActive = false;\n this._isReversed = false;\n this._easingFn = null;\n this._repeat = 0;\n this._repeatDelay = 0;\n this._yoyo = false;\n this._currentRepeat = 0;\n this._onStart = undefined;\n this._onUpdate = undefined;\n this._onComplete = undefined;\n this._onRepeat = undefined;\n this._onReverseComplete = undefined;\n this._startFired = false;\n this._completeFired = false;\n this._timelineChild = false;\n this._needsStartCapture = false;\n this._startCaptured = false;\n this._fitSetupFn = null;\n this._pendingFitSetup = null;\n this._fitCleanupFn = null;\n this._unregisterCallback = null;\n\n // Release all PropTweens back to pool\n const pool = PropTweenPool;\n let propTween = this._propTweens;\n while (propTween) {\n const next = propTween.next;\n pool.release(propTween);\n propTween = next;\n }\n this._propTweens = null;\n this._lastPropTween = null;\n }\n}\n",
|
|
45
|
+
"/**\n * Animation - Individual animation unit\n *\n * Manages interpolation of properties over time with easing.\n * Pooled for performance.\n */\n\nimport type { AnimationTarget, StaggerVars } from '../types';\n\n/**\n * Internal animation configuration (not exported from SDK)\n */\nexport interface InternalAnimationConfig {\n repeat?: number;\n repeatDelay?: number;\n yoyo?: boolean;\n stagger?: number | StaggerVars;\n onStart?: () => void;\n onUpdate?: (progress: number) => void;\n onComplete?: () => void;\n onRepeat?: (repeatCount: number) => void;\n onReverseComplete?: () => void;\n /**\n * When true, Engine skips the immediate render(0) for FROM animations.\n * Used by Timeline._addBuilder so it can capture initial values BEFORE\n * FROM values are applied to the DOM, then render FROM itself afterward.\n */\n _skipInitialRender?: boolean;\n}\nimport type { Timeline } from './Timeline';\nimport { PropTween } from './PropTween';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { getEasing } from '../easing';\nimport { getCurrentValue, isColorProp, isFilterProp, isClipPathProp } from '../utils/PropertyParser';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\nexport class Animation {\n // Animation state\n private _id: number = 0;\n private _targets: AnimationTarget[] = [];\n private _duration: number = 0.5;\n private _delay: number = 0;\n private _time: number = 0;\n private _progress: number = 0;\n private _timeScale: number = 1;\n private _isActive: boolean = false;\n private _isReversed: boolean = false;\n\n // Property tweens (linked list head + tail for O(1) append)\n private _propTweens: PropTween | null = null;\n private _lastPropTween: PropTween | null = null;\n\n // Animation config\n private _easingFn: ((t: number) => number) | null = null;\n private _repeat: number = 0;\n private _repeatDelay: number = 0;\n private _yoyo: boolean = false;\n private _currentRepeat: number = 0;\n\n // Callbacks (properly typed)\n private _onStart?: () => void;\n private _onUpdate?: (progress: number) => void;\n private _onComplete?: () => void;\n private _onRepeat?: (repeatCount: number) => void;\n private _onReverseComplete?: () => void;\n private _startFired: boolean = false;\n private _completeFired: boolean = false;\n\n // Flag to indicate this animation is controlled by a Timeline\n // When true, Engine should NOT update this animation directly\n // and should NOT auto-pool this animation on completion\n private _timelineChild: boolean = false;\n\n // Flag for lazy start value capture (for timeline chaining)\n private _needsStartCapture: boolean = false;\n private _startCaptured: boolean = false;\n\n // Fit animation support: setup function that captures geometry and creates PropTweens at play time\n private _fitSetupFn: ((pool: typeof PropTweenPool) => void) | null = null;\n private _pendingFitSetup: ((pool: typeof PropTweenPool) => void) | null = null;\n // Fit cleanup: resets inline styles set by a previous fit so replay starts from natural position\n private _fitCleanupFn: (() => void) | null = null;\n\n // Callback for unregistering from Engine (avoids circular dependency)\n private _unregisterCallback: (() => void) | null = null;\n\n /**\n * Initialize the animation\n */\n init(\n id: number,\n targets: AnimationTarget[],\n duration: number,\n delay: number,\n ease: string,\n config?: InternalAnimationConfig\n ): this {\n this._id = id;\n this._targets = targets;\n this._duration = duration;\n this._delay = delay;\n this._easingFn = getEasing(ease);\n this._time = 0;\n this._progress = 0;\n this._timeScale = 1;\n this._isActive = false;\n this._isReversed = false;\n this._startFired = false;\n this._completeFired = false;\n this._currentRepeat = 0;\n\n // Apply config\n this._repeat = config?.repeat ?? 0;\n this._repeatDelay = config?.repeatDelay ?? 0;\n this._yoyo = config?.yoyo ?? false;\n this._onStart = config?.onStart;\n this._onUpdate = config?.onUpdate;\n this._onComplete = config?.onComplete;\n this._onRepeat = config?.onRepeat;\n this._onReverseComplete = config?.onReverseComplete;\n\n return this;\n }\n\n /**\n * Update animation by delta time\n */\n update(deltaTime: number): void {\n if (!this._isActive) return;\n\n // Apply time scale\n const scaledDelta = deltaTime * this._timeScale;\n\n // Update time\n this._time += this._isReversed ? -scaledDelta : scaledDelta;\n\n // Handle delays for forward playback\n if (!this._isReversed && this._time < 0) {\n this._time = 0;\n return;\n }\n\n // Calculate progress\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n\n // Fire onStart callback on first update\n if (!this._startFired && this._progress > 0) {\n this._startFired = true;\n this._onStart?.();\n }\n\n // Render at current progress\n this.render(this._progress);\n\n // Fire onUpdate callback with current progress\n this._onUpdate?.(this._progress);\n\n // Check if complete\n if (this._isReversed) {\n // When reversing, check if we've reached the beginning\n if (this._time <= 0) {\n this.handleComplete();\n }\n } else {\n // When playing forward, check if we've reached the end\n if (this._progress >= 1) {\n this.handleComplete();\n }\n }\n }\n\n /**\n * Render animation at specific progress (0-1)\n */\n render(progress: number): void {\n // Apply easing\n const easedProgress = this.applyEasing(progress);\n\n // Render all property tweens\n let propTween = this._propTweens;\n while (propTween) {\n propTween.render(easedProgress);\n propTween = propTween.next;\n }\n }\n\n /**\n * Render animation at an absolute time position (seconds)\n * Handles repeat, repeatDelay, and yoyo for timeline-driven scrubbing.\n * When driven by a Timeline (_isActive = true), also fires lifecycle callbacks.\n */\n renderAtTime(totalTime: number): void {\n const clampedTime = Math.max(0, totalTime);\n const duration = this._duration;\n const delay = this._delay;\n // Save repeat count before any update so we can detect cycle advances\n const prevRepeat = this._currentRepeat;\n\n if (duration === 0) {\n const progress = clampedTime >= delay ? 1 : 0;\n this._time = clampedTime;\n this._progress = progress;\n this.render(progress);\n } else {\n const activeTime = Math.max(0, clampedTime - delay);\n\n if (this._repeat === 0) {\n const progress = Math.min(1, activeTime / duration);\n this._time = Math.min(duration, activeTime);\n this._progress = progress;\n this.render(progress);\n } else {\n const repeatDelay = this._repeatDelay;\n const cycleDuration = duration + repeatDelay;\n const totalActiveDuration = this._repeat === -1\n ? Infinity\n : duration * (this._repeat + 1) + repeatDelay * this._repeat;\n\n const cappedActiveTime = this._repeat === -1\n ? activeTime\n : Math.min(activeTime, totalActiveDuration);\n\n const cycleIndex = cycleDuration > 0\n ? Math.floor(cappedActiveTime / cycleDuration)\n : 0;\n const cycleTime = cappedActiveTime - cycleIndex * cycleDuration;\n const inRepeatDelay = cycleTime > duration;\n\n let cycleProgress = duration > 0 ? Math.min(1, cycleTime / duration) : 1;\n const isYoyoCycle = this._yoyo && cycleIndex % 2 === 1;\n\n if (inRepeatDelay) {\n cycleProgress = isYoyoCycle ? 0 : 1;\n } else if (isYoyoCycle) {\n cycleProgress = 1 - cycleProgress;\n }\n\n this._currentRepeat = this._repeat === -1 ? cycleIndex : Math.min(cycleIndex, this._repeat);\n this._isReversed = isYoyoCycle;\n this._progress = cycleProgress;\n this._time = duration * cycleProgress;\n this.render(cycleProgress);\n }\n }\n\n // Fire lifecycle callbacks when driven by Timeline.\n // The Timeline sets _isActive = true via play() before the first active renderAtTime call\n // and _isActive = false via pause() when exiting the active window.\n // This guard ensures callbacks never fire during scrubbing (seek/progress ops).\n if (this._isActive) {\n // onStart — fires once on first active render\n if (!this._startFired) {\n this._startFired = true;\n try { this._onStart?.(); } catch (e) { console.error('[Motion] onStart callback error:', e); }\n }\n\n // onUpdate — fires every active frame\n try { this._onUpdate?.(this._progress); } catch (e) { console.error('[Motion] onUpdate callback error:', e); }\n\n // onRepeat — fires when the repeat cycle advances\n if (this._currentRepeat > prevRepeat) {\n try { this._onRepeat?.(this._currentRepeat); } catch (e) { console.error('[Motion] onRepeat callback error:', e); }\n }\n\n // onComplete — fires once when reaching total duration (not for infinite repeats)\n if (!this._completeFired && this._repeat !== -1) {\n if (clampedTime >= this.totalDuration()) {\n this._completeFired = true;\n try { this._onComplete?.(); } catch (e) { console.error('[Motion] onComplete callback error:', e); }\n }\n }\n }\n }\n\n /**\n * Apply easing function to progress\n */\n private applyEasing(progress: number): number {\n // Guard against null (fallback to linear if called before init)\n if (!this._easingFn) return progress;\n return this._easingFn(progress);\n }\n\n /**\n * Handle animation completion\n */\n private handleComplete(): void {\n // Check if we should repeat\n if (this._repeat === -1 || this._currentRepeat < this._repeat) {\n this._currentRepeat++;\n\n // Fire onRepeat callback with current repeat count\n this._onRepeat?.(this._currentRepeat);\n\n // Reset time for repeat\n if (this._yoyo) {\n this._isReversed = !this._isReversed;\n this._time = this._isReversed ? this._duration + this._delay : 0;\n } else {\n this._time = 0;\n this._startFired = false;\n }\n\n // Apply repeat delay\n this._time -= this._repeatDelay;\n\n return;\n }\n\n // Fire completion callback\n if (this._isReversed) {\n this._onReverseComplete?.();\n } else {\n this._onComplete?.();\n }\n\n // Animation is complete\n this._isActive = false;\n }\n\n /**\n * Play the animation\n * - If already playing forward, does nothing\n * - If paused, resumes from current position\n */\n play(from?: number): this {\n if (from !== undefined) {\n this.seek(from);\n this._isActive = true;\n this._isReversed = false;\n return this;\n }\n\n // If already playing forward, do nothing\n if (this._isActive && !this._isReversed) {\n return this;\n }\n\n this._isActive = true;\n this._isReversed = false;\n return this;\n }\n\n /**\n * Pause the animation\n */\n pause(atTime?: number): this {\n if (atTime !== undefined) {\n this._time = atTime;\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n this.render(this._progress);\n }\n this._isActive = false;\n return this;\n }\n\n /**\n * Reverse the animation\n */\n reverse(from?: number): this {\n if (from !== undefined) {\n this.seek(from);\n }\n this._isActive = true;\n this._isReversed = true;\n return this;\n }\n\n /**\n * Restart the animation\n */\n restart(includeDelay: boolean = true): this {\n this._time = includeDelay ? 0 : this._delay;\n this._progress = 0;\n this._isActive = true;\n this._isReversed = false;\n this._startFired = false;\n this._currentRepeat = 0;\n this.render(0);\n return this;\n }\n\n /**\n * Seek to specific position\n */\n seek(position: number): this {\n this._time = position;\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n this.render(this._progress);\n return this;\n }\n\n /**\n * Get/set progress (0-1)\n */\n progress(): number;\n progress(value: number): this;\n progress(value?: number): number | this {\n if (value === undefined) {\n return this._progress;\n }\n this._progress = Math.max(0, Math.min(1, value));\n // When setting progress to 0, set time to 0 so stagger delays work properly\n // Otherwise, calculate time from progress (positioned after delay)\n if (this._progress === 0) {\n this._time = 0;\n } else {\n this._time = this._progress * this._duration + this._delay;\n }\n this.render(this._progress);\n return this;\n }\n\n /**\n * Get/set time in seconds\n */\n time(): number;\n time(value: number): this;\n time(value?: number): number | this {\n if (value === undefined) {\n return this._time;\n }\n this._time = value;\n const activeTime = Math.max(0, this._time - this._delay);\n this._progress = this._duration > 0 ? Math.min(1, activeTime / this._duration) : 1;\n this.render(this._progress);\n return this;\n }\n\n /**\n * Get/set timeScale multiplier\n */\n timeScale(): number;\n timeScale(value: number): this;\n timeScale(value?: number): number | this {\n if (value === undefined) {\n return this._timeScale;\n }\n this._timeScale = value;\n return this;\n }\n\n /**\n * Get timeScale multiplier (typed getter)\n */\n getTimeScale(): number {\n return this._timeScale;\n }\n\n /**\n * Kill the animation\n */\n kill(): void {\n this._isActive = false;\n this._unregisterCallback?.();\n }\n\n /**\n * Check if animation is active\n */\n isActive(): boolean {\n return this._isActive;\n }\n\n /**\n * Check if this animation is a child of a Timeline\n * When true, Engine won't update or auto-pool this animation\n */\n isTimelineChild(): boolean {\n return this._timelineChild;\n }\n\n /**\n * Set whether this animation is a child of a Timeline\n * Timeline children are controlled by their parent timeline and\n * won't be auto-pooled until the timeline is killed\n */\n setTimelineChild(isChild: boolean): void {\n this._timelineChild = isChild;\n }\n\n /**\n * Enable lazy start value capture (for timeline chaining)\n * When enabled, start values are captured on first render instead of at creation\n */\n setLazyStartCapture(enabled: boolean): void {\n this._needsStartCapture = enabled;\n this._startCaptured = false;\n }\n\n /**\n * Register a Fit setup function to execute at play time.\n * Stores both a persistent copy (_fitSetupFn) for replay and an active\n * copy (_pendingFitSetup) that is consumed by captureStartValues().\n */\n setPendingFitSetup(fn: (pool: typeof PropTweenPool) => void): void {\n this._fitSetupFn = fn;\n this._pendingFitSetup = fn;\n }\n\n /**\n * Register a cleanup function that resets inline styles written by a Fit animation.\n * Called during resetForReplay() so chained fits start from a clean slate on replay.\n */\n setFitCleanup(fn: () => void): void {\n this._fitCleanupFn = fn;\n }\n\n /**\n * Reset for timeline replay\n * Resets callback state for clean replay but KEEPS captured start values\n * so TO-only animations replay from their original start position.\n * Fit animations re-enable their setup so geometry is re-captured on the\n * next play (picking up any intervening DOM changes).\n */\n resetForReplay(): void {\n // Re-enable Fit setup so geometry is re-captured on the next play.\n // Run cleanup first to clear inline styles from the previous play cycle,\n // ensuring the first fit in a chain measures from the element's natural position.\n if (this._fitSetupFn) {\n this._fitCleanupFn?.();\n this._pendingFitSetup = this._fitSetupFn;\n this._startCaptured = false;\n }\n // Don't reset _startCaptured for normal animations - we want to KEEP the\n // originally captured start values so the animation replays from the same\n // starting point\n this._startFired = false;\n this._completeFired = false;\n }\n\n /**\n * Check if this animation needs lazy start capture but hasn't captured yet\n * Used by Timeline to trigger capture when animation starts\n */\n needsStartCapture(): boolean {\n return this._needsStartCapture && !this._startCaptured;\n }\n\n /**\n * Check if this animation uses lazy start capture (TO-only animation)\n * These animations should NEVER render before their start time,\n * even after values have been captured, to avoid overwriting earlier animations\n */\n isLazyCapture(): boolean {\n return this._needsStartCapture;\n }\n\n /**\n * Fire the onReverseComplete callback and reset start/complete flags.\n * Called by Timeline when an active animation exits the start boundary (going in reverse).\n * Resetting flags allows onStart and onComplete to fire correctly on the next forward play.\n */\n fireReverseComplete(): void {\n try { this._onReverseComplete?.(); } catch (e) { console.error('[Motion] onReverseComplete callback error:', e); }\n // Reset flags so callbacks fire correctly on next forward play\n this._startFired = false;\n this._completeFired = false;\n }\n\n /**\n * Set callback for unregistering from Engine (called on kill)\n * This avoids circular dependency with Engine\n */\n setUnregisterCallback(callback: () => void): void {\n this._unregisterCallback = callback;\n }\n\n /**\n * Capture current element values as start values for all PropTweens\n * This enables proper chaining in timelines\n */\n captureStartValues(): void {\n if (!this._needsStartCapture || this._startCaptured) return;\n\n // Fit handling — executes after the guard passes.\n // Releases any placeholder PropTweens created at build time, then\n // invokes the setup function which captures source and target element\n // geometry and builds PropTweens from the source→target delta.\n if (this._pendingFitSetup) {\n let propTween = this._propTweens;\n while (propTween) {\n const next = propTween.next;\n PropTweenPool.release(propTween);\n propTween = next;\n }\n this._propTweens = null;\n this._lastPropTween = null;\n\n this._pendingFitSetup(PropTweenPool);\n this._pendingFitSetup = null;\n this._startCaptured = true;\n return;\n }\n\n let propTween = this._propTweens;\n while (propTween) {\n // Only capture start values for DOM elements (not plain objects)\n if (propTween.target instanceof Element) {\n const element = propTween.target;\n const property = propTween.property;\n\n if (propTween.valueType === 'color' && isColorProp(property)) {\n // Capture current color value\n const currentColor = SDKRegistry.color?.getCurrentColor(element, property);\n if (currentColor && propTween.endColor) {\n propTween.startColor = currentColor;\n // Recalculate change for each channel\n propTween.changeColor = new Float32Array([\n propTween.endColor[0] - currentColor[0],\n propTween.endColor[1] - currentColor[1],\n propTween.endColor[2] - currentColor[2],\n propTween.endColor[3] - currentColor[3]\n ]);\n }\n } else if (propTween.valueType === 'filter' && isFilterProp(property)) {\n // Capture current filter value\n const currentFilters = SDKRegistry.filter?.getCurrentFilter(element);\n if (currentFilters && propTween.endFilters) {\n // Merge to ensure consistent array structure\n const merged = SDKRegistry.filter!.mergeFilterArrays(currentFilters, propTween.endFilters);\n propTween.startFilters = merged.start;\n propTween.endFilters = merged.end;\n }\n } else if (propTween.valueType === 'clipPath' && isClipPathProp(property)) {\n // Capture current clip-path value as the START so animations begin\n // from the live computed shape (matches filter/color behavior).\n // Only override when shapes are interpolation-compatible — otherwise\n // keep the parser-provided start so we don't break a deliberate\n // cross-shape config (which falls back to a hard swap at progress 0.5).\n const clipPathReg = SDKRegistry.clipPath;\n if (clipPathReg && propTween.endClipPath) {\n const currentClipPath = clipPathReg.getCurrentClipPath(element);\n if (currentClipPath && clipPathReg.canInterpolate(currentClipPath, propTween.endClipPath)) {\n propTween.startClipPath = currentClipPath;\n }\n }\n } else {\n // Scalar value - get current numeric value\n const currentValue = getCurrentValue(element, property);\n propTween.startValue = currentValue;\n propTween.change = propTween.endValue - propTween.startValue;\n }\n }\n propTween = propTween.next;\n }\n\n this._startCaptured = true;\n }\n\n /**\n * Get animation ID\n */\n getId(): number {\n return this._id;\n }\n\n /**\n * Get animation targets\n */\n getTargets(): AnimationTarget[] {\n return this._targets;\n }\n\n /**\n * Get duration\n */\n getDuration(): number {\n return this._duration;\n }\n\n /**\n * Get delay (used by Timeline for stagger positioning)\n */\n getDelay(): number {\n return this._delay;\n }\n\n /**\n * Clear delay (used by Timeline after extracting delay for positioning)\n */\n clearDelay(): void {\n this._delay = 0;\n }\n\n /**\n * Get the head of the PropTween linked list.\n * @internal - Use only within sdk package for property inspection (e.g. captureStartValues).\n */\n getFirstPropTween(): PropTween | null {\n return this._propTweens;\n }\n\n /**\n * Add a property tween to the linked list\n */\n addPropTween(propTween: PropTween): void {\n if (!this._lastPropTween) {\n this._propTweens = propTween;\n } else {\n this._lastPropTween.next = propTween;\n }\n this._lastPropTween = propTween;\n }\n\n /**\n * Get animation duration (including delay and repeats)\n * Returns Infinity for infinitely repeating animations (_repeat === -1).\n */\n totalDuration(): number {\n if (this._repeat === -1) return Infinity;\n const baseDuration = this._duration;\n const total = baseDuration * (this._repeat + 1) + this._repeatDelay * this._repeat;\n return this._delay + total;\n }\n\n /**\n * Reset for object pooling\n */\n reset(): void {\n this._id = 0;\n this._targets = [];\n this._duration = 0.5;\n this._delay = 0;\n this._time = 0;\n this._progress = 0;\n this._timeScale = 1;\n this._isActive = false;\n this._isReversed = false;\n this._easingFn = null;\n this._repeat = 0;\n this._repeatDelay = 0;\n this._yoyo = false;\n this._currentRepeat = 0;\n this._onStart = undefined;\n this._onUpdate = undefined;\n this._onComplete = undefined;\n this._onRepeat = undefined;\n this._onReverseComplete = undefined;\n this._startFired = false;\n this._completeFired = false;\n this._timelineChild = false;\n this._needsStartCapture = false;\n this._startCaptured = false;\n this._fitSetupFn = null;\n this._pendingFitSetup = null;\n this._fitCleanupFn = null;\n this._unregisterCallback = null;\n\n // Release all PropTweens back to pool\n const pool = PropTweenPool;\n let propTween = this._propTweens;\n while (propTween) {\n const next = propTween.next;\n pool.release(propTween);\n propTween = next;\n }\n this._propTweens = null;\n this._lastPropTween = null;\n }\n}\n",
|
|
44
46
|
"/**\n * AnimationPool - Singleton object pool for Animation instances\n *\n * Reuses Animation objects to minimize garbage collection overhead\n */\n\nimport { Animation } from '../core/Animation';\nimport { ObjectPool } from './ObjectPool';\n\nconst pool = new ObjectPool<Animation>(() => new Animation(), 100);\n\nexport const AnimationPool = {\n acquire: () => pool.acquire(),\n release: (animation: Animation) => pool.release(animation),\n getPoolSize: () => pool.getPoolSize(),\n getTotalCreated: () => pool.getTotalCreated(),\n clear: () => pool.clear(),\n};\n\nexport type AnimationPool = typeof AnimationPool;\n",
|
|
45
|
-
"/**\n * Engine Singleton - Central animation registry and coordinator\n *\n * Manages:\n * - Global animation registry\n * - Named timeline registry\n * - Object pool coordination\n * - Ticker integration\n * - Render queue routing\n */\n\nimport { Ticker } from './Ticker';\nimport { Animation } from './Animation';\nimport { Timeline } from './Timeline';\nimport { AnimationBuilder } from './AnimationBuilder';\nimport { AnimationPool } from '../memory/AnimationPool';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { parseProperty, type PropertyValue } from '../utils/PropertyParser';\nimport { setTransformValue, buildTransformString } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { cleanupPathParser } from '../utils/PathParser';\nimport type { AnimationVars, StaggerVars, AnimationTarget } from '../types';\nimport type { InternalAnimationConfig } from './Animation';\n\nexport class Engine {\n private static instance: Engine;\n\n // Animation registry - array for fast iteration, map for O(1) ID lookup\n private animations: Animation[] = [];\n private animationIdToIndex: Map<number, number> = new Map();\n private nextAnimationId: number = 1;\n private totalCreated: number = 0;\n\n // Pre-allocated snapshot buffer for safe mid-iteration mutation handling\n private _animationSnapshot: Animation[] = [];\n\n // Timeline registry (named timelines)\n private timelines: Map<string, Timeline> = new Map();\n\n // Ticker reference\n private ticker: Ticker;\n\n // Pools\n private animationPool: AnimationPool;\n private propTweenPool: PropTweenPool;\n\n private constructor() {\n this.ticker = Ticker.getInstance();\n this.animationPool = AnimationPool;\n this.propTweenPool = PropTweenPool;\n\n // Register ticker callback\n this.ticker.add(this.update, 0);\n }\n\n /**\n * Get the singleton Engine instance\n */\n static getInstance(): Engine {\n if (!Engine.instance) {\n Engine.instance = new Engine();\n // Set up Timeline registration callback (breaks circular dependency)\n Timeline.setEngineRegisterCallback((name: string, timeline: Timeline) => {\n const engine = Engine.instance!;\n if (!engine.hasTimeline(name)) {\n engine.registerTimeline(name, timeline);\n }\n });\n // Set up AnimationBuilder callback to break circular dependency\n AnimationBuilder.setCreateAnimationCallback(\n (targets, vars, duration, delay, ease, isFrom, fromVars, config) => {\n return Engine.instance!.createAnimation(targets, vars, duration, delay, ease, isFrom, fromVars, config);\n }\n );\n }\n return Engine.instance;\n }\n\n /**\n * Main update loop called by Ticker\n */\n private update = (deltaTime: number): void => {\n // Snapshot the live array before iteration so that onComplete callbacks\n // which call kill() on other animations (triggering swap-and-pop on\n // this.animations) cannot corrupt the current loop.\n const snapshot = this._animationSnapshot;\n const liveLen = this.animations.length;\n snapshot.length = liveLen;\n for (let i = 0; i < liveLen; i++) {\n snapshot[i] = this.animations[i];\n }\n\n const toRemove: number[] = [];\n\n for (let i = 0, len = snapshot.length; i < len; i++) {\n const animation = snapshot[i];\n\n // Skip animations that are children of a Timeline (timeline controls them)\n if (animation.isTimelineChild()) continue;\n\n if (animation.isActive()) {\n animation.update(deltaTime);\n\n // Check if animation completed and should be pooled\n // Timeline children are NOT auto-pooled - they persist until timeline.kill()\n if (!animation.isActive() && !animation.isTimelineChild()) {\n toRemove.push(animation.getId());\n }\n }\n }\n\n // Remove completed animations (return to pool for performance)\n for (const id of toRemove) {\n this.unregisterAnimation(id);\n }\n\n // Update all active timelines\n for (const timeline of this.timelines.values()) {\n if (timeline.isActive()) {\n timeline.update(deltaTime);\n }\n }\n };\n\n /**\n * Create a new animation (called by AnimationBuilder)\n * Returns either a single Animation or an array of Animations (for stagger)\n */\n createAnimation(\n targets: AnimationTarget[],\n vars: AnimationVars,\n duration: number,\n delay: number,\n ease: string,\n isFrom: boolean,\n fromVars?: AnimationVars,\n config?: InternalAnimationConfig\n ): Animation | Animation[] {\n // Check if stagger is specified (from config, not vars)\n const staggerConfig = config?.stagger;\n\n if (staggerConfig && targets.length > 1) {\n // Create staggered animations\n return this.createStaggeredAnimations(\n targets,\n vars,\n duration,\n delay,\n ease,\n isFrom,\n fromVars,\n staggerConfig,\n config\n );\n }\n\n // Create single animation for all targets\n // Acquire animation from pool\n const animation = this.animationPool.acquire();\n\n // Generate ID and register\n const id = this.registerAnimation(animation);\n\n // Initialize animation with config\n animation.init(id, targets, duration, delay, ease, config);\n\n // Parse properties and create PropTweens for each target\n this.setupPropertyTweens(animation, targets, vars, isFrom, fromVars);\n\n // If duration is 0, immediately render at final values (instant set)\n if (duration === 0) {\n animation.render(1);\n } else if (isFrom && !config?._skipInitialRender) {\n // If this is a \"from\" animation (and not instant), immediately render starting values.\n // Timeline children skip this — Timeline captures initial values first, then renders FROM.\n animation.render(0);\n }\n\n return animation;\n }\n\n /**\n * Create staggered animations - one animation per target\n */\n private createStaggeredAnimations(\n targets: AnimationTarget[],\n vars: AnimationVars,\n duration: number,\n baseDelay: number,\n ease: string,\n isFrom: boolean,\n fromVars: AnimationVars | undefined,\n staggerConfig: number | StaggerVars,\n config?: InternalAnimationConfig\n ): Animation[] {\n // Calculate stagger delays (gracefully handle if StaggerResolver not loaded)\n if (!SDKRegistry.stagger) {\n // StaggerResolver not loaded - create animations without stagger\n const animation = this.animationPool.acquire();\n const id = this.registerAnimation(animation);\n animation.init(id, targets, duration, baseDelay, ease, config);\n this.setupPropertyTweens(animation, targets, vars, isFrom, fromVars);\n if (duration === 0) {\n animation.render(1);\n } else if (isFrom && !config?._skipInitialRender) {\n animation.render(0);\n }\n return [animation];\n }\n const staggerDelays = SDKRegistry.stagger.resolve(targets, staggerConfig);\n\n // Create one animation per target\n const animations: Animation[] = [];\n\n for (let i = 0; i < targets.length; i++) {\n const target = targets[i];\n const staggerDelay = staggerDelays[i];\n\n // Acquire animation from pool\n const animation = this.animationPool.acquire();\n\n // Generate ID and register\n const id = this.registerAnimation(animation);\n\n // Initialize animation with stagger delay added to base delay\n animation.init(id, [target], duration, baseDelay + staggerDelay, ease, config);\n\n // Parse properties and create PropTweens for this single target\n this.setupPropertyTweens(animation, [target], vars, isFrom, fromVars);\n\n // If duration is 0, immediately render at final values (instant set)\n if (duration === 0) {\n animation.render(1);\n } else if (isFrom && !config?._skipInitialRender) {\n // Timeline children skip this — Timeline captures initial values first, then renders FROM.\n animation.render(0);\n }\n\n animations.push(animation);\n }\n\n return animations;\n }\n\n /**\n * Set up property tweens for an animation\n */\n private setupPropertyTweens(\n animation: Animation,\n targets: AnimationTarget[],\n vars: AnimationVars,\n isFrom: boolean,\n fromVars?: AnimationVars\n ): void {\n // Normalize kebab-case CSS property names to camelCase (e.g. 'background-size' → 'backgroundSize').\n // This matches GSAP behavior and ensures element.style[prop] assignment works correctly.\n const varsRecord = normalizePropertyKeys(vars as Record<string, PropertyValue>);\n const fromVarsRecord = fromVars ? normalizePropertyKeys(fromVars as Record<string, PropertyValue>) : undefined;\n\n // Collect all properties from both vars (to) and fromVars (from)\n // This ensures properties only in 'from' are also animated\n const toKeys = Object.keys(varsRecord);\n const fromKeys = fromVarsRecord ? Object.keys(fromVarsRecord) : [];\n const propsToAnimate = [...new Set([...toKeys, ...fromKeys])];\n\n // Check if perspective should be animated (both from and to have explicit values)\n // or set as immediate context (only one side specified)\n const hasPerspectiveInTo = 'perspective' in varsRecord;\n const hasPerspectiveInFrom = fromVarsRecord && 'perspective' in fromVarsRecord;\n const animatePerspective = hasPerspectiveInTo && hasPerspectiveInFrom;\n\n for (const target of targets) {\n // Handle perspective as immediate 3D context when NOT animating it\n // (i.e., when only one side is specified, not both from and to)\n // This prevents interpolating from 0 which causes extreme visual distortion\n if (!animatePerspective && (hasPerspectiveInTo || hasPerspectiveInFrom) && target instanceof Element) {\n // Use 'to' value if available, otherwise 'from' value\n const rawValue = hasPerspectiveInTo ? varsRecord.perspective : fromVarsRecord!.perspective;\n const perspectiveValue = typeof rawValue === 'number'\n ? rawValue\n : parseFloat(String(rawValue)) || 0;\n\n if (perspectiveValue > 0) {\n setTransformValue(target, 'perspective', perspectiveValue, 'px');\n // Apply immediately to element\n (target as HTMLElement).style.transform = buildTransformString(target);\n }\n }\n\n // For each property\n for (const prop of propsToAnimate) {\n // Skip perspective if it's being set as immediate context (not animated)\n if (prop === 'perspective' && !animatePerspective) continue;\n\n const targetValue = varsRecord[prop];\n\n // Parse the property\n const parsed = parseProperty(\n target,\n prop,\n targetValue,\n fromVarsRecord ? fromVarsRecord[prop] : undefined\n );\n\n // Create PropTween from pool\n const propTween = this.propTweenPool.acquire();\n const swapForFrom = isFrom && !fromVars;\n\n switch (parsed.type) {\n case 'color': {\n const startColor = swapForFrom ? parsed.endColor : parsed.startColor;\n const endColor = swapForFrom ? parsed.startColor : parsed.endColor;\n propTween.initColor(target, prop, startColor, endColor);\n break;\n }\n case 'filter': {\n const startFilters = swapForFrom ? parsed.endFilters : parsed.startFilters;\n const endFilters = swapForFrom ? parsed.startFilters : parsed.endFilters;\n propTween.initFilter(target, prop, startFilters, endFilters);\n break;\n }\n case 'scalar': {\n const startValue = swapForFrom ? parsed.endValue : parsed.startValue;\n const endValue = swapForFrom ? parsed.startValue : parsed.endValue;\n propTween.init(target, prop, startValue, endValue, parsed.unit);\n break;\n }\n case 'drawSVG': {\n const startDraw = swapForFrom ? parsed.endDraw : parsed.startDraw;\n const endDraw = swapForFrom ? parsed.startDraw : parsed.endDraw;\n propTween.initDrawSVG(target, prop, startDraw, endDraw, parsed.length);\n break;\n }\n case 'path': {\n const startProgress = swapForFrom ? parsed.endProgress : parsed.startProgress;\n const endProgress = swapForFrom ? parsed.startProgress : parsed.endProgress;\n propTween.initPath(\n target, prop, parsed.pathData, parsed.pathLength,\n startProgress, endProgress, parsed.rotate, parsed.alignOffset, parsed.pathOffset\n );\n break;\n }\n }\n\n // Add to animation's linked list\n animation.addPropTween(propTween);\n }\n }\n }\n\n /**\n * Register an animation with the engine\n */\n registerAnimation(animation: Animation): number {\n const id = this.nextAnimationId++;\n const index = this.animations.length;\n\n this.animations.push(animation);\n this.animationIdToIndex.set(id, index);\n this.totalCreated++;\n\n // Provide unregister callback to avoid circular dependency\n animation.setUnregisterCallback(() => this.unregisterAnimation(id));\n\n return id;\n }\n\n /**\n * Unregister an animation from the engine\n * Uses swap-and-pop for O(1) removal\n */\n unregisterAnimation(id: number): void {\n const index = this.animationIdToIndex.get(id);\n if (index === undefined) return;\n\n const animation = this.animations[index];\n const lastIndex = this.animations.length - 1;\n\n // Swap with last element if not already last\n if (index !== lastIndex) {\n const lastAnimation = this.animations[lastIndex];\n this.animations[index] = lastAnimation;\n // Update index of moved animation\n this.animationIdToIndex.set(lastAnimation.getId(), index);\n }\n\n // Pop the last element\n this.animations.pop();\n this.animationIdToIndex.delete(id);\n\n // Return animation to pool\n this.animationPool.release(animation);\n }\n\n /**\n * Get an animation by ID\n */\n getAnimation(id: number): Animation | undefined {\n const index = this.animationIdToIndex.get(id);\n return index !== undefined ? this.animations[index] : undefined;\n }\n\n /**\n * Kill all animations\n */\n killAll(): void {\n // Copy array since kill() triggers unregister callbacks that modify it\n const animationsToKill = [...this.animations];\n for (let i = 0, len = animationsToKill.length; i < len; i++) {\n animationsToKill[i].kill();\n }\n // Ensure clean state\n this.animations.length = 0;\n this.animationIdToIndex.clear();\n }\n\n /**\n * Kill all animations targeting specific elements\n * Used by Motion.reset() to stop animations before clearing styles\n */\n killAnimationsForTargets(targets: Element[]): void {\n if (targets.length === 0) return;\n\n const targetSet = new Set(targets);\n const animationsToKill: Animation[] = [];\n\n // Find animations that target any of the specified elements\n for (let i = 0, len = this.animations.length; i < len; i++) {\n const animation = this.animations[i];\n const animTargets = animation.getTargets();\n\n for (const target of animTargets) {\n if (target instanceof Element && targetSet.has(target)) {\n animationsToKill.push(animation);\n break; // Found a match, no need to check other targets\n }\n }\n }\n\n // Kill matching animations\n for (const animation of animationsToKill) {\n animation.kill();\n }\n }\n\n /**\n * Get active animation count\n */\n getActiveCount(): number {\n let count = 0;\n for (let i = 0, len = this.animations.length; i < len; i++) {\n if (this.animations[i].isActive()) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Get total animations created\n */\n getTotalCreated(): number {\n return this.totalCreated;\n }\n\n /**\n * Get or create a named timeline\n */\n getTimeline(name: string): Timeline {\n let timeline = this.timelines.get(name);\n if (!timeline) {\n timeline = new Timeline(name);\n // Constructor may have already registered via callback, but ensure it's set\n if (!this.timelines.has(name)) {\n this.timelines.set(name, timeline);\n timeline.setUnregisterCallback(() => this.removeTimeline(name));\n }\n }\n return timeline;\n }\n\n /**\n * Check if timeline exists\n */\n hasTimeline(name: string): boolean {\n return this.timelines.has(name);\n }\n\n /**\n * Register a timeline\n */\n registerTimeline(name: string, timeline: Timeline): void {\n this.timelines.set(name, timeline);\n // Set cleanup callback so Timeline can unregister without importing Engine\n timeline.setUnregisterCallback(() => this.removeTimeline(name));\n }\n\n /**\n * Get timeline count\n */\n getTimelineCount(): number {\n return this.timelines.size;\n }\n\n /**\n * Get all timeline names\n */\n getTimelineNames(): string[] {\n return Array.from(this.timelines.keys());\n }\n\n /**\n * Remove a timeline from registry (called after kill)\n */\n removeTimeline(name: string): void {\n this.timelines.delete(name);\n }\n\n /**\n * Kill all timelines and their triggers\n */\n killAllTimelines(): void {\n for (const timeline of this.timelines.values()) {\n timeline.kill();\n }\n this.timelines.clear();\n }\n\n /**\n * Kill everything - all animations, all timelines, and cleanup resources\n */\n killEverything(): void {\n this.killAllTimelines();\n this.killAll();\n // Cleanup path parser resources (hidden SVG element, caches)\n cleanupPathParser();\n }\n\n /**\n * Get animation pool\n */\n getAnimationPool(): AnimationPool {\n return this.animationPool;\n }\n\n /**\n * Get PropTween pool\n */\n getPropTweenPool(): PropTweenPool {\n return this.propTweenPool;\n }\n\n /**\n * Get pool statistics\n */\n getPoolStats(): { animations: number; propTweens: number } {\n return {\n animations: this.animationPool.getPoolSize(),\n propTweens: this.propTweenPool.getPoolSize(),\n };\n }\n\n /**\n * Clear all pools (for debugging)\n */\n clearPools(): void {\n this.animationPool.clear();\n this.propTweenPool.clear();\n }\n\n /**\n * Get memory estimate in bytes\n */\n getMemoryEstimate(): number {\n // Rough estimate: each animation ~500 bytes, each PropTween ~100 bytes\n const activeAnimations = this.animations.length;\n const pooledAnimations = this.animationPool.getPoolSize();\n const pooledPropTweens = this.propTweenPool.getPoolSize();\n\n return (activeAnimations + pooledAnimations) * 500 + pooledPropTweens * 100;\n }\n\n /**\n * Get ticker reference\n */\n getTicker(): Ticker {\n return this.ticker;\n }\n}\n\n/**\n * Convert kebab-case CSS property names to camelCase in an animation vars object.\n * e.g. { 'background-size': '160%' } → { backgroundSize: '160%' }\n *\n * This matches GSAP's behavior of accepting both formats and ensures\n * element.style[prop] assignment works (CSSStyleDeclaration requires camelCase).\n * Keys that are already camelCase or CSS variables (--*) pass through unchanged.\n */\nfunction normalizePropertyKeys(vars: Record<string, PropertyValue>): Record<string, PropertyValue> {\n let needsNormalization = false;\n for (const key of Object.keys(vars)) {\n // Skip CSS variables (--custom-prop) — they must stay as-is\n if (key.includes('-') && !key.startsWith('--')) {\n needsNormalization = true;\n break;\n }\n }\n if (!needsNormalization) return vars;\n\n const normalized: Record<string, PropertyValue> = {};\n for (const [key, value] of Object.entries(vars)) {\n if (key.includes('-') && !key.startsWith('--')) {\n // kebab-case → camelCase\n const camelKey = key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());\n normalized[camelKey] = value;\n } else {\n normalized[key] = value;\n }\n }\n return normalized;\n}\n",
|
|
47
|
+
"/**\n * Engine Singleton - Central animation registry and coordinator\n *\n * Manages:\n * - Global animation registry\n * - Named timeline registry\n * - Object pool coordination\n * - Ticker integration\n * - Render queue routing\n */\n\nimport { Ticker } from './Ticker';\nimport { Animation } from './Animation';\nimport { Timeline } from './Timeline';\nimport { AnimationBuilder } from './AnimationBuilder';\nimport { AnimationPool } from '../memory/AnimationPool';\nimport { PropTweenPool } from '../memory/PropTweenPool';\nimport { parseProperty, isTransformProp, parseValue, getDefaultUnit, type PropertyValue } from '../utils/PropertyParser';\nimport { setTransformValue, buildTransformString } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { cleanupPathParser } from '../utils/PathParser';\nimport type { AnimationVars, StaggerVars, AnimationTarget } from '../types';\nimport type { InternalAnimationConfig } from './Animation';\n\nexport class Engine {\n private static instance: Engine;\n\n // Animation registry - array for fast iteration, map for O(1) ID lookup\n private animations: Animation[] = [];\n private animationIdToIndex: Map<number, number> = new Map();\n private nextAnimationId: number = 1;\n private totalCreated: number = 0;\n\n // Pre-allocated snapshot buffer for safe mid-iteration mutation handling\n private _animationSnapshot: Animation[] = [];\n\n // Timeline registry (named timelines)\n private timelines: Map<string, Timeline> = new Map();\n\n // Context capture — when active, timeline names are recorded into the target set\n private _captureTarget: Set<string> | null = null;\n\n // Ticker reference\n private ticker: Ticker;\n\n // Pools\n private animationPool: AnimationPool;\n private propTweenPool: PropTweenPool;\n\n private constructor() {\n this.ticker = Ticker.getInstance();\n this.animationPool = AnimationPool;\n this.propTweenPool = PropTweenPool;\n\n // Register ticker callback\n this.ticker.add(this.update, 0);\n }\n\n /**\n * Get the singleton Engine instance\n */\n static getInstance(): Engine {\n if (!Engine.instance) {\n Engine.instance = new Engine();\n // Set up Timeline registration callback (breaks circular dependency)\n Timeline.setEngineRegisterCallback((name: string, timeline: Timeline) => {\n const engine = Engine.instance!;\n if (!engine.hasTimeline(name)) {\n engine.registerTimeline(name, timeline);\n }\n });\n // Set up AnimationBuilder callback to break circular dependency\n AnimationBuilder.setCreateAnimationCallback(\n (targets, vars, duration, delay, ease, isFrom, fromVars, config) => {\n return Engine.instance!.createAnimation(targets, vars, duration, delay, ease, isFrom, fromVars, config);\n }\n );\n }\n return Engine.instance;\n }\n\n /**\n * Main update loop called by Ticker\n */\n private update = (deltaTime: number): void => {\n // Snapshot the live array before iteration so that onComplete callbacks\n // which call kill() on other animations (triggering swap-and-pop on\n // this.animations) cannot corrupt the current loop.\n const snapshot = this._animationSnapshot;\n const liveLen = this.animations.length;\n snapshot.length = liveLen;\n for (let i = 0; i < liveLen; i++) {\n snapshot[i] = this.animations[i];\n }\n\n const toRemove: number[] = [];\n\n for (let i = 0, len = snapshot.length; i < len; i++) {\n const animation = snapshot[i];\n\n // Skip animations that are children of a Timeline (timeline controls them)\n if (animation.isTimelineChild()) continue;\n\n if (animation.isActive()) {\n animation.update(deltaTime);\n\n // Check if animation completed and should be pooled\n // Timeline children are NOT auto-pooled - they persist until timeline.kill()\n if (!animation.isActive() && !animation.isTimelineChild()) {\n toRemove.push(animation.getId());\n }\n }\n }\n\n // Remove completed animations (return to pool for performance)\n for (const id of toRemove) {\n this.unregisterAnimation(id);\n }\n\n // Update all active timelines\n for (const timeline of this.timelines.values()) {\n if (timeline.isActive()) {\n timeline.update(deltaTime);\n }\n }\n };\n\n /**\n * Create a new animation (called by AnimationBuilder)\n * Returns either a single Animation or an array of Animations (for stagger)\n */\n createAnimation(\n targets: AnimationTarget[],\n vars: AnimationVars,\n duration: number,\n delay: number,\n ease: string,\n isFrom: boolean,\n fromVars?: AnimationVars,\n config?: InternalAnimationConfig\n ): Animation | Animation[] {\n // Check if stagger is specified (from config, not vars)\n const staggerConfig = config?.stagger;\n\n if (staggerConfig && targets.length > 1) {\n // Create staggered animations\n return this.createStaggeredAnimations(\n targets,\n vars,\n duration,\n delay,\n ease,\n isFrom,\n fromVars,\n staggerConfig,\n config\n );\n }\n\n // Create single animation for all targets\n // Acquire animation from pool\n const animation = this.animationPool.acquire();\n\n // Generate ID and register\n const id = this.registerAnimation(animation);\n\n // Initialize animation with config\n animation.init(id, targets, duration, delay, ease, config);\n\n // Parse properties and create PropTweens for each target\n this.setupPropertyTweens(animation, targets, vars, isFrom, fromVars);\n\n // If duration is 0, immediately render at final values (instant set)\n if (duration === 0) {\n animation.render(1);\n } else if (isFrom && !config?._skipInitialRender) {\n // If this is a \"from\" animation (and not instant), immediately render starting values.\n // Timeline children skip this — Timeline captures initial values first, then renders FROM.\n animation.render(0);\n }\n\n return animation;\n }\n\n /**\n * Create staggered animations - one animation per target\n */\n private createStaggeredAnimations(\n targets: AnimationTarget[],\n vars: AnimationVars,\n duration: number,\n baseDelay: number,\n ease: string,\n isFrom: boolean,\n fromVars: AnimationVars | undefined,\n staggerConfig: number | StaggerVars,\n config?: InternalAnimationConfig\n ): Animation[] {\n // Calculate stagger delays (gracefully handle if StaggerResolver not loaded)\n if (!SDKRegistry.stagger) {\n // StaggerResolver not loaded - create animations without stagger\n const animation = this.animationPool.acquire();\n const id = this.registerAnimation(animation);\n animation.init(id, targets, duration, baseDelay, ease, config);\n this.setupPropertyTweens(animation, targets, vars, isFrom, fromVars);\n if (duration === 0) {\n animation.render(1);\n } else if (isFrom && !config?._skipInitialRender) {\n animation.render(0);\n }\n return [animation];\n }\n const staggerDelays = SDKRegistry.stagger.resolve(targets, staggerConfig);\n\n // Create one animation per target\n const animations: Animation[] = [];\n\n for (let i = 0; i < targets.length; i++) {\n const target = targets[i];\n const staggerDelay = staggerDelays[i];\n\n // Acquire animation from pool\n const animation = this.animationPool.acquire();\n\n // Generate ID and register\n const id = this.registerAnimation(animation);\n\n // Initialize animation with stagger delay added to base delay\n animation.init(id, [target], duration, baseDelay + staggerDelay, ease, config);\n\n // Parse properties and create PropTweens for this single target\n this.setupPropertyTweens(animation, [target], vars, isFrom, fromVars);\n\n // If duration is 0, immediately render at final values (instant set)\n if (duration === 0) {\n animation.render(1);\n } else if (isFrom && !config?._skipInitialRender) {\n // Timeline children skip this — Timeline captures initial values first, then renders FROM.\n animation.render(0);\n }\n\n animations.push(animation);\n }\n\n return animations;\n }\n\n /**\n * Set up property tweens for an animation\n */\n private setupPropertyTweens(\n animation: Animation,\n targets: AnimationTarget[],\n vars: AnimationVars,\n isFrom: boolean,\n fromVars?: AnimationVars\n ): void {\n // Expand `transform` shorthand (e.g. `transform: 'perspective(1000px)'`) into\n // individual transform properties (e.g. `perspective: '1000px'`) before any parsing.\n // Then normalize kebab-case CSS property names to camelCase.\n const varsRecord = normalizePropertyKeys(expandTransformShorthand(vars as Record<string, PropertyValue>));\n const fromVarsRecord = fromVars ? normalizePropertyKeys(expandTransformShorthand(fromVars as Record<string, PropertyValue>)) : undefined;\n\n // Collect all properties from both vars (to) and fromVars (from)\n // This ensures properties only in 'from' are also animated\n const toKeys = Object.keys(varsRecord);\n const fromKeys = fromVarsRecord ? Object.keys(fromVarsRecord) : [];\n const propsToAnimate = [...new Set([...toKeys, ...fromKeys])];\n\n // Check if perspective should be animated (both from and to have explicit values)\n // or set as immediate context (only one side specified)\n const hasPerspectiveInTo = 'perspective' in varsRecord;\n const hasPerspectiveInFrom = fromVarsRecord && 'perspective' in fromVarsRecord;\n const animatePerspective = hasPerspectiveInTo && hasPerspectiveInFrom;\n\n for (const target of targets) {\n // Handle perspective as immediate 3D context when NOT animating it\n // (i.e., when only one side is specified, not both from and to)\n // This prevents interpolating from 0 which causes extreme visual distortion\n if (!animatePerspective && (hasPerspectiveInTo || hasPerspectiveInFrom) && target instanceof Element) {\n // Use 'to' value if available, otherwise 'from' value\n const rawValue = hasPerspectiveInTo ? varsRecord.perspective : fromVarsRecord!.perspective;\n const perspectiveValue = typeof rawValue === 'number'\n ? rawValue\n : parseFloat(String(rawValue)) || 0;\n\n if (perspectiveValue > 0) {\n setTransformValue(target, 'perspective', perspectiveValue, 'px');\n // Apply immediately to element\n (target as HTMLElement).style.transform = buildTransformString(target);\n }\n }\n\n // For each property\n for (const prop of propsToAnimate) {\n // Skip perspective if it's being set as immediate context (not animated)\n if (prop === 'perspective' && !animatePerspective) continue;\n\n const targetValue = varsRecord[prop];\n\n // Parse the property\n const parsed = parseProperty(\n target,\n prop,\n targetValue,\n fromVarsRecord ? fromVarsRecord[prop] : undefined\n );\n\n // Create PropTween from pool\n const propTween = this.propTweenPool.acquire();\n const swapForFrom = isFrom && !fromVars;\n\n switch (parsed.type) {\n case 'color': {\n const startColor = swapForFrom ? parsed.endColor : parsed.startColor;\n const endColor = swapForFrom ? parsed.startColor : parsed.endColor;\n propTween.initColor(target, prop, startColor, endColor);\n break;\n }\n case 'filter': {\n const startFilters = swapForFrom ? parsed.endFilters : parsed.startFilters;\n const endFilters = swapForFrom ? parsed.startFilters : parsed.endFilters;\n propTween.initFilter(target, prop, startFilters, endFilters);\n break;\n }\n case 'clipPath': {\n const startClipPath = swapForFrom ? parsed.endClipPath : parsed.startClipPath;\n const endClipPath = swapForFrom ? parsed.startClipPath : parsed.endClipPath;\n propTween.initClipPath(target, prop, startClipPath, endClipPath);\n break;\n }\n case 'scalar': {\n const startValue = swapForFrom ? parsed.endValue : parsed.startValue;\n const endValue = swapForFrom ? parsed.startValue : parsed.endValue;\n propTween.init(target, prop, startValue, endValue, parsed.unit);\n break;\n }\n case 'drawSVG': {\n const startDraw = swapForFrom ? parsed.endDraw : parsed.startDraw;\n const endDraw = swapForFrom ? parsed.startDraw : parsed.endDraw;\n propTween.initDrawSVG(target, prop, startDraw, endDraw, parsed.length);\n break;\n }\n case 'path': {\n const startProgress = swapForFrom ? parsed.endProgress : parsed.startProgress;\n const endProgress = swapForFrom ? parsed.startProgress : parsed.endProgress;\n propTween.initPath(\n target, prop, parsed.pathData, parsed.pathLength,\n startProgress, endProgress, parsed.rotate, parsed.alignOffset, parsed.pathOffset\n );\n break;\n }\n }\n\n // Add to animation's linked list\n animation.addPropTween(propTween);\n }\n }\n }\n\n /**\n * Register an animation with the engine\n */\n registerAnimation(animation: Animation): number {\n const id = this.nextAnimationId++;\n const index = this.animations.length;\n\n this.animations.push(animation);\n this.animationIdToIndex.set(id, index);\n this.totalCreated++;\n\n // Provide unregister callback to avoid circular dependency\n animation.setUnregisterCallback(() => this.unregisterAnimation(id));\n\n return id;\n }\n\n /**\n * Unregister an animation from the engine\n * Uses swap-and-pop for O(1) removal\n */\n unregisterAnimation(id: number): void {\n const index = this.animationIdToIndex.get(id);\n if (index === undefined) return;\n\n const animation = this.animations[index];\n const lastIndex = this.animations.length - 1;\n\n // Swap with last element if not already last\n if (index !== lastIndex) {\n const lastAnimation = this.animations[lastIndex];\n this.animations[index] = lastAnimation;\n // Update index of moved animation\n this.animationIdToIndex.set(lastAnimation.getId(), index);\n }\n\n // Pop the last element\n this.animations.pop();\n this.animationIdToIndex.delete(id);\n\n // Return animation to pool\n this.animationPool.release(animation);\n }\n\n /**\n * Get an animation by ID\n */\n getAnimation(id: number): Animation | undefined {\n const index = this.animationIdToIndex.get(id);\n return index !== undefined ? this.animations[index] : undefined;\n }\n\n /**\n * Kill all animations\n */\n killAll(): void {\n // Copy array since kill() triggers unregister callbacks that modify it\n const animationsToKill = [...this.animations];\n for (let i = 0, len = animationsToKill.length; i < len; i++) {\n animationsToKill[i].kill();\n }\n // Ensure clean state\n this.animations.length = 0;\n this.animationIdToIndex.clear();\n }\n\n /**\n * Kill all animations targeting specific elements\n * Used by Motion.reset() to stop animations before clearing styles\n */\n killAnimationsForTargets(targets: Element[]): void {\n if (targets.length === 0) return;\n\n const targetSet = new Set(targets);\n const animationsToKill: Animation[] = [];\n\n // Find animations that target any of the specified elements\n for (let i = 0, len = this.animations.length; i < len; i++) {\n const animation = this.animations[i];\n const animTargets = animation.getTargets();\n\n for (const target of animTargets) {\n if (target instanceof Element && targetSet.has(target)) {\n animationsToKill.push(animation);\n break; // Found a match, no need to check other targets\n }\n }\n }\n\n // Kill matching animations\n for (const animation of animationsToKill) {\n animation.kill();\n }\n }\n\n /**\n * Get active animation count\n */\n getActiveCount(): number {\n let count = 0;\n for (let i = 0, len = this.animations.length; i < len; i++) {\n if (this.animations[i].isActive()) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Get total animations created\n */\n getTotalCreated(): number {\n return this.totalCreated;\n }\n\n /**\n * Get or create a named timeline.\n * When a context capture is active, the timeline name is recorded into\n * the capture set so that MotionContext can track ownership.\n */\n getTimeline(name: string): Timeline {\n // Record into active context capture (zero-cost null check when inactive)\n if (this._captureTarget) {\n this._captureTarget.add(name);\n }\n\n let timeline = this.timelines.get(name);\n if (!timeline) {\n timeline = new Timeline(name);\n // Constructor may have already registered via callback, but ensure it's set\n if (!this.timelines.has(name)) {\n this.timelines.set(name, timeline);\n timeline.setUnregisterCallback(() => this.removeTimeline(name));\n }\n }\n return timeline;\n }\n\n /**\n * Start capturing timeline names into the provided set.\n * Used by MotionContext to track which timelines belong to a context.\n * @internal\n */\n startCapture(target: Set<string>): void {\n this._captureTarget = target;\n }\n\n /**\n * Stop capturing timeline names.\n * @internal\n */\n stopCapture(): void {\n this._captureTarget = null;\n }\n\n /**\n * Check if timeline exists\n */\n hasTimeline(name: string): boolean {\n return this.timelines.has(name);\n }\n\n /**\n * Register a timeline\n */\n registerTimeline(name: string, timeline: Timeline): void {\n this.timelines.set(name, timeline);\n // Set cleanup callback so Timeline can unregister without importing Engine\n timeline.setUnregisterCallback(() => this.removeTimeline(name));\n }\n\n /**\n * Get timeline count\n */\n getTimelineCount(): number {\n return this.timelines.size;\n }\n\n /**\n * Get all timeline names\n */\n getTimelineNames(): string[] {\n return Array.from(this.timelines.keys());\n }\n\n /**\n * Remove a timeline from registry (called after kill)\n */\n removeTimeline(name: string): void {\n this.timelines.delete(name);\n }\n\n /**\n * Kill all timelines and their triggers\n */\n killAllTimelines(): void {\n for (const timeline of this.timelines.values()) {\n timeline.kill();\n }\n this.timelines.clear();\n }\n\n /**\n * Kill everything - all animations, all timelines, and cleanup resources\n */\n killEverything(): void {\n this.killAllTimelines();\n this.killAll();\n // Cleanup path parser resources (hidden SVG element, caches)\n cleanupPathParser();\n }\n\n /**\n * Get animation pool\n */\n getAnimationPool(): AnimationPool {\n return this.animationPool;\n }\n\n /**\n * Get PropTween pool\n */\n getPropTweenPool(): PropTweenPool {\n return this.propTweenPool;\n }\n\n /**\n * Get pool statistics\n */\n getPoolStats(): { animations: number; propTweens: number } {\n return {\n animations: this.animationPool.getPoolSize(),\n propTweens: this.propTweenPool.getPoolSize(),\n };\n }\n\n /**\n * Clear all pools (for debugging)\n */\n clearPools(): void {\n this.animationPool.clear();\n this.propTweenPool.clear();\n }\n\n /**\n * Get memory estimate in bytes\n */\n getMemoryEstimate(): number {\n // Rough estimate: each animation ~500 bytes, each PropTween ~100 bytes\n const activeAnimations = this.animations.length;\n const pooledAnimations = this.animationPool.getPoolSize();\n const pooledPropTweens = this.propTweenPool.getPoolSize();\n\n return (activeAnimations + pooledAnimations) * 500 + pooledPropTweens * 100;\n }\n\n /**\n * Get ticker reference\n */\n getTicker(): Ticker {\n return this.ticker;\n }\n}\n\n/**\n * Convert kebab-case CSS property names to camelCase in an animation vars object.\n * e.g. { 'background-size': '160%' } → { backgroundSize: '160%' }\n *\n * This matches GSAP's behavior of accepting both formats and ensures\n * element.style[prop] assignment works (CSSStyleDeclaration requires camelCase).\n * Keys that are already camelCase or CSS variables (--*) pass through unchanged.\n */\n/**\n * Expand a `transform` shorthand property into individual transform properties.\n *\n * GSAP accepts `transform: 'perspective(1000px)'` as a way to set CSS transform\n * functions. Our SDK uses individual property keys (e.g. `perspective`, `x`, `rotateX`).\n * This function decomposes the CSS transform string value into those individual keys\n * so the rest of the pipeline handles them correctly.\n *\n * Properties already present in `vars` are NOT overwritten — explicit keys win.\n *\n * @example\n * expandTransformShorthand({ transform: 'perspective(1000px)', rotateX: -20 })\n * // → { perspective: '1000px', rotateX: -20 }\n */\nfunction expandTransformShorthand(vars: Record<string, PropertyValue>): Record<string, PropertyValue> {\n if (!('transform' in vars)) return vars;\n const transformValue = vars.transform;\n if (typeof transformValue !== 'string') return vars;\n\n // Parse CSS transform functions from the string\n const regex = /([a-zA-Z0-9]+)\\(([^)]+)\\)/g;\n let match: RegExpExecArray | null;\n const expanded: Record<string, PropertyValue> = {};\n let foundAny = false;\n\n while ((match = regex.exec(transformValue)) !== null) {\n const fn = match[1];\n const rawArgs = match[2];\n // Parse comma/space-separated arguments\n const args = rawArgs.match(/[-+]?\\d*\\.?\\d+(?:e[-+]?\\d+)?[a-z%]*/gi) || [];\n\n switch (fn) {\n case 'perspective': {\n if (!('perspective' in vars)) {\n expanded.perspective = args[0] || '0px';\n foundAny = true;\n }\n break;\n }\n case 'translate': {\n if (args[0] && !('x' in vars)) { expanded.x = args[0]; foundAny = true; }\n if (args[1] && !('y' in vars)) { expanded.y = args[1]; foundAny = true; }\n break;\n }\n case 'translate3d': {\n if (args[0] && !('x' in vars)) { expanded.x = args[0]; foundAny = true; }\n if (args[1] && !('y' in vars)) { expanded.y = args[1]; foundAny = true; }\n if (args[2] && !('z' in vars)) { expanded.z = args[2]; foundAny = true; }\n break;\n }\n case 'translateX': {\n if (args[0] && !('x' in vars)) { expanded.x = args[0]; foundAny = true; }\n break;\n }\n case 'translateY': {\n if (args[0] && !('y' in vars)) { expanded.y = args[0]; foundAny = true; }\n break;\n }\n case 'translateZ': {\n if (args[0] && !('z' in vars)) { expanded.z = args[0]; foundAny = true; }\n break;\n }\n case 'rotate': {\n if (args[0] && !('rotate' in vars)) { expanded.rotate = args[0]; foundAny = true; }\n break;\n }\n case 'rotateX': {\n if (args[0] && !('rotateX' in vars)) { expanded.rotateX = args[0]; foundAny = true; }\n break;\n }\n case 'rotateY': {\n if (args[0] && !('rotateY' in vars)) { expanded.rotateY = args[0]; foundAny = true; }\n break;\n }\n case 'rotateZ': {\n if (args[0] && !('rotateZ' in vars)) { expanded.rotateZ = args[0]; foundAny = true; }\n break;\n }\n case 'scale': {\n if (args[0] && !('scale' in vars)) { expanded.scale = parseFloat(args[0]); foundAny = true; }\n break;\n }\n case 'scaleX': {\n if (args[0] && !('scaleX' in vars)) { expanded.scaleX = parseFloat(args[0]); foundAny = true; }\n break;\n }\n case 'scaleY': {\n if (args[0] && !('scaleY' in vars)) { expanded.scaleY = parseFloat(args[0]); foundAny = true; }\n break;\n }\n case 'scaleZ': {\n if (args[0] && !('scaleZ' in vars)) { expanded.scaleZ = parseFloat(args[0]); foundAny = true; }\n break;\n }\n case 'skew': {\n if (args[0] && !('skewX' in vars)) { expanded.skewX = args[0]; foundAny = true; }\n if (args[1] && !('skewY' in vars)) { expanded.skewY = args[1]; foundAny = true; }\n break;\n }\n case 'skewX': {\n if (args[0] && !('skewX' in vars)) { expanded.skewX = args[0]; foundAny = true; }\n break;\n }\n case 'skewY': {\n if (args[0] && !('skewY' in vars)) { expanded.skewY = args[0]; foundAny = true; }\n break;\n }\n default:\n break;\n }\n }\n\n if (!foundAny) return vars;\n\n // Build result: expanded transform properties + original keys (minus 'transform')\n const result: Record<string, PropertyValue> = { ...expanded };\n for (const [key, value] of Object.entries(vars)) {\n if (key === 'transform') continue;\n result[key] = value;\n }\n return result;\n}\n\nfunction normalizePropertyKeys(vars: Record<string, PropertyValue>): Record<string, PropertyValue> {\n let needsNormalization = false;\n for (const key of Object.keys(vars)) {\n // Skip CSS variables (--custom-prop) — they must stay as-is\n if (key.includes('-') && !key.startsWith('--')) {\n needsNormalization = true;\n break;\n }\n }\n if (!needsNormalization) return vars;\n\n const normalized: Record<string, PropertyValue> = {};\n for (const [key, value] of Object.entries(vars)) {\n if (key.includes('-') && !key.startsWith('--')) {\n // kebab-case → camelCase\n const camelKey = key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());\n normalized[camelKey] = value;\n } else {\n normalized[key] = value;\n }\n }\n return normalized;\n}\n",
|
|
48
|
+
"/**\n * MotionContext — Scoped animation lifecycle management\n *\n * Tracks all timelines created during a callback execution, enabling\n * clean teardown and re-initialization when DOM content changes.\n *\n * Solves the universal \"dynamic content\" problem:\n * - WordPress AJAX filters (Bricks, WooCommerce, Jetpack)\n * - SPA navigation (React, Astro view transitions, Next.js)\n * - Infinite scroll / pagination\n * - Any scenario where DOM elements are replaced without a full page reload\n *\n * @example WordPress (generated code)\n * const ctx = Motion.context(() => {\n * Motion('hero', '.title', { from: { opacity: 0 }, to: { opacity: 1 } }).onPageLoad()\n * Motion('cards', '.card', { from: { y: 100 }, to: { y: 0 } }).onScroll({ scrub: true })\n * })\n * // After AJAX filter replaces content:\n * ctx.refresh()\n *\n * @example React\n * useEffect(() => {\n * const ctx = Motion.context(() => {\n * Motion('fade', '.box', { from: { opacity: 0 }, to: { opacity: 1 } }).onScroll({ scrub: true })\n * })\n * return () => ctx.revert()\n * }, [])\n *\n * @example Astro (view transitions)\n * document.addEventListener('astro:page-load', () => {\n * window._ctx?.revert()\n * window._ctx = Motion.context(() => { ... })\n * })\n */\n\nimport { Engine } from './Engine';\nimport { SDKRegistry } from '../registries/SDKRegistry';\n\nexport class MotionContext {\n /** The init function that creates animations — re-executed on refresh() */\n private _fn: () => void;\n\n /** Timeline names created within this context */\n private _timelineNames: Set<string> = new Set();\n\n /** Whether this context has been reverted (prevents double-revert) */\n private _reverted: boolean = false;\n\n constructor(fn: () => void) {\n this._fn = fn;\n this._execute();\n }\n\n /**\n * Kill all timelines created in this context, restore elements to their\n * initial CSS state, and clean up DOM artifacts (ScrollTrigger spacers/markers).\n *\n * After revert(), the context is empty and can be refreshed or discarded.\n */\n revert(): void {\n if (this._reverted) return;\n\n const engine = Engine.getInstance();\n\n for (const name of this._timelineNames) {\n if (engine.hasTimeline(name)) {\n engine.getTimeline(name).kill();\n }\n }\n\n // Clean up ScrollTrigger DOM artifacts (spacers, markers)\n if (typeof document !== 'undefined') {\n document.querySelectorAll('[data-scrolltrigger-spacer]').forEach(el => el.remove());\n document.querySelectorAll('[data-scrolltrigger-markers]').forEach(el => el.remove());\n }\n\n this._timelineNames.clear();\n this._reverted = true;\n }\n\n /**\n * Revert all animations and re-execute the init function.\n * Selectors are re-resolved against the current DOM, so new/replaced\n * elements are picked up automatically.\n *\n * This is the primary method for handling dynamic content changes.\n */\n refresh(): void {\n this.revert();\n this._reverted = false;\n this._execute();\n }\n\n /**\n * Add more animations to this context without affecting existing ones.\n * Useful for lazy-loaded content or progressive enhancement.\n *\n * @example\n * ctx.add(() => {\n * Motion('lazy-section', '.lazy-card', { ... }).onScroll({ scrub: true })\n * })\n */\n add(fn: () => void): void {\n this._reverted = false;\n this._execute(fn);\n }\n\n /**\n * Get the timeline names tracked by this context (for debugging/testing).\n */\n getTimelineNames(): string[] {\n return Array.from(this._timelineNames);\n }\n\n /**\n * Execute a function while capturing timeline names into this context.\n * Uses Engine's capture mechanism for zero-overhead tracking.\n */\n private _execute(fn?: () => void): void {\n const engine = Engine.getInstance();\n engine.startCapture(this._timelineNames);\n try {\n (fn || this._fn)();\n } finally {\n engine.stopCapture();\n }\n }\n}\n",
|
|
46
49
|
"/**\n * Motion.utils — GSAP-compatible utility functions\n *\n * Drop-in replacements for common gsap.utils.* helpers.\n * Enables migration from GSAP custom code to the Motion SDK.\n *\n * @example Migration from GSAP\n * // Before: gsap.utils.toArray(\".section\").length\n * // After: Motion.utils.toArray(\".section\").length\n */\n\n/**\n * Convert a selector, NodeList, or value into a flat Array of Elements.\n * Drop-in replacement for gsap.utils.toArray().\n *\n * @param target - CSS selector string, NodeList, HTMLCollection, Element, or array\n * @param scope - Optional parent element to scope selector queries\n * @returns Flat array of matched elements\n *\n * @example\n * Motion.utils.toArray(\".h-scroll-section\") // all matching elements\n * Motion.utils.toArray(\".item\", containerEl) // scoped to container\n * Motion.utils.toArray(document.querySelectorAll(\"p\")) // NodeList → Array\n */\nexport function toArray<T extends Element = Element>(\n target: string | NodeList | HTMLCollection | Element | T[] | ArrayLike<T>,\n scope?: Element | Document\n): T[] {\n if (typeof target === 'string') {\n const root = scope || (typeof document !== 'undefined' ? document : null);\n if (!root) return [];\n try {\n return Array.from(root.querySelectorAll(target)) as T[];\n } catch {\n return [];\n }\n }\n\n if (typeof Element !== 'undefined' && target instanceof Element) {\n return [target as unknown as T];\n }\n\n if (\n (typeof NodeList !== 'undefined' && target instanceof NodeList) ||\n (typeof HTMLCollection !== 'undefined' && target instanceof HTMLCollection)\n ) {\n return Array.from(target) as T[];\n }\n\n if (Array.isArray(target)) {\n return target;\n }\n\n // ArrayLike (has .length)\n if (target && typeof target === 'object' && 'length' in target) {\n return Array.from(target) as T[];\n }\n\n return [];\n}\n\n/**\n * Clamp a value between min and max.\n * Drop-in replacement for gsap.utils.clamp().\n *\n * Can be called with 3 args or curried with 2:\n * - `clamp(0, 100, 150)` → `100`\n * - `const c = clamp(0, 1); c(1.5)` → `1`\n *\n * @param min - Minimum allowed value\n * @param max - Maximum allowed value\n * @param value - Value to clamp (optional — returns curried function if omitted)\n */\nexport function clamp(min: number, max: number, value: number): number;\nexport function clamp(min: number, max: number): (value: number) => number;\nexport function clamp(min: number, max: number, value?: number): number | ((value: number) => number) {\n if (value === undefined) {\n return (v: number) => Math.min(max, Math.max(min, v));\n }\n return Math.min(max, Math.max(min, value));\n}\n\n/**\n * Generate a random number between min and max (inclusive).\n * Drop-in replacement for gsap.utils.random().\n *\n * @param min - Minimum value\n * @param max - Maximum value\n * @param snapIncrement - Optional increment to snap to (e.g., 5 → multiples of 5)\n * @returns Random number in the given range\n *\n * @example\n * Motion.utils.random(0, 100) // 0–100 (float)\n * Motion.utils.random(0, 100, 10) // 0, 10, 20, ... 100\n * Motion.utils.random(-1, 1) // -1 to 1\n */\nexport function random(min: number, max: number, snapIncrement?: number): number {\n const raw = min + Math.random() * (max - min);\n if (snapIncrement && snapIncrement > 0) {\n return Math.round(raw / snapIncrement) * snapIncrement;\n }\n return raw;\n}\n\n/**\n * Snap a number to the nearest increment.\n * Drop-in replacement for gsap.utils.snap().\n *\n * @param snapTo - Increment value or array of values to snap to\n * @param value - Value to snap (optional — returns curried function if omitted)\n *\n * @example\n * Motion.utils.snap(5, 13) // 15\n * Motion.utils.snap([0, 25, 50, 100], 30) // 25\n * const s = Motion.utils.snap(10); s(27) // 30\n */\nexport function snap(snapTo: number | number[], value: number): number;\nexport function snap(snapTo: number | number[]): (value: number) => number;\nexport function snap(snapTo: number | number[], value?: number): number | ((value: number) => number) {\n const snapFn = (v: number): number => {\n if (typeof snapTo === 'number') {\n if (snapTo <= 0) return v;\n return Math.round(v / snapTo) * snapTo;\n }\n\n // Array: find nearest value\n const sorted = snapTo;\n let closest = sorted[0];\n let minDist = Math.abs(v - closest);\n for (let i = 1; i < sorted.length; i++) {\n const dist = Math.abs(v - sorted[i]);\n if (dist < minDist) {\n minDist = dist;\n closest = sorted[i];\n }\n }\n return closest;\n };\n\n if (value === undefined) {\n return snapFn;\n }\n return snapFn(value);\n}\n\n/**\n * Linear interpolation between two values.\n * Drop-in replacement for gsap.utils.interpolate() (simple 2-value case).\n *\n * @param start - Start value\n * @param end - End value\n * @param progress - Interpolation factor (0–1)\n * @returns Interpolated value\n *\n * @example\n * Motion.utils.interpolate(0, 100, 0.5) // 50\n */\nexport function interpolate(start: number, end: number, progress: number): number {\n return start + (end - start) * progress;\n}\n\n/**\n * Map a value from one range to another.\n * Drop-in replacement for gsap.utils.mapRange().\n *\n * @param inMin - Input range minimum\n * @param inMax - Input range maximum\n * @param outMin - Output range minimum\n * @param outMax - Output range maximum\n * @param value - Value to map (optional — returns curried function if omitted)\n *\n * @example\n * Motion.utils.mapRange(0, 100, 0, 1, 50) // 0.5\n * const map = Motion.utils.mapRange(0, 1, -100, 100); map(0.75) // 50\n */\nexport function mapRange(inMin: number, inMax: number, outMin: number, outMax: number, value: number): number;\nexport function mapRange(inMin: number, inMax: number, outMin: number, outMax: number): (value: number) => number;\nexport function mapRange(\n inMin: number, inMax: number, outMin: number, outMax: number, value?: number\n): number | ((value: number) => number) {\n const mapFn = (v: number): number => {\n const ratio = (v - inMin) / (inMax - inMin);\n return outMin + ratio * (outMax - outMin);\n };\n\n if (value === undefined) {\n return mapFn;\n }\n return mapFn(value);\n}\n\n/**\n * Normalize a value from a range to 0–1.\n * Drop-in replacement for gsap.utils.normalize().\n *\n * @param min - Range minimum\n * @param max - Range maximum\n * @param value - Value to normalize (optional — returns curried function if omitted)\n *\n * @example\n * Motion.utils.normalize(0, 100, 25) // 0.25\n */\nexport function normalize(min: number, max: number, value: number): number;\nexport function normalize(min: number, max: number): (value: number) => number;\nexport function normalize(min: number, max: number, value?: number): number | ((value: number) => number) {\n const normFn = (v: number): number => (v - min) / (max - min);\n\n if (value === undefined) {\n return normFn;\n }\n return normFn(value);\n}\n\n/**\n * Wrap a value within a range (modular arithmetic).\n * Drop-in replacement for gsap.utils.wrap().\n *\n * @param min - Range minimum\n * @param max - Range maximum\n * @param value - Value to wrap (optional — returns curried function if omitted)\n *\n * @example\n * Motion.utils.wrap(0, 100, 150) // 50\n * Motion.utils.wrap(0, 360, -90) // 270\n */\nexport function wrap(min: number, max: number, value: number): number;\nexport function wrap(min: number, max: number): (value: number) => number;\nexport function wrap(min: number, max: number, value?: number): number | ((value: number) => number) {\n const range = max - min;\n const wrapFn = (v: number): number => {\n const r = ((v - min) % range + range) % range;\n return r + min;\n };\n\n if (value === undefined) {\n return wrapFn;\n }\n return wrapFn(value);\n}\n\n/**\n * The complete utils namespace, attached to Motion.utils\n */\nexport const MotionUtils = {\n toArray,\n clamp,\n random,\n snap,\n interpolate,\n mapRange,\n normalize,\n wrap,\n} as const;\n",
|
|
47
|
-
"/**\n * Motion API - Unified entry point for animations\n *\n * All animations are named timelines. Config-based API for consistency.\n *\n * @example Single animation\n * Motion('fadeIn', '.box', {\n * from: { opacity: 0 },\n * to: { opacity: 1 },\n * duration: 0.5\n * }).play()\n *\n * @example Multi-animation timeline\n * Motion('hero', [\n * { target: '.title', from: { y: 50 }, to: { y: 0 }, duration: 1 },\n * { target: '.subtitle', from: { opacity: 0 }, to: { opacity: 1 }, position: 0.5 }\n * ]).play()\n *\n * @example With trigger\n * Motion('scroll', '.box', { from: { x: -100 }, to: { x: 0 } })\n * .onScroll({ scrub: true })\n *\n * @example Replay\n * Motion('fadeIn').play()\n */\n\nimport type { TargetInput, AnimationConfig, AnimationEntry, AnimationVars } from '../types';\nimport { Timeline } from './Timeline';\nimport { Engine } from './Engine';\nimport { resolveTargets } from '../utils/TargetResolver';\nimport { clearTransformCache } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { MotionUtils } from '../utils/MotionUtils';\n\n/**\n * Motion function overloads\n */\nfunction MotionFunction(name: string): Timeline;\nfunction MotionFunction(name: string, target: TargetInput, config: AnimationConfig): Timeline;\nfunction MotionFunction(name: string, animations: AnimationEntry[]): Timeline;\nfunction MotionFunction(\n name: string,\n targetOrAnimations?: TargetInput | AnimationEntry[],\n config?: AnimationConfig\n): Timeline {\n const engine = Engine.getInstance();\n const timeline = engine.getTimeline(name);\n\n // Warn when NEW entries are being added to an existing timeline that already has animations.\n // Calling Motion('name') with no second argument to retrieve a timeline should NOT warn.\n if (targetOrAnimations !== undefined && timeline.duration() > 0) {\n console.warn(\n `[Motion] Timeline \"${name}\" already has animations. ` +\n `New entries will be appended. Call Motion(\"${name}\").kill() first to rebuild from scratch.`\n );\n }\n\n // Check if second arg is an array of AnimationEntry objects\n // AnimationEntry has 'target' property, TargetInput arrays don't have objects with 'target'\n if (\n Array.isArray(targetOrAnimations) &&\n targetOrAnimations.length > 0 &&\n typeof targetOrAnimations[0] === 'object' &&\n targetOrAnimations[0] !== null &&\n 'target' in targetOrAnimations[0]\n ) {\n // Multi-animation: Motion('name', [{target, from, to}, ...])\n for (const entry of targetOrAnimations as AnimationEntry[]) {\n timeline._addEntry(entry.target, entry, entry.position);\n }\n } else if (targetOrAnimations !== undefined && config !== undefined) {\n // Single animation: Motion('name', '.target', {from, to})\n timeline._addEntry(targetOrAnimations as TargetInput, config);\n }\n // else: Just retrieve timeline: Motion('name')\n\n return timeline;\n}\n\n/**\n * Reset element(s) to their original state\n * Kills any active animations, clears transform cache and inline styles.\n */\nMotionFunction.reset = function(targets: TargetInput): void {\n Engine.getInstance(); // Ensure engine is initialized before any target resolution\n const resolved = resolveTargets(targets);\n\n const elementTargets: Element[] = [];\n for (const target of resolved) {\n if (target instanceof Element) {\n elementTargets.push(target);\n }\n }\n\n if (elementTargets.length > 0) {\n Engine.getInstance().killAnimationsForTargets(elementTargets);\n }\n\n for (const target of resolved) {\n if (!(target instanceof Element)) continue;\n\n if (SDKRegistry.textSplitter?.isSplit?.(target)) {\n SDKRegistry.textSplitter?.revert?.(target);\n }\n\n clearTransformCache(target);\n SDKRegistry.styleReset?.clearAnimationStylesAndUnregister?.(target);\n SDKRegistry.styleReset?.clearPinStylesAndUnregister?.(target);\n }\n};\n\n/**\n * Immediately set CSS properties on target(s) without animation.\n * Equivalent to GSAP's gsap.set() — useful for initializing element state.\n *\n * Values are applied synchronously via a zero-duration animation.\n * The internal timeline is discarded immediately after rendering (clearProps=false\n * so the transform cache remains in sync with the applied inline styles).\n *\n * @example\n * Motion.set('.box', { opacity: 0, x: -100 })\n */\nMotionFunction.set = function(target: TargetInput, vars: AnimationVars): void {\n Engine.getInstance(); // Ensure engine is initialized (sets AnimationBuilder callback)\n const tl = new Timeline();\n tl._addEntry(target, { to: vars, duration: 0 });\n // kill(false): skip prop restoration so applied values persist on the DOM\n // and the transform cache stays consistent with the new inline styles.\n tl.kill(false);\n};\n\n/**\n * Get an existing timeline by name (returns undefined if not found)\n */\nMotionFunction.get = function(name: string): Timeline | undefined {\n const engine = Engine.getInstance();\n return engine.hasTimeline(name) ? engine.getTimeline(name) : undefined;\n};\n\n/**\n * Check if a timeline exists\n */\nMotionFunction.has = function(name: string): boolean {\n return Engine.getInstance().hasTimeline(name);\n};\n\n/**\n * Get names of all registered timelines\n */\nMotionFunction.getNames = function(): string[] {\n return Engine.getInstance().getTimelineNames();\n};\n\n/**\n * Kill a timeline by name\n */\nMotionFunction.kill = function(name: string): void {\n const engine = Engine.getInstance();\n if (engine.hasTimeline(name)) {\n engine.getTimeline(name).kill();\n }\n};\n\n/**\n * Kill all timelines and animations\n */\nMotionFunction.killAll = function(): void {\n Engine.getInstance().killEverything();\n};\n\n/**\n * Refresh all scroll triggers\n */\nMotionFunction.refreshScrollTriggers = function(): void {\n SDKRegistry.triggerManager?.getInstance?.()?.refreshScrollTriggers();\n};\n\n/**\n * Clean up ScrollTrigger DOM artifacts\n */\nMotionFunction.cleanup = function(): void {\n if (typeof document === 'undefined') return;\n document.querySelectorAll('[data-scrolltrigger-spacer]').forEach(el => el.remove());\n document.querySelectorAll('[data-scrolltrigger-markers]').forEach(el => el.remove());\n};\n\n/**\n * Utility functions — drop-in replacements for gsap.utils.*\n *\n * @example Migration from GSAP\n * // Before: snap: 1 / (gsap.utils.toArray(\".section\").length - 1)\n * // After: snap: 1 / (Motion.utils.toArray(\".section\").length - 1)\n */\nMotionFunction.utils = MotionUtils;\n\nexport const Motion = MotionFunction;\n"
|
|
50
|
+
"/**\n * Motion API - Unified entry point for animations\n *\n * All animations are named timelines. Config-based API for consistency.\n *\n * @example Single animation\n * Motion('fadeIn', '.box', {\n * from: { opacity: 0 },\n * to: { opacity: 1 },\n * duration: 0.5\n * }).play()\n *\n * @example Multi-animation timeline\n * Motion('hero', [\n * { target: '.title', from: { y: 50 }, to: { y: 0 }, duration: 1 },\n * { target: '.subtitle', from: { opacity: 0 }, to: { opacity: 1 }, position: 0.5 }\n * ]).play()\n *\n * @example With trigger\n * Motion('scroll', '.box', { from: { x: -100 }, to: { x: 0 } })\n * .onScroll({ scrub: true })\n *\n * @example Replay\n * Motion('fadeIn').play()\n */\n\nimport type { TargetInput, AnimationConfig, AnimationEntry, AnimationVars } from '../types';\nimport { Timeline } from './Timeline';\nimport { Engine } from './Engine';\nimport { MotionContext } from './MotionContext';\nimport { resolveTargets } from '../utils/TargetResolver';\nimport { clearTransformCache } from '../render/TransformCache';\nimport { SDKRegistry } from '../registries/SDKRegistry';\nimport { MotionUtils } from '../utils/MotionUtils';\n\n/**\n * Motion function overloads\n */\nfunction MotionFunction(name: string): Timeline;\nfunction MotionFunction(name: string, target: TargetInput, config: AnimationConfig): Timeline;\nfunction MotionFunction(name: string, animations: AnimationEntry[]): Timeline;\nfunction MotionFunction(\n name: string,\n targetOrAnimations?: TargetInput | AnimationEntry[],\n config?: AnimationConfig\n): Timeline {\n const engine = Engine.getInstance();\n const timeline = engine.getTimeline(name);\n\n // Warn when NEW entries are being added to an existing timeline that already has animations.\n // Calling Motion('name') with no second argument to retrieve a timeline should NOT warn.\n if (targetOrAnimations !== undefined && timeline.duration() > 0) {\n console.warn(\n `[Motion] Timeline \"${name}\" already has animations. ` +\n `New entries will be appended. Call Motion(\"${name}\").kill() first to rebuild from scratch.`\n );\n }\n\n // Check if second arg is an array of AnimationEntry objects\n // AnimationEntry has 'target' property, TargetInput arrays don't have objects with 'target'\n if (\n Array.isArray(targetOrAnimations) &&\n targetOrAnimations.length > 0 &&\n typeof targetOrAnimations[0] === 'object' &&\n targetOrAnimations[0] !== null &&\n 'target' in targetOrAnimations[0]\n ) {\n // Multi-animation: Motion('name', [{target, from, to}, ...])\n for (const entry of targetOrAnimations as AnimationEntry[]) {\n timeline._addEntry(entry.target, entry, entry.position);\n }\n } else if (targetOrAnimations !== undefined && config !== undefined) {\n // Single animation: Motion('name', '.target', {from, to})\n timeline._addEntry(targetOrAnimations as TargetInput, config);\n }\n // else: Just retrieve timeline: Motion('name')\n\n return timeline;\n}\n\n/**\n * Reset element(s) to their original state\n * Kills any active animations, clears transform cache and inline styles.\n */\nMotionFunction.reset = function(targets: TargetInput): void {\n Engine.getInstance(); // Ensure engine is initialized before any target resolution\n const resolved = resolveTargets(targets);\n\n const elementTargets: Element[] = [];\n for (const target of resolved) {\n if (target instanceof Element) {\n elementTargets.push(target);\n }\n }\n\n if (elementTargets.length > 0) {\n Engine.getInstance().killAnimationsForTargets(elementTargets);\n }\n\n for (const target of resolved) {\n if (!(target instanceof Element)) continue;\n\n if (SDKRegistry.textSplitter?.isSplit?.(target)) {\n SDKRegistry.textSplitter?.revert?.(target);\n }\n\n clearTransformCache(target);\n SDKRegistry.styleReset?.clearAnimationStylesAndUnregister?.(target);\n SDKRegistry.styleReset?.clearPinStylesAndUnregister?.(target);\n }\n};\n\n/**\n * Immediately set CSS properties on target(s) without animation.\n * Equivalent to GSAP's gsap.set() — useful for initializing element state.\n *\n * Values are applied synchronously via a zero-duration animation.\n * The internal timeline is discarded immediately after rendering (clearProps=false\n * so the transform cache remains in sync with the applied inline styles).\n *\n * @example\n * Motion.set('.box', { opacity: 0, x: -100 })\n */\nMotionFunction.set = function(target: TargetInput, vars: AnimationVars): void {\n Engine.getInstance(); // Ensure engine is initialized (sets AnimationBuilder callback)\n const tl = new Timeline();\n tl._addEntry(target, { to: vars, duration: 0 });\n // kill(false): skip prop restoration so applied values persist on the DOM\n // and the transform cache stays consistent with the new inline styles.\n tl.kill(false);\n};\n\n/**\n * Get an existing timeline by name (returns undefined if not found)\n */\nMotionFunction.get = function(name: string): Timeline | undefined {\n const engine = Engine.getInstance();\n return engine.hasTimeline(name) ? engine.getTimeline(name) : undefined;\n};\n\n/**\n * Check if a timeline exists\n */\nMotionFunction.has = function(name: string): boolean {\n return Engine.getInstance().hasTimeline(name);\n};\n\n/**\n * Get names of all registered timelines\n */\nMotionFunction.getNames = function(): string[] {\n return Engine.getInstance().getTimelineNames();\n};\n\n/**\n * Kill a timeline by name\n */\nMotionFunction.kill = function(name: string): void {\n const engine = Engine.getInstance();\n if (engine.hasTimeline(name)) {\n engine.getTimeline(name).kill();\n }\n};\n\n/**\n * Kill all timelines and animations\n */\nMotionFunction.killAll = function(): void {\n Engine.getInstance().killEverything();\n};\n\n/**\n * Refresh all scroll triggers\n */\nMotionFunction.refreshScrollTriggers = function(): void {\n SDKRegistry.triggerManager?.getInstance?.()?.refreshScrollTriggers();\n};\n\n/**\n * Clean up ScrollTrigger DOM artifacts\n */\nMotionFunction.cleanup = function(): void {\n if (typeof document === 'undefined') return;\n document.querySelectorAll('[data-scrolltrigger-spacer]').forEach(el => el.remove());\n document.querySelectorAll('[data-scrolltrigger-markers]').forEach(el => el.remove());\n};\n\n/**\n * Create a scoped animation context that tracks all timelines created\n * during the callback execution. Enables clean teardown and re-initialization\n * when DOM content changes dynamically (AJAX filters, SPA navigation, etc.).\n *\n * @example WordPress / vanilla JS\n * const ctx = Motion.context(() => {\n * Motion('hero', '.title', { from: { opacity: 0 }, to: { opacity: 1 } }).onPageLoad()\n * Motion('cards', '.card', { from: { y: 100 }, to: { y: 0 } }).onScroll({ scrub: true })\n * })\n * // After AJAX filter replaces content:\n * ctx.refresh() // kills old animations, re-runs init, re-resolves selectors\n *\n * @example React\n * useEffect(() => {\n * const ctx = Motion.context(() => { ... })\n * return () => ctx.revert() // clean teardown on unmount\n * }, [])\n */\nMotionFunction.context = function(fn: () => void): MotionContext {\n return new MotionContext(fn);\n};\n\n/**\n * Utility functions — drop-in replacements for gsap.utils.*\n *\n * @example Migration from GSAP\n * // Before: snap: 1 / (gsap.utils.toArray(\".section\").length - 1)\n * // After: snap: 1 / (Motion.utils.toArray(\".section\").length - 1)\n */\nMotionFunction.utils = MotionUtils;\n\nexport const Motion = MotionFunction;\n"
|
|
48
51
|
],
|
|
49
|
-
"mappings": "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",
|
|
50
|
-
"debugId": "
|
|
52
|
+
"mappings": "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",
|
|
53
|
+
"debugId": "59B90128F4684E8164756E2164756E21",
|
|
51
54
|
"names": []
|
|
52
55
|
}
|