@motion.page/sdk 1.0.2 → 1.0.4

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 CHANGED
@@ -636,7 +636,7 @@ Motion('parallax-depth', '.layer', {
636
636
  }).onMouseMove({ type: 'axis', smooth: 0.1 });
637
637
  ```
638
638
 
639
- #### `.onPageLoad()`
639
+ #### `.onPageLoad(config?)`
640
640
 
641
641
  Play the animation automatically when the page finishes loading. If called after `DOMContentLoaded` has already fired (common in SPAs or scripts placed at the bottom of `<body>`), the animation plays immediately.
642
642
 
@@ -647,6 +647,24 @@ Motion('page-intro', [
647
647
  ]).onPageLoad();
648
648
  ```
649
649
 
650
+ **`paused` option** — build the timeline (applies initial states) but don't auto-play. Start it manually with `Motion(name).play()`:
651
+
652
+ ```ts
653
+ // Load timeline paused — user triggers manually
654
+ Motion('hero', '.box', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 1 })
655
+ .onPageLoad({ paused: true })
656
+
657
+ // Later, trigger manually:
658
+ Motion('hero').play()
659
+ ```
660
+
661
+ Can combine with `timing`:
662
+
663
+ ```ts
664
+ Motion('hero', '.box', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 1 })
665
+ .onPageLoad({ timing: 'after', paused: true })
666
+ ```
667
+
650
668
  #### `.onPageExit(config?)`
651
669
 
652
670
  Intercept link clicks, play the exit animation, then navigate to the destination URL after the timeline completes. Works on any website with no server-side dependencies.
@@ -881,7 +899,7 @@ Motion('slide-in', '.card', { from: { opacity: 0 }, duration: 0.6 })
881
899
  | Start | `.onStart(cb)` | `onStart` | none |
882
900
  | Update | `.onUpdate(cb)` | `onUpdate` | `(progress: number, time: number)` via method; `(progress: number)` via config |
883
901
  | Complete | `.onComplete(cb)` | `onComplete` | none |
884
- | Repeat | | `onRepeat` | `(repeatCount: number)` |
902
+ | Repeat | `.onRepeat(cb)` | `onRepeat` | `(repeatCount: number)` |
885
903
  | Reverse complete | — | `onReverseComplete` | none |
886
904
 
887
905
  ---
@@ -1051,6 +1069,55 @@ repeat: { times: -1, yoyo: true, delay: 0.2 } // infinite yoyo with pause betwe
1051
1069
  repeat: { times: 2, yoyo: true, delay: 0.5 } // 2 extra cycles, yoyo, 0.5s pause
1052
1070
  ```
1053
1071
 
1072
+ ### Timeline Repeat
1073
+
1074
+ `.withRepeat()` loops the **entire timeline** after all its animations complete — distinct from per-animation `repeat` in `AnimationConfig`, which only loops an individual animation within the timeline.
1075
+
1076
+ ```ts
1077
+ // Shorthand — loop count (-1 = infinite, 0 = no extra loops)
1078
+ tl.withRepeat(-1): this
1079
+
1080
+ // Full config
1081
+ tl.withRepeat(config: TimelineRepeatConfig): this
1082
+
1083
+ interface TimelineRepeatConfig {
1084
+ times: number; // Number of additional repetitions (-1 = infinite)
1085
+ yoyo?: boolean; // Alternate direction each cycle
1086
+ delay?: number; // Seconds between timeline repetitions
1087
+ }
1088
+ ```
1089
+
1090
+ ```ts
1091
+ // Loop the entire timeline infinitely
1092
+ Motion('hero', '.box', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 1 })
1093
+ .withRepeat(-1)
1094
+ .onPageLoad()
1095
+
1096
+ // Loop 3 times with yoyo and delay
1097
+ Motion('bounce', [
1098
+ { target: '.a', from: { y: '100%' }, to: { y: '0%' }, duration: 0.5 },
1099
+ { target: '.b', from: { opacity: 0 }, to: { opacity: 1 }, duration: 0.3, position: 0.2 }
1100
+ ])
1101
+ .withRepeat({ times: 3, yoyo: true, delay: 0.5 })
1102
+ .onPageLoad()
1103
+ ```
1104
+
1105
+ Use `.onRepeat()` on the `Timeline` instance to react to each completed cycle:
1106
+
1107
+ ```ts
1108
+ Motion('loop', '.el', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 0.8 })
1109
+ .withRepeat(-1)
1110
+ .onRepeat((count) => console.log(`Cycle ${count} complete`))
1111
+ .onPageLoad()
1112
+ ```
1113
+
1114
+ **Per-animation vs. timeline repeat:**
1115
+
1116
+ | Scope | API | Effect |
1117
+ |-------|-----|--------|
1118
+ | Individual animation | `repeat` in `AnimationConfig` | Loops just that one animation within the timeline |
1119
+ | Entire timeline | `.withRepeat()` on `Timeline` | Loops all animations in the timeline together |
1120
+
1054
1121
  ### SplitType
1055
1122
 
1056
1123
  ```ts
