@pixodesk/svg-animator-core 1.0.45 → 1.0.47
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 +12 -0
- package/dist/{PxDocumentDiagnostic-CBhVyNEi.d.cts → PxDocumentDiagnostic-DX8w42-I.d.cts} +364 -207
- package/dist/{PxDocumentDiagnostic-CBhVyNEi.d.ts → PxDocumentDiagnostic-DX8w42-I.d.ts} +364 -207
- package/dist/{chunk-YVKMPBHE.js → chunk-37OFJ3RX.js} +139 -78
- package/dist/chunk-37OFJ3RX.js.map +1 -0
- package/dist/chunk-K2732IVS.min.js +1 -0
- package/dist/index.cjs +141 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/internal.cjs +137 -76
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +4 -5
- package/dist/internal.d.ts +4 -5
- package/dist/internal.js +3 -1
- package/dist/internal.js.map +1 -1
- package/dist/internal.min.cjs +1 -1
- package/dist/internal.min.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-5HEVTP2N.min.js +0 -1
- package/dist/chunk-YVKMPBHE.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PxAnimatedSvgDocument, a as PxEngineCallbacks, b as PxAnimatorApi, c as PxAnimatorConfig, d as PxTriggerStart, e as PxNode } from './PxDocumentDiagnostic-
|
|
2
|
-
export { f as PX_TRANSFORM_PART_KEYS, g as PX_TRIGGER_DEFAULTS, h as PX_WIRE_BASELINE_VERSION, i as PX_WIRE_STEPS, j as PX_WIRE_VERSION, k as PX_WIRE_VERSION_KEY, l as PxAlongPathMode, m as PxAnimatedSvgDocumentSchema, n as PxAnimationDefinition, o as PxAnimatorCallbacks, p as PxAnimatorConfigSchema, q as PxAnimatorHandle, r as PxAttrValueSchema, s as PxBezierPath, t as PxBezierPathSchema, u as PxBinding, v as PxClipPathEffectSchema, w as PxCloneEffectSchema, x as PxCloneWithout, y as PxControlMode, z as PxControlProps, A as PxDefinitions, B as PxDefinitionsSchema, C as PxDiagnostic, D as PxDiagnosticCode, E as PxDiagnosticKind, F as PxDiagnostics, G as PxDiagnosticsConfig, H as PxEffects, I as PxEffectsSchema, J as PxElementAnimation, K as PxElementAnimationSchema, L as PxFillGradientEffectSchema, M as PxFillMode, N as PxFinishAction, O as PxGlyph, Q as PxGlyphFont, R as PxGradientSpreadMethod, S as PxGradientStopSchema, T as PxGradientType, U as PxInfer, V as PxKeyframe, W as PxKeyframeSchema, X as PxKeyframeValueSchema, Y as PxLengthAdjust, Z as PxLoop, _ as PxLoopDirection, $ as PxLoopRepeatAt, a0 as PxLoopSchema, a1 as PxMaskType, a2 as PxMaskedByEffectSchema, a3 as PxMouseOutAction, a4 as PxNodeBaseSchema, a5 as PxNodeSchema, a6 as PxOffScreenAction, a7 as PxPathOverflow, a8 as PxPinAlign, a9 as PxPlaybackApi, aa as PxPlaybackDirection, ab as PxPropertyAnimation, ac as PxPropertyAnimationSchema, ad as PxRemoveIndex, ae as PxRepeaterEffectSchema, af as PxRetimeEffectSchema, ag as PxSchema, ah as PxSchemaDesc, ai as PxScroll, aj as PxScrollAxis, ak as PxScrollKind, al as PxScrollPhase, am as PxScrollRangePoint, an as PxScrollRangePointSchema, ao as PxScrollRangeSchema, ap as PxScrollSchema, aq as PxScrollSource, ar as PxStrokeTrimEffectSchema, as as PxStrokeTrimSubPaths, at as PxSvgNode, au as PxSvgNodeRootSchema, av as PxTextEffectSchema, aw as PxTextPathEffectSchema, ax as PxTextPathMethod, ay as PxTextPathSpacing, az as
|
|
1
|
+
import { P as PxAnimatedSvgDocument, a as PxEngineCallbacks, b as PxAnimatorApi, c as PxAnimatorConfig, d as PxTriggerStart, e as PxNode } from './PxDocumentDiagnostic-DX8w42-I.cjs';
|
|
2
|
+
export { f as PX_TRANSFORM_PART_KEYS, g as PX_TRIGGER_DEFAULTS, h as PX_WIRE_BASELINE_VERSION, i as PX_WIRE_STEPS, j as PX_WIRE_VERSION, k as PX_WIRE_VERSION_KEY, l as PxAlongPathMode, m as PxAnimatedSvgDocumentSchema, n as PxAnimationDefinition, o as PxAnimatorCallbacks, p as PxAnimatorConfigSchema, q as PxAnimatorHandle, r as PxAttrValueSchema, s as PxBezierPath, t as PxBezierPathSchema, u as PxBinding, v as PxClipPathEffectSchema, w as PxCloneEffectSchema, x as PxCloneWithout, y as PxControlMode, z as PxControlProps, A as PxDefinitions, B as PxDefinitionsSchema, C as PxDiagnostic, D as PxDiagnosticCode, E as PxDiagnosticKind, F as PxDiagnostics, G as PxDiagnosticsConfig, H as PxEffects, I as PxEffectsSchema, J as PxElementAnimation, K as PxElementAnimationSchema, L as PxFillGradientEffectSchema, M as PxFillMode, N as PxFinishAction, O as PxGlyph, Q as PxGlyphFont, R as PxGradientSpreadMethod, S as PxGradientStopSchema, T as PxGradientType, U as PxInfer, V as PxKeyframe, W as PxKeyframeSchema, X as PxKeyframeValueSchema, Y as PxLengthAdjust, Z as PxLoop, _ as PxLoopDirection, $ as PxLoopRepeatAt, a0 as PxLoopSchema, a1 as PxMaskType, a2 as PxMaskedByEffectSchema, a3 as PxMouseOutAction, a4 as PxNodeBaseSchema, a5 as PxNodeSchema, a6 as PxOffScreenAction, a7 as PxPathOverflow, a8 as PxPinAlign, a9 as PxPlaybackApi, aa as PxPlaybackDirection, ab as PxPropertyAnimation, ac as PxPropertyAnimationSchema, ad as PxRemoveIndex, ae as PxRepeaterEffectSchema, af as PxRetimeEffectSchema, ag as PxSchema, ah as PxSchemaDesc, ai as PxScroll, aj as PxScrollAxis, ak as PxScrollKind, al as PxScrollPhase, am as PxScrollRangePoint, an as PxScrollRangePointSchema, ao as PxScrollRangeSchema, ap as PxScrollSchema, aq as PxScrollSource, ar as PxStrokeTrimEffectSchema, as as PxStrokeTrimSubPaths, at as PxSvgNode, au as PxSvgNodeRootSchema, av as PxTextEffectSchema, aw as PxTextPathEffectSchema, ax as PxTextPathMethod, ay as PxTextPathSpacing, az as PxTimeTimelineSchema, aA as PxTimeline, aB as PxTimelineEngine, aC as PxTimelineEngineSetting, aD as PxTimelineSchema, aE as PxTransformByEffectSchema, aF as PxTransformParts, aG as PxTransformPartsSchema, aH as PxTransformValue, aI as PxTransformValueSchema, aJ as PxTrigger, aK as PxTriggerSchema, aL as PxUnits, aM as PxValidationContext, aN as PxVec2, aO as PxWireConversionOptions, aP as PxWireConversionResult, aQ as PxWireStepKind, aR as PxWireVersion, aS as PxWireVersionRelation, aT as PxWireVersionStep, aU as applyWireSteps, aV as calcAnimationValues, aW as compareWireVersion, aX as controlModeTakesOverTrigger, aY as convertWireDocument, aZ as describeSchema, a_ as diagnoseDocument, a$ as downgradeWireDocument, b0 as flattenAnimatorTimeline, b1 as formatWireVersion, b2 as generateNewIds, b3 as getAnimatorConfig, b4 as getBindings, b5 as getChildren, b6 as getDefinitions, b7 as isNativeForced, b8 as isPxDocument, b9 as isValidPxDocument, ba as materializeAllInTree, bb as mayUseNativeScrollTimeline, bc as nestAnimatorTimeline, bd as normalizeBindings, be as parseWireVersion, bf as px, bg as readWireVersion, bh as resolveControlMode, bi as resolveTimelineEngine, bj as resolveTrigger, bk as schemaKeys, bl as toDomProps, bm as validateDocument, bn as validateNodeEffects, bo as wireVersionAdvice } from './PxDocumentDiagnostic-DX8w42-I.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PLAYER'S WIRE SCHEMA VERSION — the `a.b` of `svgRoot.animator.version`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PxAnimatedSvgDocument, a as PxEngineCallbacks, b as PxAnimatorApi, c as PxAnimatorConfig, d as PxTriggerStart, e as PxNode } from './PxDocumentDiagnostic-
|
|
2
|
-
export { f as PX_TRANSFORM_PART_KEYS, g as PX_TRIGGER_DEFAULTS, h as PX_WIRE_BASELINE_VERSION, i as PX_WIRE_STEPS, j as PX_WIRE_VERSION, k as PX_WIRE_VERSION_KEY, l as PxAlongPathMode, m as PxAnimatedSvgDocumentSchema, n as PxAnimationDefinition, o as PxAnimatorCallbacks, p as PxAnimatorConfigSchema, q as PxAnimatorHandle, r as PxAttrValueSchema, s as PxBezierPath, t as PxBezierPathSchema, u as PxBinding, v as PxClipPathEffectSchema, w as PxCloneEffectSchema, x as PxCloneWithout, y as PxControlMode, z as PxControlProps, A as PxDefinitions, B as PxDefinitionsSchema, C as PxDiagnostic, D as PxDiagnosticCode, E as PxDiagnosticKind, F as PxDiagnostics, G as PxDiagnosticsConfig, H as PxEffects, I as PxEffectsSchema, J as PxElementAnimation, K as PxElementAnimationSchema, L as PxFillGradientEffectSchema, M as PxFillMode, N as PxFinishAction, O as PxGlyph, Q as PxGlyphFont, R as PxGradientSpreadMethod, S as PxGradientStopSchema, T as PxGradientType, U as PxInfer, V as PxKeyframe, W as PxKeyframeSchema, X as PxKeyframeValueSchema, Y as PxLengthAdjust, Z as PxLoop, _ as PxLoopDirection, $ as PxLoopRepeatAt, a0 as PxLoopSchema, a1 as PxMaskType, a2 as PxMaskedByEffectSchema, a3 as PxMouseOutAction, a4 as PxNodeBaseSchema, a5 as PxNodeSchema, a6 as PxOffScreenAction, a7 as PxPathOverflow, a8 as PxPinAlign, a9 as PxPlaybackApi, aa as PxPlaybackDirection, ab as PxPropertyAnimation, ac as PxPropertyAnimationSchema, ad as PxRemoveIndex, ae as PxRepeaterEffectSchema, af as PxRetimeEffectSchema, ag as PxSchema, ah as PxSchemaDesc, ai as PxScroll, aj as PxScrollAxis, ak as PxScrollKind, al as PxScrollPhase, am as PxScrollRangePoint, an as PxScrollRangePointSchema, ao as PxScrollRangeSchema, ap as PxScrollSchema, aq as PxScrollSource, ar as PxStrokeTrimEffectSchema, as as PxStrokeTrimSubPaths, at as PxSvgNode, au as PxSvgNodeRootSchema, av as PxTextEffectSchema, aw as PxTextPathEffectSchema, ax as PxTextPathMethod, ay as PxTextPathSpacing, az as
|
|
1
|
+
import { P as PxAnimatedSvgDocument, a as PxEngineCallbacks, b as PxAnimatorApi, c as PxAnimatorConfig, d as PxTriggerStart, e as PxNode } from './PxDocumentDiagnostic-DX8w42-I.js';
|
|
2
|
+
export { f as PX_TRANSFORM_PART_KEYS, g as PX_TRIGGER_DEFAULTS, h as PX_WIRE_BASELINE_VERSION, i as PX_WIRE_STEPS, j as PX_WIRE_VERSION, k as PX_WIRE_VERSION_KEY, l as PxAlongPathMode, m as PxAnimatedSvgDocumentSchema, n as PxAnimationDefinition, o as PxAnimatorCallbacks, p as PxAnimatorConfigSchema, q as PxAnimatorHandle, r as PxAttrValueSchema, s as PxBezierPath, t as PxBezierPathSchema, u as PxBinding, v as PxClipPathEffectSchema, w as PxCloneEffectSchema, x as PxCloneWithout, y as PxControlMode, z as PxControlProps, A as PxDefinitions, B as PxDefinitionsSchema, C as PxDiagnostic, D as PxDiagnosticCode, E as PxDiagnosticKind, F as PxDiagnostics, G as PxDiagnosticsConfig, H as PxEffects, I as PxEffectsSchema, J as PxElementAnimation, K as PxElementAnimationSchema, L as PxFillGradientEffectSchema, M as PxFillMode, N as PxFinishAction, O as PxGlyph, Q as PxGlyphFont, R as PxGradientSpreadMethod, S as PxGradientStopSchema, T as PxGradientType, U as PxInfer, V as PxKeyframe, W as PxKeyframeSchema, X as PxKeyframeValueSchema, Y as PxLengthAdjust, Z as PxLoop, _ as PxLoopDirection, $ as PxLoopRepeatAt, a0 as PxLoopSchema, a1 as PxMaskType, a2 as PxMaskedByEffectSchema, a3 as PxMouseOutAction, a4 as PxNodeBaseSchema, a5 as PxNodeSchema, a6 as PxOffScreenAction, a7 as PxPathOverflow, a8 as PxPinAlign, a9 as PxPlaybackApi, aa as PxPlaybackDirection, ab as PxPropertyAnimation, ac as PxPropertyAnimationSchema, ad as PxRemoveIndex, ae as PxRepeaterEffectSchema, af as PxRetimeEffectSchema, ag as PxSchema, ah as PxSchemaDesc, ai as PxScroll, aj as PxScrollAxis, ak as PxScrollKind, al as PxScrollPhase, am as PxScrollRangePoint, an as PxScrollRangePointSchema, ao as PxScrollRangeSchema, ap as PxScrollSchema, aq as PxScrollSource, ar as PxStrokeTrimEffectSchema, as as PxStrokeTrimSubPaths, at as PxSvgNode, au as PxSvgNodeRootSchema, av as PxTextEffectSchema, aw as PxTextPathEffectSchema, ax as PxTextPathMethod, ay as PxTextPathSpacing, az as PxTimeTimelineSchema, aA as PxTimeline, aB as PxTimelineEngine, aC as PxTimelineEngineSetting, aD as PxTimelineSchema, aE as PxTransformByEffectSchema, aF as PxTransformParts, aG as PxTransformPartsSchema, aH as PxTransformValue, aI as PxTransformValueSchema, aJ as PxTrigger, aK as PxTriggerSchema, aL as PxUnits, aM as PxValidationContext, aN as PxVec2, aO as PxWireConversionOptions, aP as PxWireConversionResult, aQ as PxWireStepKind, aR as PxWireVersion, aS as PxWireVersionRelation, aT as PxWireVersionStep, aU as applyWireSteps, aV as calcAnimationValues, aW as compareWireVersion, aX as controlModeTakesOverTrigger, aY as convertWireDocument, aZ as describeSchema, a_ as diagnoseDocument, a$ as downgradeWireDocument, b0 as flattenAnimatorTimeline, b1 as formatWireVersion, b2 as generateNewIds, b3 as getAnimatorConfig, b4 as getBindings, b5 as getChildren, b6 as getDefinitions, b7 as isNativeForced, b8 as isPxDocument, b9 as isValidPxDocument, ba as materializeAllInTree, bb as mayUseNativeScrollTimeline, bc as nestAnimatorTimeline, bd as normalizeBindings, be as parseWireVersion, bf as px, bg as readWireVersion, bh as resolveControlMode, bi as resolveTimelineEngine, bj as resolveTrigger, bk as schemaKeys, bl as toDomProps, bm as validateDocument, bn as validateNodeEffects, bo as wireVersionAdvice } from './PxDocumentDiagnostic-DX8w42-I.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PLAYER'S WIRE SCHEMA VERSION — the `a.b` of `svgRoot.animator.version`.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PX_DEFAULT_DURATION_MS,
|
|
3
|
+
PX_DEFAULT_ITERATIONS,
|
|
3
4
|
PX_TIMELINE_SHARED_KEYS,
|
|
4
5
|
PX_TIME_ONLY_TIMELINE_KEYS,
|
|
5
6
|
PX_TRANSFORM_PART_KEYS,
|
|
@@ -61,6 +62,7 @@ import {
|
|
|
61
62
|
PxTextPathEffectSchema,
|
|
62
63
|
PxTextPathMethod,
|
|
63
64
|
PxTextPathSpacing,
|
|
65
|
+
PxTimeTimelineSchema,
|
|
64
66
|
PxTimelineEngine,
|
|
65
67
|
PxTimelineEngineSetting,
|
|
66
68
|
PxTimelineSchema,
|
|
@@ -109,7 +111,7 @@ import {
|
|
|
109
111
|
validateDocument,
|
|
110
112
|
validateNodeEffects,
|
|
111
113
|
wireVersionAdvice
|
|
112
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-37OFJ3RX.js";
|
|
113
115
|
|
|
114
116
|
// src/playback/PxPlaybackTime.ts
|
|
115
117
|
var PX_RATE_REJECTED = "setPlaybackRate: rate must be finite and non-zero";
|
|
@@ -191,13 +193,13 @@ function createAdapterAnimator(doc, adapter, callbacks) {
|
|
|
191
193
|
const bindings = normalizeBindings(doc, PxTimelineEngine.js);
|
|
192
194
|
const _iterations = config.iterations;
|
|
193
195
|
let iterations = 1;
|
|
194
|
-
if (typeof _iterations === "number") iterations = _iterations ||
|
|
196
|
+
if (typeof _iterations === "number") iterations = _iterations || PX_DEFAULT_ITERATIONS;
|
|
195
197
|
if (_iterations === "infinite") iterations = Infinity;
|
|
196
198
|
if (iterations < 1) iterations = 1;
|
|
197
199
|
const duration = +(config.duration || PX_DEFAULT_DURATION_MS);
|
|
198
|
-
const totalDuration = duration && iterations ? duration * (iterations === Infinity ? Infinity : iterations) : duration ? (iterations != null ? iterations :
|
|
199
|
-
const direction = config.direction ||
|
|
200
|
-
const fill = (_a = config.fill) != null ? _a :
|
|
200
|
+
const totalDuration = duration && iterations ? duration * (iterations === Infinity ? Infinity : iterations) : duration ? (iterations != null ? iterations : PX_DEFAULT_ITERATIONS) * duration : 0;
|
|
201
|
+
const direction = config.direction || PxTimeTimelineSchema.defaults.direction;
|
|
202
|
+
const fill = (_a = config.fill) != null ? _a : PxTimeTimelineSchema.defaults.fillMode;
|
|
201
203
|
const fillsForwards = fill === "forwards" || fill === "both";
|
|
202
204
|
const fillsBackwards = fill === "backwards" || fill === "both";
|
|
203
205
|
let timerId = null;
|
|
@@ -664,6 +666,7 @@ export {
|
|
|
664
666
|
PxTextPathEffectSchema,
|
|
665
667
|
PxTextPathMethod,
|
|
666
668
|
PxTextPathSpacing,
|
|
669
|
+
PxTimeTimelineSchema,
|
|
667
670
|
PxTimelineEngine,
|
|
668
671
|
PxTimelineEngineSetting,
|
|
669
672
|
PxTimelineSchema,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/playback/PxPlaybackTime.ts","../src/playback/PxFrameLoop.ts","../src/playback/PxAnimatorConfigPatch.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ONE time contract for every engine (API review §3). The same three calls used to mean three\n// different things:\n//\n// | engine | getCurrentTime() | setCurrentTime(t) | setPlaybackRate(0) |\n// |-----------------|-------------------------|-------------------|--------------------|\n// | web, WAAPI | ms across the whole run | NOT clamped | accepted |\n// | web, frame loop | ms across the whole run | clamped | rejected + warn |\n// | React Native | ms within ONE iteration | wrapped | rejected + warn |\n//\n// So a slider built on `getCurrentTime()` jumped back every iteration on React Native, and\n// `timeline.engine: 'auto'` meant two documents on one page could answer the same call\n// differently. The contract every player now implements:\n//\n// - time is ms from the start of the WHOLE run, iterations included — never per-iteration;\n// - a seek clamps to [0, seekCeilingMs];\n// - a rate of 0 is rejected everywhere, with this one message.\n\n/** The one message every engine prints for a rejected rate. @public @advanced */\nexport const PX_RATE_REJECTED = 'setPlaybackRate: rate must be finite and non-zero';\n\n/**\n * A playback rate is usable when it is finite and non-zero.\n *\n * 0 is rejected rather than accepted: it freezes the animation in a state indistinguishable\n * from a stuck player, and `pause()` already says that properly. Two of the three engines\n * rejected it already — this makes the third agree.\n * @public @advanced\n */\nexport function isValidPlaybackRate(rate: number): boolean {\n return Number.isFinite(rate) && rate !== 0;\n}\n\n/**\n * Highest seekable time, ms — `duration × iterations`.\n *\n * `Infinity` for an endless timeline, so callers must test `Number.isFinite` before using it\n * as an upper bound. This is the SEEK ceiling, which is deliberately not the same thing as the\n * span `progress` covers — see `progressSpanMs`.\n * @public @advanced\n */\nexport function seekCeilingMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return Infinity;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * The span `progress` 0→1 covers, ms.\n *\n * An endless timeline maps progress onto ONE iteration — the rule the components already\n * document for the `progress` prop (\"0–1 of duration × iterations, one iteration when\n * iterations is 'infinite'\"). Always finite, so it is safe as a divisor.\n * @public @advanced\n */\nexport function progressSpanMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return durationMs;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * Clamps a seek into [0, ceiling].\n *\n * `NaN` and anything below 0 land on 0. `Infinity` means \"the end\", so it clamps to the ceiling\n * like any other overshoot — except on an endless timeline, where there is no end to land on and\n * a non-finite playhead would poison every later read, so that reads as 0.\n * @public @advanced\n */\nexport function clampSeekMs(timeMs: number, ceilingMs: number): number {\n if (Number.isNaN(timeMs) || timeMs < 0) return 0;\n if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;\n return Number.isFinite(timeMs) ? timeMs : 0;\n}\n\n/**\n * Whole-run ms → 0–1.\n *\n * A finite timeline clamps at both ends. An endless one wraps within the current iteration,\n * so the value stays meaningful however long it has been running. A zero-length span reads as\n * 0 rather than NaN.\n * @public @advanced\n */\nexport function timeToProgress(timeMs: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(timeMs)) return 0;\n if (timeMs <= 0) return 0;\n if (iterations === Infinity) return (timeMs % span) / span;\n return timeMs >= span ? 1 : timeMs / span;\n}\n\n/** 0–1 → whole-run ms, clamped into the span. A non-finite progress reads as 0. @public @advanced */\nexport function progressToTimeMs(progress: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(progress)) return 0;\n if (progress <= 0) return 0;\n return progress >= 1 ? span : progress * span;\n}\n\n/** A playhead that keeps whole-run time across iterations. See `createRunClock`. @public @advanced */\nexport interface PxRunClock {\n /** Whole-run position right now, ms, clamped to the ceiling. */\n now(): number;\n /** True while time is advancing. */\n isRunning(): boolean;\n /** Start or resume at `rate`, optionally from a given whole-run time. */\n start(rate: number, atMs?: number): void;\n /** Stop advancing, freezing the current position. */\n stop(): void;\n /** Jump to a whole-run time; keeps running if it already was. */\n seek(ms: number): void;\n}\n\n/**\n * A whole-run playhead that survives repetition.\n *\n * React Native drives playback with Reanimated's `withRepeat`, whose shared value only ever\n * holds the position WITHIN one iteration and which never reports how many iterations have\n * elapsed. Whole-run time therefore cannot be recovered from it: under `alternate` the value\n * runs backwards rather than wrapping, which is indistinguishable from a negative rate. So the\n * time is kept on a clock of its own — the same thing the frame-loop engine does inline.\n *\n * `nowFn` is injectable so this is testable without real time passing.\n * @public @advanced\n */\nexport function createRunClock(ceilingMs: number, nowFn: () => number = Date.now): PxRunClock {\n let baseMs = 0; // whole-run ms as of the last start/seek/stop\n let startedAt = 0; // nowFn() when running began\n let running = false; // a FLAG, not `startedAt !== 0`: a time source may legitimately\n let rate = 1; // read 0, and `Date.now()` never does — so the bug would hide.\n\n const value = (): number => running\n ? clampSeekMs(baseMs + (nowFn() - startedAt) * rate, ceilingMs)\n : clampSeekMs(baseMs, ceilingMs);\n\n return {\n now: value,\n isRunning: () => running,\n start: (r: number, atMs?: number) => {\n baseMs = clampSeekMs(atMs ?? value(), ceilingMs);\n rate = isValidPlaybackRate(r) ? r : 1;\n startedAt = nowFn();\n running = true;\n },\n stop: () => {\n baseMs = value();\n startedAt = 0;\n running = false;\n },\n seek: (ms: number) => {\n baseMs = clampSeekMs(ms, ceilingMs);\n if (running) startedAt = nowFn();\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxAnimatedSvgDocument, type PxAnimatorApi, type PxEngineCallbacks } from '../format/PxAnimatorTypes';\nimport { getAnimatorConfig, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport { camelCaseToKebabWordIfNeeded, clamp, PX_DEFAULT_DURATION_MS, PX_STYLE_ATTR_NAMES } from '../util/PxAnimatorUtil';\nimport { calcAnimationValues, normalizeBindings } from '../animation/PxDefinitions';\nimport { clampSeekMs, isValidPlaybackRate, progressToTimeMs, PX_RATE_REJECTED, timeToProgress } from './PxPlaybackTime';\n\n\n// Frame scheduling — resolved LAZILY from globalThis on every call so test\n// harnesses that install fake timers (and hosts that polyfill rAF late) are\n// honored. Falls back to a ~60fps setTimeout when rAF is unavailable.\nfunction requestFrame(cb: () => void): number {\n const g: any = globalThis as any;\n if (typeof g.requestAnimationFrame === 'function') return g.requestAnimationFrame(cb);\n return g.setTimeout(cb, 16) as unknown as number;\n}\n\nfunction cancelFrame(handle: number): void {\n const g: any = globalThis as any;\n if (typeof g.cancelAnimationFrame === 'function') { g.cancelAnimationFrame(handle); return; }\n g.clearTimeout(handle);\n}\n\n/**\n * Platform adapter interface for abstracting platform-specific operations.\n * @public\n */\nexport interface PxPlatformAdapter {\n\n /** Check if the root element is still connected/mounted */\n isConnected(): boolean;\n\n /** Set an attribute on an element by id */\n setAttribute(id: string, attrName: string, value: string): void;\n}\n\n/**\n * Creates an animator instance that uses a frame loop for animations.\n * This is the abstract/platform-agnostic version.\n *\n * @param adapter Platform adapter for DOM/environment operations.\n * @param callbacks Optional lifecycle callbacks.\n * @returns A PxAnimatorApi instance.\n * @public @advanced\n */\nexport function createAdapterAnimator(\n doc: PxAnimatedSvgDocument,\n adapter: PxPlatformAdapter,\n callbacks?: PxEngineCallbacks\n): PxAnimatorApi {\n\n const config = getAnimatorConfig(doc) || {};\n\n const bindings = normalizeBindings(doc, PxTimelineEngine.js);\n\n // iterations: either number or Infinity\n const _iterations = config.iterations;\n let iterations: number = 1;\n if (typeof _iterations === 'number') iterations = _iterations || 1;\n if (_iterations === 'infinite') iterations = Infinity;\n if (iterations < 1) iterations = 1;\n\n const duration = +(config.duration || PX_DEFAULT_DURATION_MS); // per-iteration duration (ms), cannot be 0!\n const totalDuration = duration && iterations ?\n duration * (iterations === Infinity ? Infinity : iterations) :\n (duration ? (iterations ?? 1) * duration : 0);\n\n // direction handling\n const direction = config.direction || 'normal'; // 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'\n\n // fill handling — mirrors the Web Animations API `fill` semantics as closely\n // as a frame-writer can. Default 'forwards' (hold final state) matches the\n // waapi engine and the config doc.\n const fill = config.fill ?? 'forwards';\n const fillsForwards = fill === 'forwards' || fill === 'both';\n const fillsBackwards = fill === 'backwards' || fill === 'both';\n\n ////////////////////////////////////////////////////////////////\n\n let timerId: number | null = null;\n let playing = false;\n\n // Accumulated logical time before the current run (ms). A positive\n // config.delay means \"wait before starting\" → the animation starts at\n // NEGATIVE logical time and reaches 0 after `delay` ms (same convention as\n // the Web Animations API). A negative config.delay means \"seek into the\n // animation\" → positive start time.\n let timeBeforeLastStartMs = -(config.delay || 0);\n let lastStartedTs = 0; // timestamp when last started/resumed\n let playbackRate = 1;\n\n let finishCalled = false; // ensures onFinish called once\n\n // Frame rate throttling\n const frameRate = config.frameRate;\n const minFrameIntervalMs = frameRate && frameRate > 0 ? 1000 / frameRate : 0;\n let lastRenderTs = 0; // timestamp of last render\n\n // Raw logical time — may be negative during the delay phase; not clamped\n // to the [0, totalDuration] range at the top end either. Internal use only.\n const getRawAnimTime = () => {\n // compute effective elapsed time (ms), taking playbackRate into account\n const runningElapsed = lastStartedTs ? (Date.now() - lastStartedTs) * playbackRate : 0;\n return timeBeforeLastStartMs + runningElapsed;\n };\n\n const getAnimCurrentTime = () => {\n let time = getRawAnimTime();\n\n // clamp to [0, totalDuration]\n if (Number.isFinite(totalDuration) && time > (totalDuration as number)) time = totalDuration as number;\n if (time < 0) time = 0;\n return time;\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n // separate render function that uses a given currentTime (ms)\n function renderFrame(currentTimeMs: number) {\n\n function getEffectiveProgress() {\n // If no duration or duration === 0, set iteration/progress accordingly\n const safeDuration = duration > 0 ? duration : 1; // avoid division by zero\n\n let rawProgress = 0;\n let iteration = 0;\n if (duration > 0) {\n\n // rawProgress is normalized progress within the current iteration:\n // - first iteration: [0 .. 1]\n // - following iterations: (0 .. 1]\n //\n // iteration is the zero-based iteration index\n //\n // Examples (safeDuration = 3):\n // currentTimeMs = 0 → rawProgress = 0 , iteration = 0\n // currentTimeMs = 3 → rawProgress = 1 , iteration = 0\n // currentTimeMs = 3.0001 → rawProgress = 0.0001 , iteration = 1\n\n currentTimeMs = clamp(currentTimeMs, 0, iterations * safeDuration);\n\n iteration = Math.max(0, Math.ceil(currentTimeMs / safeDuration) - 1);\n iteration = Math.min(iteration, iterations - 1);\n\n // Time elapsed since the start of the current iteration\n const iterationTime = currentTimeMs - iteration * safeDuration;\n\n // Normalized progress (preserves fractional overflow after boundaries)\n rawProgress = clamp(iterationTime / safeDuration, 0, 1);\n } else {\n rawProgress = currentTimeMs; // We shouldn't be here\n }\n\n\n // compute per-iteration directional progress\n // start with baseProgress = rawProgress in [0,1)\n\n let baseProgress = rawProgress;\n // apply direction rules:\n // - normal: as is\n // - reverse: progress = 1 - baseProgress\n // - alternate: reverse on odd iterations\n // - alternate-reverse: reverse on even iterations\n const dir = direction || 'normal';\n let effectiveProgress = baseProgress;\n if (dir === 'reverse') {\n effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate') {\n if (iteration % 2 === 1) effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate-reverse') {\n // alternate-reverse: start reversed on iteration 0\n if (iteration % 2 === 0) effectiveProgress = 1 - baseProgress;\n } // else 'normal' -> keep\n return effectiveProgress;\n }\n let effectiveProgress = getEffectiveProgress(); // else 'normal' -> keep\n\n ////////////////////////////////////////////////////////////////\n\n // FIXME - pre-calc defs, then use\n\n for (const binding of bindings || []) {\n const animDef = binding.animate;\n if (!animDef || typeof animDef !== 'object' || Array.isArray(animDef)) {\n console.warn('Empty or unresolved binding', binding);\n continue;\n }\n\n // Calculate interpolated values for this binding's animation\n const computedValues = calcAnimationValues(animDef, effectiveProgress * duration);\n\n // Apply each computed attribute\n for (const [attrName, value] of Object.entries(computedValues)) {\n adapter.setAttribute(binding.id, attrName, value);\n }\n }\n }\n\n\n ////////////////////////////////////////////////////////////////\n\n const tick = () => {\n\n // if root element provided and detached, stop\n if (!adapter.isConnected()) {\n // stop animation loop (we'll let external code call play/resume)\n // do not call onFinish here. It's a detach situation.\n pauseAnim();\n return;\n }\n\n const currentTime = getAnimCurrentTime();\n\n // Delay phase (raw time < 0): the animation hasn't started yet.\n // With fill 'backwards'/'both' hold the first frame; otherwise leave\n // the element's static attributes untouched — mirrors WAAPI fill.\n // Checked BEFORE throttling so boundary states can't be skipped.\n const rawTime = getRawAnimTime();\n if (rawTime < 0 && playbackRate > 0) {\n if (fillsBackwards) renderFrame(0);\n return;\n }\n\n // Detect reverse playback reaching the start (natural end for rate < 0)\n // — mirrors WAAPI, where reverse playback fires `finish` at time 0.\n // pauseAnim runs FIRST (its trailing render must not clobber the\n // boundary frame rendered below).\n if (playbackRate < 0 && rawTime <= 0) {\n pauseAnim();\n renderFrame(0);\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Detect finished (natural end, forward playback)\n if (playbackRate > 0 && totalDuration && Number.isFinite(totalDuration) && currentTime >= (totalDuration as number)) {\n pauseAnim();\n // Render the end state — final frame when filling forwards, first\n // frame otherwise (closest frame-writer approximation of WAAPI\n // fill:'none'/'backwards', which reverts to the pre-animation state).\n renderFrame(fillsForwards ? (totalDuration as number) : 0);\n // call onFinish once\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Frame rate throttling: skip render if not enough time has passed.\n // Applies only to normal in-flight frames — boundary states above are\n // always rendered, so a throttled tick can never swallow the finish.\n if (minFrameIntervalMs > 0) {\n const now = Date.now();\n if (lastRenderTs && (now - lastRenderTs) < minFrameIntervalMs) {\n // Not enough time elapsed, skip this frame but continue the loop\n return;\n }\n lastRenderTs = now;\n }\n\n // Otherwise render normal frame\n renderFrame(currentTime);\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n if (config.delay) {\n if (config.delay < 0) {\n // Negative delay = seek into the animation; render the seeked frame.\n renderFrame(getAnimCurrentTime());\n } else if (fillsBackwards) {\n // Positive delay = wait before start; hold the first frame only\n // when filling backwards (WAAPI convention).\n renderFrame(0);\n }\n }\n\n ////////////////////////////////////////////////////////////////\n\n const _isPlaying = () => {\n if (!playing) return false;\n if (!adapter.isConnected()) { return false; }\n if (playbackRate < 0) {\n // Reverse playback finishes when it reaches the start.\n return getRawAnimTime() > 0;\n }\n if (Number.isFinite(totalDuration) && getAnimCurrentTime() >= (totalDuration as number)) return false;\n return true;\n };\n\n const loopAnim = (isFirst?: boolean) => {\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n\n if (!(isFirst || _isPlaying())) {\n // finished or not playing\n return;\n }\n\n timerId = requestFrame(() => {\n timerId = null;\n tick();\n loopAnim();\n });\n };\n\n const startAnim = () => {\n if (playing) return;\n\n // If the animation has already reached its natural end (for the current\n // playback direction), rewind to the opposite end before resuming —\n // mirrors the Web Animations API, where calling play() on a finished\n // animation auto-rewinds. Without this, resuming from the boundary\n // would re-finish on the first tick and leave the animation stuck.\n if (playbackRate >= 0) {\n if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= (totalDuration as number)) {\n timeBeforeLastStartMs = 0;\n }\n } else {\n // Reverse playback starting at (or before) the start: seek to the end.\n if (timeBeforeLastStartMs <= 0) {\n if (!Number.isFinite(totalDuration)) {\n // Cannot rewind to an infinite end (WAAPI throws here) —\n // warn and stay stopped instead of \"finishing\" instantly.\n console.warn('play: cannot start reverse playback of an infinite animation from time 0');\n return;\n }\n timeBeforeLastStartMs = totalDuration as number;\n }\n }\n\n // starting playback opens a new finish episode (mirrors WAAPI, where\n // play() always re-arms the finished promise / finish event)\n finishCalled = false;\n\n playing = true;\n lastStartedTs = Date.now();\n loopAnim(true);\n };\n\n const pauseAnim = () => {\n if (!playing) return;\n // Capture the RAW logical time — during the delay phase it is negative\n // and must stay negative, otherwise pausing would silently swallow the\n // remaining delay. Clamp only the top end.\n let raw = getRawAnimTime();\n if (Number.isFinite(totalDuration) && raw > (totalDuration as number)) raw = totalDuration as number;\n timeBeforeLastStartMs = raw;\n lastStartedTs = 0;\n playing = false;\n\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render a frame at the paused time to keep the DOM consistent —\n // except during the delay phase, where rendering would violate the\n // fill semantics (only 'backwards'/'both' show frame 0 before start).\n if (raw >= 0) {\n renderFrame(raw);\n } else if (fillsBackwards) {\n renderFrame(0);\n }\n };\n\n const cancelAnim = () => {\n pauseAnim();\n\n timeBeforeLastStartMs = 0;\n lastStartedTs = 0;\n playing = false;\n finishCalled = false;\n\n renderFrame(timeBeforeLastStartMs);\n\n callbacks?.onCancel?.();\n };\n\n const finishAnim = (callOnFinish = true) => {\n // jump to the end\n if (Number.isFinite(totalDuration)) {\n timeBeforeLastStartMs = totalDuration as number;\n } else {\n // infinity animations: set to current time (no-op)\n timeBeforeLastStartMs = getAnimCurrentTime();\n }\n lastStartedTs = 0;\n playing = false;\n // cancel any pending frame — pause/destroy early-return when not\n // playing, so a frame left queued here would never be cleaned up\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render the end state honoring `fill` (same rule as the natural\n // finish in `tick()`, and same as WAAPI where fill:'none' reverts even\n // after an explicit finish()).\n renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);\n\n if (callOnFinish && !finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n const api: PxAnimatorApi = {\n\n \"isReady\": () => true,\n\n \"getRootElement\": () => null,\n\n \"isPlaying\": (): boolean => { return _isPlaying(); },\n\n \"play\": () => {\n startAnim();\n callbacks?.onPlay?.();\n },\n \"pause\": () => {\n pauseAnim();\n callbacks?.onPause?.();\n },\n \"cancel\": () => {\n cancelAnim();\n // onCancel already called in cancelAnim\n },\n \"finish\": () => {\n finishAnim(true);\n },\n\n \"setPlaybackRate\": (rate: number) => {\n if (!isValidPlaybackRate(rate)) {\n console.warn(PX_RATE_REJECTED);\n return;\n }\n // Preserve the RAW logical time when changing rate — during the\n // delay phase it is negative and clamping it to 0 would skip the\n // remaining delay. Clamp only the top end.\n let current = getRawAnimTime();\n if (Number.isFinite(totalDuration) && current > (totalDuration as number)) current = totalDuration as number;\n // a direction change opens a new finish episode\n if ((rate < 0) !== (playbackRate < 0)) finishCalled = false;\n playbackRate = rate;\n timeBeforeLastStartMs = current;\n if (playing) lastStartedTs = Date.now();\n },\n\n \"getCurrentTime\": (): number | null => { return getAnimCurrentTime(); },\n\n \"setCurrentTime\": (newTime: number) => {\n // One clamp rule for every engine (review §3).\n newTime = clampSeekMs(newTime, totalDuration as number);\n\n timeBeforeLastStartMs = newTime;\n if (playing) lastStartedTs = Date.now();\n // seeking re-arms the finish notification (mirrors WAAPI)\n if (!Number.isFinite(totalDuration) || newTime < (totalDuration as number)) finishCalled = false;\n // render immediately to reflect the change\n renderFrame(getAnimCurrentTime());\n },\n\n \"getCurrentProgress\": (): number | null => timeToProgress(getAnimCurrentTime(), duration, iterations),\n\n \"setCurrentProgress\": (progress: number) => {\n api.setCurrentTime(progressToTimeMs(progress, duration, iterations));\n },\n\n \"destroy\": () => {\n api.cancel();\n callbacks?.onRemove?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n return api;\n}\n\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * PER-INSTANCE CONFIG OVERRIDE.\n *\n * One document, played twice on a page with different timing — without editing the document.\n * The host supplies a partial `animator` config; this merges it over the document's own.\n *\n * Base semantics are JSON Merge Patch (RFC 7386): objects merge per level, primitives and\n * arrays REPLACE, and `null` DELETES. `null` is the right sentinel because it is not a legal\n * value anywhere in the schema, and deleting is the only way to restore a meaningful ABSENCE\n * (`timeline.type` absent = time, `mode` absent = auto, `fillMode` absent = forwards).\n *\n * Six places need more than that — see the RULE comments below.\n *\n * WHY THE WIRE FORM, not the flat runtime view: `nestAnimatorTimeline` is a no-op on anything\n * that already carries `timeline`, and the round-trip is lossy (`iterations:'infinite'` on the\n * scroll branch, `pin:false`, and an empty time block emitting no `timeline` at all). Merging\n * on the nested form is the only place those values mean what they say.\n */\nimport { PX_TIMELINE_SHARED_KEYS, PX_TIME_ONLY_TIMELINE_KEYS } from '../format/PxAnimatorConstants';\nimport type { PxTriggerStart } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxAnimatorConfig } from '../format/PxAnimatorTypes';\n\n/** A deep-partial of the WIRE animator config; `null` at any slot deletes it. @public */\nexport type PxAnimatorConfigPatch = Record<string, any> | null;\n\n/** @public */\nexport interface PxAnimatorConfigMergeResult {\n /** The merged WIRE config, or `undefined` when there is nothing left of it. */\n config: PxAnimatorConfig | undefined;\n /** Human-readable problems, `path: what is wrong`. Empty when the merge was clean. */\n warnings: Array<string>;\n}\n\n/** Keys that live only on the flat RUNTIME view and have no slot on the wire. */\nconst FLAT_ONLY_KEYS = [\n 'timelineSource', 'scroll', 'trigger', 'delay', 'iterations',\n 'direction', 'fill', 'resetOnFinish', 'duration', 'mode', 'frameRate',\n];\n\n/** The lookup tables and the bindings. They are animation CONTENT, not playback, and are never reset. */\nconst CONTENT_KEYS = ['definitions', 'bindings'];\n\nconst isPlainObject = (v: unknown): v is Record<string, any> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Absent `type` means the time-driven timeline, so compare the normalized values (RULE 2). */\nconst timelineTypeOf = (t: unknown): string =>\n (isPlainObject(t) && typeof t.type === 'string') ? t.type : 'time';\n\nconst isScrollish = (type: string): boolean => type === 'scroll' || type === 'view';\n\n/**\n * RFC 7386 merge with no special cases. Used for every sub-object that has no rule of its own\n * (`trigger`, `range`, `definitions.fonts`, …), which is why patching one font leaves its\n * siblings alone. Arrays are values: a patched `bindings` list replaces the document's whole list.\n */\nfunction mergePlain(base: unknown, patch: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; } // RULE 7: null deletes\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value; // RULE 6: arrays/primitives replace\n }\n return out;\n}\n\n/**\n * The timeline, which is a discriminated union and therefore cannot be merged blindly.\n *\n * RULE 1 — the patch names a DIFFERENT type: replace the timeline outright, carrying over only\n * the keys both members share. Merging instead would strand `trigger`/`delay` on a\n * scroll timeline, where the format has no slot for them.\n * RULE 2 — same type, or the patch does not mention one: merge. A patch that spells\n * `type: 'time'` against a document with an ABSENT type must count as a match, hence\n * the normalization above.\n * RULE 3 — time-only keys landing on a scroll/view timeline are dropped with a warning, the\n * same way the wire has no slot for them.\n * RULE 4 — `iterations: 'infinite'` cannot map onto a scroll range.\n */\nfunction mergeTimeline(base: unknown, patch: Record<string, any>, warn: (m: string) => void): Record<string, any> {\n const baseType = timelineTypeOf(base);\n const patchNamesType = isPlainObject(patch) && typeof patch.type === 'string';\n const patchType = patchNamesType ? String(patch.type) : baseType;\n\n let merged: Record<string, any>;\n if (patchType !== baseType) {\n // RULE 1\n const carried: Record<string, any> = {};\n if (isPlainObject(base)) {\n for (const k of PX_TIMELINE_SHARED_KEYS) {\n if (base[k] !== undefined) carried[k] = base[k];\n }\n }\n merged = mergePlain(carried, patch);\n } else {\n merged = mergePlain(base, patch); // RULE 2\n }\n\n if (isScrollish(patchType)) {\n for (const k of PX_TIME_ONLY_TIMELINE_KEYS) { // RULE 3\n if (merged[k] === undefined) continue;\n warn('animator.timeline.' + k + \": no slot on a '\" + patchType + \"' timeline — dropped\");\n delete merged[k];\n }\n if (merged.iterations === 'infinite') { // RULE 4\n warn(\"animator.timeline.iterations: 'infinite' cannot map onto a scroll range — dropped\");\n delete merged.iterations;\n }\n }\n return merged;\n}\n\n/** A deep-partial of the WIRE `timeline` block; `null` at any slot deletes it. @public */\nexport type PxTimelinePatch = Record<string, any> | null;\n\n/** The four flat shortcuts every surface offers for the keys people reach for most. @public */\nexport interface PxTimelineShortcuts {\n /** Shortcut for `timeline.duration` — one iteration, ms. Wins over the same key in `timeline`. */\n duration?: number;\n /** Shortcut for `timeline.delay` — the wait before the first iteration, ms. */\n delay?: number;\n /** Shortcut for `timeline.iterations`; `'infinite'` never stops. */\n iterations?: number | 'infinite';\n /**\n * Shortcut for `timeline.trigger.start`. Typed from the WIRE, so it includes\n * `'none'` — \"nothing starts this but a `play()` call\".\n */\n start?: PxTriggerStart;\n}\n\n/**\n * The playback-override props every surface takes — `createAnimator` and the three components:\n * the document's `timeline` as a patch, the reset flag, and the four shortcuts above.\n *\n * ONE definition (review §9): React and React Native extend it, Vue derives its internal shape\n * from it, `createAnimator`'s options extend it.\n * @public\n */\nexport interface PxPlaybackOverride extends PxTimelineShortcuts {\n /**\n * Per-instance override of the document's `animator.timeline` — the same shape as `timeline`\n * in docs/format/README.md, deep-merged over what the document says, so one file can play twice on a page\n * with different timing. `null` at any slot DELETES that key, restoring the default its\n * absence means. Also accepts a JSON STRING, which survives a build that mangles object keys.\n *\n * `timeline` is the whole useful override surface: the rest of the `animator` block is\n * content (`definitions`, `bindings`), a version stamp and a debug handle — none of which\n * a per-instance override should touch. That is why this is not a wrapper object.\n */\n timeline?: PxTimelinePatch | string;\n /**\n * Ignore the document's own timeline and start from the player's DEFAULT timeline, with\n * `timeline` on top. `definitions` and `bindings` are content and are kept either way.\n */\n resetTimeline?: boolean;\n}\n\n/**\n * Folds the four shortcuts into the `timeline` patch, accepts the JSON-STRING form of the patch\n * (immune to property mangling — see docs/library/minification.md), and returns it in the shape\n * `applyAnimatorConfig` takes: an animator-level patch `{ timeline: … }`.\n *\n * A shortcut WINS over the same key inside the object: more specific beats more general, the\n * way an inline style beats a stylesheet. One implementation so every surface agrees.\n * @public\n */\nexport function foldTimelineOverride(\n timeline: PxTimelinePatch | string | undefined,\n shortcuts: PxTimelineShortcuts,\n): PxAnimatorConfigPatch | undefined {\n let base: PxTimelinePatch | undefined;\n if (typeof timeline === 'string') {\n try {\n base = JSON.parse(timeline);\n } catch (e) {\n console.warn('timeline override: not valid JSON — ignored', e);\n base = undefined;\n }\n } else {\n base = timeline ?? undefined;\n }\n\n const { duration, delay, iterations, start } = shortcuts;\n if (duration === undefined && delay === undefined && iterations === undefined && start === undefined) {\n return base === undefined ? undefined : { timeline: base };\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n if (duration !== undefined) out.duration = duration;\n if (delay !== undefined) out.delay = delay;\n if (iterations !== undefined) out.iterations = iterations;\n if (start !== undefined) {\n out.trigger = { ...(out.trigger as Record<string, any> | undefined), start };\n }\n return { timeline: out };\n}\n\n/**\n * Merge a patch over an animator config. PURE — neither argument is mutated, and the result is\n * always a NEW object, which also matters because `flattenAnimatorTimeline` memoises on config\n * identity: mutating in place would hand every later reader the pre-merge view.\n * @public\n */\nexport function mergeAnimatorConfig(\n base: PxAnimatorConfig | undefined,\n patch: PxAnimatorConfigPatch,\n): PxAnimatorConfigMergeResult {\n const warnings: Array<string> = [];\n const warn = (m: string) => warnings.push(m);\n\n if (patch === null) return { config: undefined, warnings };\n if (!isPlainObject(patch) || Object.keys(patch).length === 0) {\n return { config: base, warnings }; // nothing to do: same identity\n }\n\n // RULE 5 — a base in the flat runtime spelling cannot take a wire patch soundly:\n // `flattenAnimatorTimeline` copies the flat keys first and then overwrites them from\n // `timeline`, so a flat `delay` would survive a `timeline.delay: null` deletion.\n const flatInBase = isPlainObject(base) ? FLAT_ONLY_KEYS.filter(k => (base as any)[k] !== undefined) : [];\n if (flatInBase.length) {\n warn('animator: the base carries the flat runtime spelling (' + flatInBase.join(', ') + '); '\n + 'the patch merges the wire spelling only');\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; }\n if (key === 'timeline') {\n out.timeline = isPlainObject(value) ? mergeTimeline(out.timeline, value, warn) : value;\n continue;\n }\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value;\n }\n return { config: out as PxAnimatorConfig, warnings };\n}\n\n/**\n * Document level: resolves the two canonical addresses of the animator config and returns a NEW\n * document that shares every untouched subtree by reference.\n *\n * `resetTimeline` starts from the player's own defaults instead of the document's playback\n * settings — \"play this file as if it said nothing about timing\". The lookup tables are kept\n * either way: resetting `definitions`/`bindings` would leave an animation with nothing to\n * animate, which is never what a caller means.\n * @public\n */\nexport function applyAnimatorConfig(\n doc: PxAnimatedSvgDocument,\n patch: PxAnimatorConfigPatch,\n options?: { resetTimeline?: boolean },\n): { doc: PxAnimatedSvgDocument; warnings: Array<string> } {\n const reset = !!options?.resetTimeline;\n if (!doc || (patch === undefined || (!reset && (patch === null || !isPlainObject(patch) || !Object.keys(patch).length)))) {\n return { doc, warnings: [] };\n }\n\n const anyDoc = doc as any;\n const atRoot = isPlainObject(anyDoc.animator);\n const atMeta = !atRoot && isPlainObject(anyDoc.meta?.animator);\n const current: PxAnimatorConfig | undefined = atRoot ? anyDoc.animator\n : atMeta ? anyDoc.meta.animator\n : undefined;\n\n const warnings: Array<string> = [];\n if (atRoot && isPlainObject(anyDoc.meta?.animator)) {\n warnings.push('animator: doc.meta.animator is shadowed by doc.animator and was not patched');\n }\n\n let base = current;\n if (reset) {\n // Keep only the content tables; everything else starts from the player's defaults.\n const kept: Record<string, any> = {};\n for (const k of CONTENT_KEYS) {\n if (isPlainObject(current) && (current as any)[k] !== undefined) kept[k] = (current as any)[k];\n }\n base = kept as PxAnimatorConfig;\n }\n\n const merged = mergeAnimatorConfig(base, patch ?? {});\n warnings.push(...merged.warnings);\n if (merged.config === current) return { doc, warnings };\n\n if (atMeta) {\n return {\n doc: { ...anyDoc, meta: { ...anyDoc.meta, animator: merged.config } } as PxAnimatedSvgDocument,\n warnings,\n };\n }\n return { doc: { ...anyDoc, animator: merged.config } as PxAnimatedSvgDocument, warnings };\n}\n"],"mappings":"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","names":["effectiveProgress","_a"]}
|
|
1
|
+
{"version":3,"sources":["../src/playback/PxPlaybackTime.ts","../src/playback/PxFrameLoop.ts","../src/playback/PxAnimatorConfigPatch.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ONE time contract for every engine (API review §3). The same three calls used to mean three\n// different things:\n//\n// | engine | getCurrentTime() | setCurrentTime(t) | setPlaybackRate(0) |\n// |-----------------|-------------------------|-------------------|--------------------|\n// | web, WAAPI | ms across the whole run | NOT clamped | accepted |\n// | web, frame loop | ms across the whole run | clamped | rejected + warn |\n// | React Native | ms within ONE iteration | wrapped | rejected + warn |\n//\n// So a slider built on `getCurrentTime()` jumped back every iteration on React Native, and\n// `timeline.engine: 'auto'` meant two documents on one page could answer the same call\n// differently. The contract every player now implements:\n//\n// - time is ms from the start of the WHOLE run, iterations included — never per-iteration;\n// - a seek clamps to [0, seekCeilingMs];\n// - a rate of 0 is rejected everywhere, with this one message.\n\n/** The one message every engine prints for a rejected rate. @public @advanced */\nexport const PX_RATE_REJECTED = 'setPlaybackRate: rate must be finite and non-zero';\n\n/**\n * A playback rate is usable when it is finite and non-zero.\n *\n * 0 is rejected rather than accepted: it freezes the animation in a state indistinguishable\n * from a stuck player, and `pause()` already says that properly. Two of the three engines\n * rejected it already — this makes the third agree.\n * @public @advanced\n */\nexport function isValidPlaybackRate(rate: number): boolean {\n return Number.isFinite(rate) && rate !== 0;\n}\n\n/**\n * Highest seekable time, ms — `duration × iterations`.\n *\n * `Infinity` for an endless timeline, so callers must test `Number.isFinite` before using it\n * as an upper bound. This is the SEEK ceiling, which is deliberately not the same thing as the\n * span `progress` covers — see `progressSpanMs`.\n * @public @advanced\n */\nexport function seekCeilingMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return Infinity;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * The span `progress` 0→1 covers, ms.\n *\n * An endless timeline maps progress onto ONE iteration — the rule the components already\n * document for the `progress` prop (\"0–1 of duration × iterations, one iteration when\n * iterations is 'infinite'\"). Always finite, so it is safe as a divisor.\n * @public @advanced\n */\nexport function progressSpanMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return durationMs;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * Clamps a seek into [0, ceiling].\n *\n * `NaN` and anything below 0 land on 0. `Infinity` means \"the end\", so it clamps to the ceiling\n * like any other overshoot — except on an endless timeline, where there is no end to land on and\n * a non-finite playhead would poison every later read, so that reads as 0.\n * @public @advanced\n */\nexport function clampSeekMs(timeMs: number, ceilingMs: number): number {\n if (Number.isNaN(timeMs) || timeMs < 0) return 0;\n if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;\n return Number.isFinite(timeMs) ? timeMs : 0;\n}\n\n/**\n * Whole-run ms → 0–1.\n *\n * A finite timeline clamps at both ends. An endless one wraps within the current iteration,\n * so the value stays meaningful however long it has been running. A zero-length span reads as\n * 0 rather than NaN.\n * @public @advanced\n */\nexport function timeToProgress(timeMs: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(timeMs)) return 0;\n if (timeMs <= 0) return 0;\n if (iterations === Infinity) return (timeMs % span) / span;\n return timeMs >= span ? 1 : timeMs / span;\n}\n\n/** 0–1 → whole-run ms, clamped into the span. A non-finite progress reads as 0. @public @advanced */\nexport function progressToTimeMs(progress: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(progress)) return 0;\n if (progress <= 0) return 0;\n return progress >= 1 ? span : progress * span;\n}\n\n/** A playhead that keeps whole-run time across iterations. See `createRunClock`. @public @advanced */\nexport interface PxRunClock {\n /** Whole-run position right now, ms, clamped to the ceiling. */\n now(): number;\n /** True while time is advancing. */\n isRunning(): boolean;\n /** Start or resume at `rate`, optionally from a given whole-run time. */\n start(rate: number, atMs?: number): void;\n /** Stop advancing, freezing the current position. */\n stop(): void;\n /** Jump to a whole-run time; keeps running if it already was. */\n seek(ms: number): void;\n}\n\n/**\n * A whole-run playhead that survives repetition.\n *\n * React Native drives playback with Reanimated's `withRepeat`, whose shared value only ever\n * holds the position WITHIN one iteration and which never reports how many iterations have\n * elapsed. Whole-run time therefore cannot be recovered from it: under `alternate` the value\n * runs backwards rather than wrapping, which is indistinguishable from a negative rate. So the\n * time is kept on a clock of its own — the same thing the frame-loop engine does inline.\n *\n * `nowFn` is injectable so this is testable without real time passing.\n * @public @advanced\n */\nexport function createRunClock(ceilingMs: number, nowFn: () => number = Date.now): PxRunClock {\n let baseMs = 0; // whole-run ms as of the last start/seek/stop\n let startedAt = 0; // nowFn() when running began\n let running = false; // a FLAG, not `startedAt !== 0`: a time source may legitimately\n let rate = 1; // read 0, and `Date.now()` never does — so the bug would hide.\n\n const value = (): number => running\n ? clampSeekMs(baseMs + (nowFn() - startedAt) * rate, ceilingMs)\n : clampSeekMs(baseMs, ceilingMs);\n\n return {\n now: value,\n isRunning: () => running,\n start: (r: number, atMs?: number) => {\n baseMs = clampSeekMs(atMs ?? value(), ceilingMs);\n rate = isValidPlaybackRate(r) ? r : 1;\n startedAt = nowFn();\n running = true;\n },\n stop: () => {\n baseMs = value();\n startedAt = 0;\n running = false;\n },\n seek: (ms: number) => {\n baseMs = clampSeekMs(ms, ceilingMs);\n if (running) startedAt = nowFn();\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxAnimatedSvgDocument, type PxAnimatorApi, type PxEngineCallbacks } from '../format/PxAnimatorTypes';\nimport { getAnimatorConfig, PX_DEFAULT_ITERATIONS, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport { PxTimeTimelineSchema } from '../format/PxAnimatorTypes';\nimport { camelCaseToKebabWordIfNeeded, clamp, PX_DEFAULT_DURATION_MS, PX_STYLE_ATTR_NAMES } from '../util/PxAnimatorUtil';\nimport { calcAnimationValues, normalizeBindings } from '../animation/PxDefinitions';\nimport { clampSeekMs, isValidPlaybackRate, progressToTimeMs, PX_RATE_REJECTED, timeToProgress } from './PxPlaybackTime';\n\n\n// Frame scheduling — resolved LAZILY from globalThis on every call so test\n// harnesses that install fake timers (and hosts that polyfill rAF late) are\n// honored. Falls back to a ~60fps setTimeout when rAF is unavailable.\nfunction requestFrame(cb: () => void): number {\n const g: any = globalThis as any;\n if (typeof g.requestAnimationFrame === 'function') return g.requestAnimationFrame(cb);\n return g.setTimeout(cb, 16) as unknown as number;\n}\n\nfunction cancelFrame(handle: number): void {\n const g: any = globalThis as any;\n if (typeof g.cancelAnimationFrame === 'function') { g.cancelAnimationFrame(handle); return; }\n g.clearTimeout(handle);\n}\n\n/**\n * Platform adapter interface for abstracting platform-specific operations.\n * @public\n */\nexport interface PxPlatformAdapter {\n\n /** Check if the root element is still connected/mounted */\n isConnected(): boolean;\n\n /** Set an attribute on an element by id */\n setAttribute(id: string, attrName: string, value: string): void;\n}\n\n/**\n * Creates an animator instance that uses a frame loop for animations.\n * This is the abstract/platform-agnostic version.\n *\n * @param adapter Platform adapter for DOM/environment operations.\n * @param callbacks Optional lifecycle callbacks.\n * @returns A PxAnimatorApi instance.\n * @public @advanced\n */\nexport function createAdapterAnimator(\n doc: PxAnimatedSvgDocument,\n adapter: PxPlatformAdapter,\n callbacks?: PxEngineCallbacks\n): PxAnimatorApi {\n\n const config = getAnimatorConfig(doc) || {};\n\n const bindings = normalizeBindings(doc, PxTimelineEngine.js);\n\n // iterations: either number or Infinity\n const _iterations = config.iterations;\n let iterations: number = 1;\n if (typeof _iterations === 'number') iterations = _iterations || PX_DEFAULT_ITERATIONS;\n if (_iterations === 'infinite') iterations = Infinity;\n if (iterations < 1) iterations = 1;\n\n const duration = +(config.duration || PX_DEFAULT_DURATION_MS); // per-iteration duration (ms), cannot be 0!\n const totalDuration = duration && iterations ?\n duration * (iterations === Infinity ? Infinity : iterations) :\n (duration ? (iterations ?? PX_DEFAULT_ITERATIONS) * duration : 0);\n\n // direction handling\n // What an omitted field means is the SCHEMA's statement — read, not restated here.\n const direction = config.direction || PxTimeTimelineSchema.defaults.direction; // 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'\n\n // fill handling — mirrors the Web Animations API `fill` semantics as closely\n // as a frame-writer can. Default 'forwards' (hold final state) matches the\n // waapi engine and the config doc.\n const fill = config.fill ?? PxTimeTimelineSchema.defaults.fillMode;\n const fillsForwards = fill === 'forwards' || fill === 'both';\n const fillsBackwards = fill === 'backwards' || fill === 'both';\n\n ////////////////////////////////////////////////////////////////\n\n let timerId: number | null = null;\n let playing = false;\n\n // Accumulated logical time before the current run (ms). A positive\n // config.delay means \"wait before starting\" → the animation starts at\n // NEGATIVE logical time and reaches 0 after `delay` ms (same convention as\n // the Web Animations API). A negative config.delay means \"seek into the\n // animation\" → positive start time.\n let timeBeforeLastStartMs = -(config.delay || 0);\n let lastStartedTs = 0; // timestamp when last started/resumed\n let playbackRate = 1;\n\n let finishCalled = false; // ensures onFinish called once\n\n // Frame rate throttling\n const frameRate = config.frameRate;\n const minFrameIntervalMs = frameRate && frameRate > 0 ? 1000 / frameRate : 0;\n let lastRenderTs = 0; // timestamp of last render\n\n // Raw logical time — may be negative during the delay phase; not clamped\n // to the [0, totalDuration] range at the top end either. Internal use only.\n const getRawAnimTime = () => {\n // compute effective elapsed time (ms), taking playbackRate into account\n const runningElapsed = lastStartedTs ? (Date.now() - lastStartedTs) * playbackRate : 0;\n return timeBeforeLastStartMs + runningElapsed;\n };\n\n const getAnimCurrentTime = () => {\n let time = getRawAnimTime();\n\n // clamp to [0, totalDuration]\n if (Number.isFinite(totalDuration) && time > (totalDuration as number)) time = totalDuration as number;\n if (time < 0) time = 0;\n return time;\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n // separate render function that uses a given currentTime (ms)\n function renderFrame(currentTimeMs: number) {\n\n function getEffectiveProgress() {\n // If no duration or duration === 0, set iteration/progress accordingly\n const safeDuration = duration > 0 ? duration : 1; // avoid division by zero\n\n let rawProgress = 0;\n let iteration = 0;\n if (duration > 0) {\n\n // rawProgress is normalized progress within the current iteration:\n // - first iteration: [0 .. 1]\n // - following iterations: (0 .. 1]\n //\n // iteration is the zero-based iteration index\n //\n // Examples (safeDuration = 3):\n // currentTimeMs = 0 → rawProgress = 0 , iteration = 0\n // currentTimeMs = 3 → rawProgress = 1 , iteration = 0\n // currentTimeMs = 3.0001 → rawProgress = 0.0001 , iteration = 1\n\n currentTimeMs = clamp(currentTimeMs, 0, iterations * safeDuration);\n\n iteration = Math.max(0, Math.ceil(currentTimeMs / safeDuration) - 1);\n iteration = Math.min(iteration, iterations - 1);\n\n // Time elapsed since the start of the current iteration\n const iterationTime = currentTimeMs - iteration * safeDuration;\n\n // Normalized progress (preserves fractional overflow after boundaries)\n rawProgress = clamp(iterationTime / safeDuration, 0, 1);\n } else {\n rawProgress = currentTimeMs; // We shouldn't be here\n }\n\n\n // compute per-iteration directional progress\n // start with baseProgress = rawProgress in [0,1)\n\n let baseProgress = rawProgress;\n // apply direction rules:\n // - normal: as is\n // - reverse: progress = 1 - baseProgress\n // - alternate: reverse on odd iterations\n // - alternate-reverse: reverse on even iterations\n const dir = direction || 'normal';\n let effectiveProgress = baseProgress;\n if (dir === 'reverse') {\n effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate') {\n if (iteration % 2 === 1) effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate-reverse') {\n // alternate-reverse: start reversed on iteration 0\n if (iteration % 2 === 0) effectiveProgress = 1 - baseProgress;\n } // else 'normal' -> keep\n return effectiveProgress;\n }\n let effectiveProgress = getEffectiveProgress(); // else 'normal' -> keep\n\n ////////////////////////////////////////////////////////////////\n\n // FIXME - pre-calc defs, then use\n\n for (const binding of bindings || []) {\n const animDef = binding.animate;\n if (!animDef || typeof animDef !== 'object' || Array.isArray(animDef)) {\n console.warn('Empty or unresolved binding', binding);\n continue;\n }\n\n // Calculate interpolated values for this binding's animation\n const computedValues = calcAnimationValues(animDef, effectiveProgress * duration);\n\n // Apply each computed attribute\n for (const [attrName, value] of Object.entries(computedValues)) {\n adapter.setAttribute(binding.id, attrName, value);\n }\n }\n }\n\n\n ////////////////////////////////////////////////////////////////\n\n const tick = () => {\n\n // if root element provided and detached, stop\n if (!adapter.isConnected()) {\n // stop animation loop (we'll let external code call play/resume)\n // do not call onFinish here. It's a detach situation.\n pauseAnim();\n return;\n }\n\n const currentTime = getAnimCurrentTime();\n\n // Delay phase (raw time < 0): the animation hasn't started yet.\n // With fill 'backwards'/'both' hold the first frame; otherwise leave\n // the element's static attributes untouched — mirrors WAAPI fill.\n // Checked BEFORE throttling so boundary states can't be skipped.\n const rawTime = getRawAnimTime();\n if (rawTime < 0 && playbackRate > 0) {\n if (fillsBackwards) renderFrame(0);\n return;\n }\n\n // Detect reverse playback reaching the start (natural end for rate < 0)\n // — mirrors WAAPI, where reverse playback fires `finish` at time 0.\n // pauseAnim runs FIRST (its trailing render must not clobber the\n // boundary frame rendered below).\n if (playbackRate < 0 && rawTime <= 0) {\n pauseAnim();\n renderFrame(0);\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Detect finished (natural end, forward playback)\n if (playbackRate > 0 && totalDuration && Number.isFinite(totalDuration) && currentTime >= (totalDuration as number)) {\n pauseAnim();\n // Render the end state — final frame when filling forwards, first\n // frame otherwise (closest frame-writer approximation of WAAPI\n // fill:'none'/'backwards', which reverts to the pre-animation state).\n renderFrame(fillsForwards ? (totalDuration as number) : 0);\n // call onFinish once\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Frame rate throttling: skip render if not enough time has passed.\n // Applies only to normal in-flight frames — boundary states above are\n // always rendered, so a throttled tick can never swallow the finish.\n if (minFrameIntervalMs > 0) {\n const now = Date.now();\n if (lastRenderTs && (now - lastRenderTs) < minFrameIntervalMs) {\n // Not enough time elapsed, skip this frame but continue the loop\n return;\n }\n lastRenderTs = now;\n }\n\n // Otherwise render normal frame\n renderFrame(currentTime);\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n if (config.delay) {\n if (config.delay < 0) {\n // Negative delay = seek into the animation; render the seeked frame.\n renderFrame(getAnimCurrentTime());\n } else if (fillsBackwards) {\n // Positive delay = wait before start; hold the first frame only\n // when filling backwards (WAAPI convention).\n renderFrame(0);\n }\n }\n\n ////////////////////////////////////////////////////////////////\n\n const _isPlaying = () => {\n if (!playing) return false;\n if (!adapter.isConnected()) { return false; }\n if (playbackRate < 0) {\n // Reverse playback finishes when it reaches the start.\n return getRawAnimTime() > 0;\n }\n if (Number.isFinite(totalDuration) && getAnimCurrentTime() >= (totalDuration as number)) return false;\n return true;\n };\n\n const loopAnim = (isFirst?: boolean) => {\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n\n if (!(isFirst || _isPlaying())) {\n // finished or not playing\n return;\n }\n\n timerId = requestFrame(() => {\n timerId = null;\n tick();\n loopAnim();\n });\n };\n\n const startAnim = () => {\n if (playing) return;\n\n // If the animation has already reached its natural end (for the current\n // playback direction), rewind to the opposite end before resuming —\n // mirrors the Web Animations API, where calling play() on a finished\n // animation auto-rewinds. Without this, resuming from the boundary\n // would re-finish on the first tick and leave the animation stuck.\n if (playbackRate >= 0) {\n if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= (totalDuration as number)) {\n timeBeforeLastStartMs = 0;\n }\n } else {\n // Reverse playback starting at (or before) the start: seek to the end.\n if (timeBeforeLastStartMs <= 0) {\n if (!Number.isFinite(totalDuration)) {\n // Cannot rewind to an infinite end (WAAPI throws here) —\n // warn and stay stopped instead of \"finishing\" instantly.\n console.warn('play: cannot start reverse playback of an infinite animation from time 0');\n return;\n }\n timeBeforeLastStartMs = totalDuration as number;\n }\n }\n\n // starting playback opens a new finish episode (mirrors WAAPI, where\n // play() always re-arms the finished promise / finish event)\n finishCalled = false;\n\n playing = true;\n lastStartedTs = Date.now();\n loopAnim(true);\n };\n\n const pauseAnim = () => {\n if (!playing) return;\n // Capture the RAW logical time — during the delay phase it is negative\n // and must stay negative, otherwise pausing would silently swallow the\n // remaining delay. Clamp only the top end.\n let raw = getRawAnimTime();\n if (Number.isFinite(totalDuration) && raw > (totalDuration as number)) raw = totalDuration as number;\n timeBeforeLastStartMs = raw;\n lastStartedTs = 0;\n playing = false;\n\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render a frame at the paused time to keep the DOM consistent —\n // except during the delay phase, where rendering would violate the\n // fill semantics (only 'backwards'/'both' show frame 0 before start).\n if (raw >= 0) {\n renderFrame(raw);\n } else if (fillsBackwards) {\n renderFrame(0);\n }\n };\n\n const cancelAnim = () => {\n pauseAnim();\n\n timeBeforeLastStartMs = 0;\n lastStartedTs = 0;\n playing = false;\n finishCalled = false;\n\n renderFrame(timeBeforeLastStartMs);\n\n callbacks?.onCancel?.();\n };\n\n const finishAnim = (callOnFinish = true) => {\n // jump to the end\n if (Number.isFinite(totalDuration)) {\n timeBeforeLastStartMs = totalDuration as number;\n } else {\n // infinity animations: set to current time (no-op)\n timeBeforeLastStartMs = getAnimCurrentTime();\n }\n lastStartedTs = 0;\n playing = false;\n // cancel any pending frame — pause/destroy early-return when not\n // playing, so a frame left queued here would never be cleaned up\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render the end state honoring `fill` (same rule as the natural\n // finish in `tick()`, and same as WAAPI where fill:'none' reverts even\n // after an explicit finish()).\n renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);\n\n if (callOnFinish && !finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n const api: PxAnimatorApi = {\n\n \"isReady\": () => true,\n\n \"getRootElement\": () => null,\n\n \"isPlaying\": (): boolean => { return _isPlaying(); },\n\n \"play\": () => {\n startAnim();\n callbacks?.onPlay?.();\n },\n \"pause\": () => {\n pauseAnim();\n callbacks?.onPause?.();\n },\n \"cancel\": () => {\n cancelAnim();\n // onCancel already called in cancelAnim\n },\n \"finish\": () => {\n finishAnim(true);\n },\n\n \"setPlaybackRate\": (rate: number) => {\n if (!isValidPlaybackRate(rate)) {\n console.warn(PX_RATE_REJECTED);\n return;\n }\n // Preserve the RAW logical time when changing rate — during the\n // delay phase it is negative and clamping it to 0 would skip the\n // remaining delay. Clamp only the top end.\n let current = getRawAnimTime();\n if (Number.isFinite(totalDuration) && current > (totalDuration as number)) current = totalDuration as number;\n // a direction change opens a new finish episode\n if ((rate < 0) !== (playbackRate < 0)) finishCalled = false;\n playbackRate = rate;\n timeBeforeLastStartMs = current;\n if (playing) lastStartedTs = Date.now();\n },\n\n \"getCurrentTime\": (): number | null => { return getAnimCurrentTime(); },\n\n \"setCurrentTime\": (newTime: number) => {\n // One clamp rule for every engine (review §3).\n newTime = clampSeekMs(newTime, totalDuration as number);\n\n timeBeforeLastStartMs = newTime;\n if (playing) lastStartedTs = Date.now();\n // seeking re-arms the finish notification (mirrors WAAPI)\n if (!Number.isFinite(totalDuration) || newTime < (totalDuration as number)) finishCalled = false;\n // render immediately to reflect the change\n renderFrame(getAnimCurrentTime());\n },\n\n \"getCurrentProgress\": (): number | null => timeToProgress(getAnimCurrentTime(), duration, iterations),\n\n \"setCurrentProgress\": (progress: number) => {\n api.setCurrentTime(progressToTimeMs(progress, duration, iterations));\n },\n\n \"destroy\": () => {\n api.cancel();\n callbacks?.onRemove?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n return api;\n}\n\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * PER-INSTANCE CONFIG OVERRIDE.\n *\n * One document, played twice on a page with different timing — without editing the document.\n * The host supplies a partial `animator` config; this merges it over the document's own.\n *\n * Base semantics are JSON Merge Patch (RFC 7386): objects merge per level, primitives and\n * arrays REPLACE, and `null` DELETES. `null` is the right sentinel because it is not a legal\n * value anywhere in the schema, and deleting is the only way to restore a meaningful ABSENCE\n * (`timeline.type` absent = time, `mode` absent = auto, `fillMode` absent = forwards).\n *\n * Six places need more than that — see the RULE comments below.\n *\n * WHY THE WIRE FORM, not the flat runtime view: `nestAnimatorTimeline` is a no-op on anything\n * that already carries `timeline`, and the round-trip is lossy (`iterations:'infinite'` on the\n * scroll branch, `pin:false`, and an empty time block emitting no `timeline` at all). Merging\n * on the nested form is the only place those values mean what they say.\n */\nimport { PX_TIMELINE_SHARED_KEYS, PX_TIME_ONLY_TIMELINE_KEYS } from '../format/PxAnimatorConstants';\nimport type { PxTriggerStart } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxAnimatorConfig } from '../format/PxAnimatorTypes';\n\n/** A deep-partial of the WIRE animator config; `null` at any slot deletes it. @public */\nexport type PxAnimatorConfigPatch = Record<string, any> | null;\n\n/** @public */\nexport interface PxAnimatorConfigMergeResult {\n /** The merged WIRE config, or `undefined` when there is nothing left of it. */\n config: PxAnimatorConfig | undefined;\n /** Human-readable problems, `path: what is wrong`. Empty when the merge was clean. */\n warnings: Array<string>;\n}\n\n/** Keys that live only on the flat RUNTIME view and have no slot on the wire. */\nconst FLAT_ONLY_KEYS = [\n 'timelineSource', 'scroll', 'trigger', 'delay', 'iterations',\n 'direction', 'fill', 'resetOnFinish', 'duration', 'mode', 'frameRate',\n];\n\n/** The lookup tables and the bindings. They are animation CONTENT, not playback, and are never reset. */\nconst CONTENT_KEYS = ['definitions', 'bindings'];\n\nconst isPlainObject = (v: unknown): v is Record<string, any> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Absent `type` means the time-driven timeline, so compare the normalized values (RULE 2). */\nconst timelineTypeOf = (t: unknown): string =>\n (isPlainObject(t) && typeof t.type === 'string') ? t.type : 'time';\n\nconst isScrollish = (type: string): boolean => type === 'scroll' || type === 'view';\n\n/**\n * RFC 7386 merge with no special cases. Used for every sub-object that has no rule of its own\n * (`trigger`, `range`, `definitions.fonts`, …), which is why patching one font leaves its\n * siblings alone. Arrays are values: a patched `bindings` list replaces the document's whole list.\n */\nfunction mergePlain(base: unknown, patch: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; } // RULE 7: null deletes\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value; // RULE 6: arrays/primitives replace\n }\n return out;\n}\n\n/**\n * The timeline, which is a discriminated union and therefore cannot be merged blindly.\n *\n * RULE 1 — the patch names a DIFFERENT type: replace the timeline outright, carrying over only\n * the keys both members share. Merging instead would strand `trigger`/`delay` on a\n * scroll timeline, where the format has no slot for them.\n * RULE 2 — same type, or the patch does not mention one: merge. A patch that spells\n * `type: 'time'` against a document with an ABSENT type must count as a match, hence\n * the normalization above.\n * RULE 3 — time-only keys landing on a scroll/view timeline are dropped with a warning, the\n * same way the wire has no slot for them.\n * RULE 4 — `iterations: 'infinite'` cannot map onto a scroll range.\n */\nfunction mergeTimeline(base: unknown, patch: Record<string, any>, warn: (m: string) => void): Record<string, any> {\n const baseType = timelineTypeOf(base);\n const patchNamesType = isPlainObject(patch) && typeof patch.type === 'string';\n const patchType = patchNamesType ? String(patch.type) : baseType;\n\n let merged: Record<string, any>;\n if (patchType !== baseType) {\n // RULE 1\n const carried: Record<string, any> = {};\n if (isPlainObject(base)) {\n for (const k of PX_TIMELINE_SHARED_KEYS) {\n if (base[k] !== undefined) carried[k] = base[k];\n }\n }\n merged = mergePlain(carried, patch);\n } else {\n merged = mergePlain(base, patch); // RULE 2\n }\n\n if (isScrollish(patchType)) {\n for (const k of PX_TIME_ONLY_TIMELINE_KEYS) { // RULE 3\n if (merged[k] === undefined) continue;\n warn('animator.timeline.' + k + \": no slot on a '\" + patchType + \"' timeline — dropped\");\n delete merged[k];\n }\n if (merged.iterations === 'infinite') { // RULE 4\n warn(\"animator.timeline.iterations: 'infinite' cannot map onto a scroll range — dropped\");\n delete merged.iterations;\n }\n }\n return merged;\n}\n\n/** A deep-partial of the WIRE `timeline` block; `null` at any slot deletes it. @public */\nexport type PxTimelinePatch = Record<string, any> | null;\n\n/** The four flat shortcuts every surface offers for the keys people reach for most. @public */\nexport interface PxTimelineShortcuts {\n /** Shortcut for `timeline.duration` — one iteration, ms. Wins over the same key in `timeline`. */\n duration?: number;\n /** Shortcut for `timeline.delay` — the wait before the first iteration, ms. */\n delay?: number;\n /** Shortcut for `timeline.iterations`; `'infinite'` never stops. */\n iterations?: number | 'infinite';\n /**\n * Shortcut for `timeline.trigger.start`. Typed from the WIRE, so it includes\n * `'none'` — \"nothing starts this but a `play()` call\".\n */\n start?: PxTriggerStart;\n}\n\n/**\n * The playback-override props every surface takes — `createAnimator` and the three components:\n * the document's `timeline` as a patch, the reset flag, and the four shortcuts above.\n *\n * ONE definition (review §9): React and React Native extend it, Vue derives its internal shape\n * from it, `createAnimator`'s options extend it.\n * @public\n */\nexport interface PxPlaybackOverride extends PxTimelineShortcuts {\n /**\n * Per-instance override of the document's `animator.timeline` — the same shape as `timeline`\n * in docs/format/README.md, deep-merged over what the document says, so one file can play twice on a page\n * with different timing. `null` at any slot DELETES that key, restoring the default its\n * absence means. Also accepts a JSON STRING, which survives a build that mangles object keys.\n *\n * `timeline` is the whole useful override surface: the rest of the `animator` block is\n * content (`definitions`, `bindings`), a version stamp and a debug handle — none of which\n * a per-instance override should touch. That is why this is not a wrapper object.\n */\n timeline?: PxTimelinePatch | string;\n /**\n * Ignore the document's own timeline and start from the player's DEFAULT timeline, with\n * `timeline` on top. `definitions` and `bindings` are content and are kept either way.\n */\n resetTimeline?: boolean;\n}\n\n/**\n * Folds the four shortcuts into the `timeline` patch, accepts the JSON-STRING form of the patch\n * (immune to property mangling — see docs/library/minification.md), and returns it in the shape\n * `applyAnimatorConfig` takes: an animator-level patch `{ timeline: … }`.\n *\n * A shortcut WINS over the same key inside the object: more specific beats more general, the\n * way an inline style beats a stylesheet. One implementation so every surface agrees.\n * @public\n */\nexport function foldTimelineOverride(\n timeline: PxTimelinePatch | string | undefined,\n shortcuts: PxTimelineShortcuts,\n): PxAnimatorConfigPatch | undefined {\n let base: PxTimelinePatch | undefined;\n if (typeof timeline === 'string') {\n try {\n base = JSON.parse(timeline);\n } catch (e) {\n console.warn('timeline override: not valid JSON — ignored', e);\n base = undefined;\n }\n } else {\n base = timeline ?? undefined;\n }\n\n const { duration, delay, iterations, start } = shortcuts;\n if (duration === undefined && delay === undefined && iterations === undefined && start === undefined) {\n return base === undefined ? undefined : { timeline: base };\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n if (duration !== undefined) out.duration = duration;\n if (delay !== undefined) out.delay = delay;\n if (iterations !== undefined) out.iterations = iterations;\n if (start !== undefined) {\n out.trigger = { ...(out.trigger as Record<string, any> | undefined), start };\n }\n return { timeline: out };\n}\n\n/**\n * Merge a patch over an animator config. PURE — neither argument is mutated, and the result is\n * always a NEW object, which also matters because `flattenAnimatorTimeline` memoises on config\n * identity: mutating in place would hand every later reader the pre-merge view.\n * @public\n */\nexport function mergeAnimatorConfig(\n base: PxAnimatorConfig | undefined,\n patch: PxAnimatorConfigPatch,\n): PxAnimatorConfigMergeResult {\n const warnings: Array<string> = [];\n const warn = (m: string) => warnings.push(m);\n\n if (patch === null) return { config: undefined, warnings };\n if (!isPlainObject(patch) || Object.keys(patch).length === 0) {\n return { config: base, warnings }; // nothing to do: same identity\n }\n\n // RULE 5 — a base in the flat runtime spelling cannot take a wire patch soundly:\n // `flattenAnimatorTimeline` copies the flat keys first and then overwrites them from\n // `timeline`, so a flat `delay` would survive a `timeline.delay: null` deletion.\n const flatInBase = isPlainObject(base) ? FLAT_ONLY_KEYS.filter(k => (base as any)[k] !== undefined) : [];\n if (flatInBase.length) {\n warn('animator: the base carries the flat runtime spelling (' + flatInBase.join(', ') + '); '\n + 'the patch merges the wire spelling only');\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; }\n if (key === 'timeline') {\n out.timeline = isPlainObject(value) ? mergeTimeline(out.timeline, value, warn) : value;\n continue;\n }\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value;\n }\n return { config: out as PxAnimatorConfig, warnings };\n}\n\n/**\n * Document level: resolves the two canonical addresses of the animator config and returns a NEW\n * document that shares every untouched subtree by reference.\n *\n * `resetTimeline` starts from the player's own defaults instead of the document's playback\n * settings — \"play this file as if it said nothing about timing\". The lookup tables are kept\n * either way: resetting `definitions`/`bindings` would leave an animation with nothing to\n * animate, which is never what a caller means.\n * @public\n */\nexport function applyAnimatorConfig(\n doc: PxAnimatedSvgDocument,\n patch: PxAnimatorConfigPatch,\n options?: { resetTimeline?: boolean },\n): { doc: PxAnimatedSvgDocument; warnings: Array<string> } {\n const reset = !!options?.resetTimeline;\n if (!doc || (patch === undefined || (!reset && (patch === null || !isPlainObject(patch) || !Object.keys(patch).length)))) {\n return { doc, warnings: [] };\n }\n\n const anyDoc = doc as any;\n const atRoot = isPlainObject(anyDoc.animator);\n const atMeta = !atRoot && isPlainObject(anyDoc.meta?.animator);\n const current: PxAnimatorConfig | undefined = atRoot ? anyDoc.animator\n : atMeta ? anyDoc.meta.animator\n : undefined;\n\n const warnings: Array<string> = [];\n if (atRoot && isPlainObject(anyDoc.meta?.animator)) {\n warnings.push('animator: doc.meta.animator is shadowed by doc.animator and was not patched');\n }\n\n let base = current;\n if (reset) {\n // Keep only the content tables; everything else starts from the player's defaults.\n const kept: Record<string, any> = {};\n for (const k of CONTENT_KEYS) {\n if (isPlainObject(current) && (current as any)[k] !== undefined) kept[k] = (current as any)[k];\n }\n base = kept as PxAnimatorConfig;\n }\n\n const merged = mergeAnimatorConfig(base, patch ?? {});\n warnings.push(...merged.warnings);\n if (merged.config === current) return { doc, warnings };\n\n if (atMeta) {\n return {\n doc: { ...anyDoc, meta: { ...anyDoc.meta, animator: merged.config } } as PxAnimatedSvgDocument,\n warnings,\n };\n }\n return { doc: { ...anyDoc, animator: merged.config } as PxAnimatedSvgDocument, warnings };\n}\n"],"mappings":"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","names":["effectiveProgress","_a"]}
|