@@ -56,6 +56,7 @@ export declare class Animation {
56
56
  private _startCaptured;
57
57
  private _fitSetupFn;
58
58
  private _pendingFitSetup;
59
+ private _fitCleanupFn;
59
60
  private _unregisterCallback;
60
61
  /**
61
62
  * Initialize the animation
@@ -154,6 +155,11 @@ export declare class Animation {
154
155
  * copy (_pendingFitSetup) that is consumed by captureStartValues().
155
156
  */
156
157
  setPendingFitSetup(fn: (pool: typeof PropTweenPool) => void): void;
158
+ /**
159
+ * Register a cleanup function that resets inline styles written by a Fit animation.
160
+ * Called during resetForReplay() so chained fits start from a clean slate on replay.
161
+ */
162
+ setFitCleanup(fn: () => void): void;
157
163
  /**
158
164
  * Reset for timeline replay
159
165
  * Resets callback state for clean replay but KEEPS captured start values
@@ -9,7 +9,7 @@
9
9
  * - Control methods (play, pause, reverse, seek, etc.)
10
10
  * - Trigger system integration
11
11
  */
12
- import type { HoverConfig, ClickConfig, ScrollConfig, MouseMoveConfig, GestureConfig, CursorConfig, PageExitConfig, TargetInput, AnimationConfig } from '../types';
12
+ import type { HoverConfig, ClickConfig, ScrollConfig, MouseMoveConfig, GestureConfig, CursorConfig, PageExitConfig, TargetInput, AnimationConfig, TimelineConfig, RepeatConfig } from '../types';
13
13
  import { Animation } from './Animation';
14
14
  export declare class Timeline {
15
15
  /** Monotonic counter for generating unique each-mode instance names */
@@ -34,9 +34,18 @@ export declare class Timeline {
34
34
  private _isActive;
35
35
  private _isReversed;
36
36
  private _killed;
37
+ private _atZeroState;
38
+ private _repeat;
39
+ private _repeatDelay;
40
+ private _yoyo;
41
+ private _currentRepeat;
42
+ private _completeFired;
43
+ private _inRepeatDelay;
44
+ private _repeatDelayCounter;
37
45
  private _onStart?;
38
46
  private _onUpdate?;
39
47
  private _onComplete?;
48
+ private _onRepeat?;
40
49
  private _startFired;
41
50
  /**
42
51
  * Reference to attached trigger (for cleanup in kill()).
@@ -62,11 +71,18 @@ export declare class Timeline {
62
71
  private _savedTransitions;
63
72
  private _transitionsDisabled;
64
73
  private _savedWillChange;
65
- constructor(name?: string);
74
+ private _willChangeApplied;
75
+ private _splitParentOverrides;
76
+ constructor(name?: string, config?: TimelineConfig);
66
77
  /**
67
- * Get timeline duration
78
+ * Get timeline duration (single cycle)
68
79
  */
69
80
  duration(): number;
81
+ /**
82
+ * Get total duration including all repeats.
83
+ * Returns Infinity when repeat is -1 (infinite loop).
84
+ */
85
+ totalDuration(): number;
70
86
  /**
71
87
  * Clear timeline state for rebuild (used for re-triggerable animations)
72
88
  * Kills existing animations, clears children, resets state
@@ -86,6 +102,12 @@ export declare class Timeline {
86
102
  * from fighting frame-by-frame SDK rendering.
87
103
  */
88
104
  private _disableTransitions;
105
+ /**
106
+ * Apply will-change: transform to elements with transform animations.
107
+ * Deferred from build time to first tick so the browser has at least one
108
+ * frame to layout newly-created elements (e.g. text split spans).
109
+ */
110
+ private _applyWillChange;
89
111
  /**
90
112
  * Restore original CSS transition values on all animated elements.
91
113
  * Called on timeline completion and kill to re-enable CSS hover/focus
@@ -174,9 +196,17 @@ export declare class Timeline {
174
196
  */
175
197
  private _setupMouseMoveEachMode;
176
198
  /**
177
- * Trigger timeline on page load
199
+ * Trigger timeline on page load.
200
+ *
201
+ * @param config - Optional configuration
202
+ * @param config.timing - When to play: 'before' (immediately), 'during' (DOMContentLoaded), 'after' (window load). Defaults to 'during'.
203
+ * @param config.paused - When true, the timeline is built but NOT played automatically.
204
+ * Start it manually with `Motion('timelineName').play()`.
178
205
  */
179
- onPageLoad(): this;
206
+ onPageLoad(config?: {
207
+ timing?: 'before' | 'during' | 'after';
208
+ paused?: boolean;
209
+ }): this;
180
210
  /**
181
211
  * Trigger timeline when user navigates away from the page (clicks a link).
182
212
  * Intercepts the click, plays the exit animation, then navigates to the target URL.
@@ -215,6 +245,8 @@ export declare class Timeline {
215
245
  /**
216
246
  * Play timeline from position.
217
247
  * If already playing forward, continues from current position.
248
+ * No-op if already completed forward (at the end) and no `from` is specified —
249
+ * prevents spurious onStart/onComplete re-firing during continuous gesture events.
218
250
  */
219
251
  play(from?: number): this;
220
252
  /**
@@ -224,12 +256,20 @@ export declare class Timeline {
224
256
  /**
225
257
  * Reverse timeline from position.
226
258
  * If already reversing, continues from current position.
259
+ * No-op if already completed reverse (at the start) and no `from` is specified —
260
+ * prevents spurious onStart/onComplete re-firing during continuous gesture events.
227
261
  */
228
262
  reverse(from?: number): this;
229
263
  /**
230
264
  * Restart timeline
231
265
  */
232
266
  restart(): this;
267
+ /**
268
+ * Reset all children for the start of a new repeat cycle.
269
+ * Restores elements to their captured initial values and re-renders
270
+ * position-0 animations at their FROM state.
271
+ */
272
+ private _resetChildrenForCycle;
233
273
  /**
234
274
  * Render only position-0 (non-lazy) animations at their start state
235
275
  */
@@ -258,6 +298,24 @@ export declare class Timeline {
258
298
  * Set callback for when timeline completes
259
299
  */
260
300
  onComplete(callback: () => void): this;
301
+ /**
302
+ * Set callback for each repeat cycle completion
303
+ * @param callback - Receives the current repeat count (starts at 1)
304
+ */
305
+ onRepeat(callback: (repeatCount: number) => void): this;
306
+ /**
307
+ * Configure timeline-level repeat behavior (fluent).
308
+ * Use -1 for infinite repeat, or a RepeatConfig for advanced options.
309
+ * @example
310
+ * Motion('loop', '.box', { from: { opacity: 0 }, to: { opacity: 1 }, duration: 0.5 })
311
+ * .withRepeat(-1)
312
+ * .onPageLoad()
313
+ *
314
+ * Motion('bounce', [entries])
315
+ * .withRepeat({ times: 3, yoyo: true, delay: 0.2 })
316
+ * .onPageLoad()
317
+ */
318
+ withRepeat(config: number | RepeatConfig): this;
261
319
  /**
262
320
  * Get or set progress (0-1)
263
321
  */
@@ -282,6 +340,10 @@ export declare class Timeline {
282
340
  * Check if timeline is currently active
283
341
  */
284
342
  isActive(): boolean;
343
+ /**
344
+ * Check if timeline is currently playing in reverse direction
345
+ */
346
+ reversed(): boolean;
285
347
  /**
286
348
  * Get timeline name
287
349
  */
@@ -2,7 +2,10 @@
2
2
  * Easing Functions
3
3
  *
4
4
  * All easings are based on Robert Penner's easing equations
5
- * and normalized to accept/return values in the 0-1 range
5
+ * and normalized to accept/return values in the 0-1 range.
6
+ *
7
+ * Includes GSAP-compatible aliases (quad, cubic, quart, quint, strong)
8
+ * and special easings (slow, rough) for migration compatibility.
6
9
  */
7
10
  import type { EasingFunction } from '../types';
8
11
  export declare const linear: (t: number) => number;
@@ -27,6 +30,31 @@ export declare const power4: {
27
30
  out: (t: number) => number;
28
31
  inOut: (t: number) => number;
29
32
  };
33
+ export declare const quad: {
34
+ in: (t: number) => number;
35
+ out: (t: number) => number;
36
+ inOut: (t: number) => number;
37
+ };
38
+ export declare const cubic: {
39
+ in: (t: number) => number;
40
+ out: (t: number) => number;
41
+ inOut: (t: number) => number;
42
+ };
43
+ export declare const quart: {
44
+ in: (t: number) => number;
45
+ out: (t: number) => number;
46
+ inOut: (t: number) => number;
47
+ };
48
+ export declare const quint: {
49
+ in: (t: number) => number;
50
+ out: (t: number) => number;
51
+ inOut: (t: number) => number;
52
+ };
53
+ export declare const strong: {
54
+ in: (t: number) => number;
55
+ out: (t: number) => number;
56
+ inOut: (t: number) => number;
57
+ };
30
58
  export declare const sine: {
31
59
  in: (t: number) => number;
32
60
  out: (t: number) => number;
@@ -57,8 +85,24 @@ export declare const bounce: {
57
85
  out: (t: number) => number;
58
86
  inOut: (t: number) => number;
59
87
  };
88
+ export declare const slow: {
89
+ in: EasingFunction;
90
+ out: EasingFunction;
91
+ inOut: EasingFunction;
92
+ };
93
+ export declare const rough: {
94
+ in: EasingFunction;
95
+ out: EasingFunction;
96
+ inOut: EasingFunction;
97
+ };
98
+ export declare const steps: {
99
+ in: EasingFunction;
100
+ out: EasingFunction;
101
+ inOut: EasingFunction;
102
+ };
60
103
  /**
61
104
  * Get an easing function by string name
105
+ * Supports both simple names ("back.out") and parameterized ("back.out(1.4)")
62
106
  * @param name Easing name (case insensitive)
63
107
  * @returns Easing function, or power1.out if not found
64
108
  */