@pixodesk/svg-animator-rn 1.0.34 → 1.0.39
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 +38 -30
- package/dist/index.cjs +117 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -62
- package/dist/index.d.ts +40 -62
- package/dist/index.js +130 -73
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/PixodeskSvgAnimator.tsx +176 -162
- package/src/PxRnErrorBoundary.tsx +5 -1
- package/src/PxRnMatrix.test.ts +2 -2
- package/src/PxRnMatrix.ts +1 -1
- package/src/PxRnPropNames.ts +2 -2
- package/src/PxRnRender.tsx +14 -15
- package/src/PxRnTracks.test.ts +16 -16
- package/src/PxRnTracks.ts +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,69 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PxPlaybackOverrideProps, PxControlProps, PxComponentCallbacks, PxAnimatedSvgDocument, PxTimelinePatch, PxAnimatorHandle, PxDefs, PxNode, PxDiagnostics } from '@pixodesk/svg-animator-core';
|
|
2
2
|
import React, { ReactElement, ComponentType, ReactNode, Component, ErrorInfo } from 'react';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/** Returns the current playback time in milliseconds. */
|
|
19
|
-
getCurrentTime(): number | null;
|
|
20
|
-
/** Jumps to a specific time (in milliseconds) in the animation. */
|
|
21
|
-
setCurrentTime(time: number): void;
|
|
22
|
-
}
|
|
23
|
-
interface PixodeskSvgAnimatorProps {
|
|
4
|
+
/**
|
|
5
|
+
* The imperative handle `apiRef` is filled with — core's `PxAnimatorHandle` under this package's
|
|
6
|
+
* name (review §9). It no longer "mirrors" the React one by hand: the local copy had already
|
|
7
|
+
* drifted (its `setPlaybackRate` comment lost "negative plays backwards"). One definition.
|
|
8
|
+
*/
|
|
9
|
+
type RnAnimatorApi = PxAnimatorHandle;
|
|
10
|
+
/**
|
|
11
|
+
* The component's props. The playback override, the control props and the callbacks are core's
|
|
12
|
+
* shared shapes (review §9) — `PxPlaybackOverrideProps`, `PxControlProps` and
|
|
13
|
+
* `PxComponentCallbacks` — so React, Vue and React Native cannot drift apart. Only what differs
|
|
14
|
+
* on this platform is declared here: `onError` keeps its richer signature (the error boundary
|
|
15
|
+
* hands it a component stack), and `fallback` has no web counterpart.
|
|
16
|
+
*/
|
|
17
|
+
interface PixodeskSvgAnimatorProps extends PxPlaybackOverrideProps, PxControlProps, Omit<PxComponentCallbacks, 'onError'> {
|
|
24
18
|
/** The animation document to render. */
|
|
25
19
|
doc: PxAnimatedSvgDocument;
|
|
26
|
-
/** Duration of a single iteration in milliseconds. */
|
|
27
|
-
duration?: number;
|
|
28
|
-
/** Delay before the animation starts, in milliseconds. */
|
|
29
|
-
delay?: number;
|
|
30
|
-
/** Number of iterations, or 'infinite' for endless looping. */
|
|
31
|
-
iterations?: number | 'infinite';
|
|
32
|
-
/** Defines the element's state when the animation is not active. */
|
|
33
|
-
fill?: FillMode;
|
|
34
|
-
/** Playback direction. */
|
|
35
|
-
direction?: PlaybackDirection;
|
|
36
|
-
/** Snap back to the start state after a natural finish. */
|
|
37
|
-
resetOnFinish?: boolean;
|
|
38
20
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
21
|
+
* Per-instance override of the document's `timeline` — see `PxPlaybackOverrideProps`.
|
|
22
|
+
*
|
|
23
|
+
* `engine` is accepted but ignored here: React Native always uses the `native` (fully
|
|
24
|
+
* flattened) materialization, because react-native-svg has no `<use>` shadow-tree
|
|
25
|
+
* propagation.
|
|
26
|
+
*/
|
|
27
|
+
timeline?: PxTimelinePatch | string;
|
|
28
|
+
/**
|
|
29
|
+
* Ref populated with the imperative playback API. Filled in EVERY mode and never picks one
|
|
30
|
+
* (review §1): `autoplay` next to it still autoplays.
|
|
41
31
|
*/
|
|
42
|
-
outAction?: OutAction;
|
|
43
|
-
/** When true, honours the document trigger (`startOn: 'load'` plays on mount). */
|
|
44
|
-
autoplay?: boolean;
|
|
45
|
-
/** Starts playback unconditionally. */
|
|
46
|
-
play?: boolean;
|
|
47
|
-
/** Pauses current playback. */
|
|
48
|
-
pause?: boolean;
|
|
49
|
-
/** Ref populated with the imperative playback API. */
|
|
50
32
|
apiRef?: React.RefObject<RnAnimatorApi | null>;
|
|
51
|
-
/** Seek to a fraction (0–1) of the whole timeline (duration × iterations). */
|
|
52
|
-
progress?: number;
|
|
53
|
-
/** Seek to a specific time in milliseconds. */
|
|
54
|
-
time?: number;
|
|
55
|
-
onPlay?: () => void;
|
|
56
|
-
onStop?: () => void;
|
|
57
|
-
onPause?: () => void;
|
|
58
|
-
onCancel?: () => void;
|
|
59
|
-
onFinish?: () => void;
|
|
60
33
|
/**
|
|
61
34
|
* Called when a document cannot be compiled or rendered. The component
|
|
62
35
|
* renders {@link fallback} instead of throwing, so a single broken
|
|
63
36
|
* animation never takes down the screen around it.
|
|
64
37
|
*
|
|
65
38
|
* Only JavaScript failures reach this — a crash inside react-native-svg's
|
|
66
|
-
* native renderer bypasses JavaScript entirely.
|
|
39
|
+
* native renderer bypasses JavaScript entirely. Richer than the web's
|
|
40
|
+
* `onError(diagnostic)` because the error boundary hands it a component stack; it is
|
|
41
|
+
* adapted into the shared diagnostics channel rather than narrowed to match it.
|
|
67
42
|
*/
|
|
68
43
|
onError?: (error: Error, componentStack?: string) => void;
|
|
69
44
|
/** Rendered in place of the animation after a failure. Default: nothing. */
|
|
@@ -72,14 +47,15 @@ interface PixodeskSvgAnimatorProps {
|
|
|
72
47
|
/**
|
|
73
48
|
* React Native component for rendering and controlling Pixodesk SVG animations.
|
|
74
49
|
*
|
|
75
|
-
* The document is
|
|
50
|
+
* The document is materialized once through the shared core pipeline (effects,
|
|
76
51
|
* loops, motion-path sampling, animated-`<use>` inlining — identical to the
|
|
77
|
-
* web
|
|
52
|
+
* web's `native` engine, NOT the frame loop, which keeps `<use>` live), compiled
|
|
53
|
+
* into densely sampled per-element tracks, and
|
|
78
54
|
* played back natively: a single reanimated progress value driven by
|
|
79
55
|
* `withTiming`/`withRepeat` on the UI thread, with per-element worklets
|
|
80
56
|
* indexing the precompiled tracks. No JS-thread frame loop.
|
|
81
57
|
*/
|
|
82
|
-
declare function PixodeskSvgAnimator({ doc,
|
|
58
|
+
declare function PixodeskSvgAnimator({ doc, timeline, resetTimeline, duration, delay, iterations, startOn, autoplay, play, pause, apiRef, progress: progressProp, time, onPlay, onStop, onPause, onCancel, onFinish, onRemove, onError, fallback, onWarn, silent, }: PixodeskSvgAnimatorProps): ReactElement | null;
|
|
83
59
|
|
|
84
60
|
interface RenderRnNodeOptions {
|
|
85
61
|
/** Collects non-fatal issues (unsupported tags, dropped attrs). */
|
|
@@ -99,13 +75,13 @@ interface RenderRnNodeOptions {
|
|
|
99
75
|
decorate?: (node: PxNode, Component: ComponentType<any>, props: Record<string, any>, children: ReactNode, key: string | number | undefined) => ReactElement | undefined;
|
|
100
76
|
}
|
|
101
77
|
/**
|
|
102
|
-
* Converts core-
|
|
103
|
-
* naming,
|
|
78
|
+
* Converts core-normalized wire props into react-native-svg props: RN prop
|
|
79
|
+
* naming, sanitization (same security rules as the web renderer), numeric
|
|
104
80
|
* coercion where possible.
|
|
105
81
|
*/
|
|
106
82
|
declare function toRnProps(props: Record<string, any>, warnings?: Array<string>, tag?: string): Record<string, any>;
|
|
107
83
|
/**
|
|
108
|
-
* Renders a (
|
|
84
|
+
* Renders a (materialized) PxNode tree to react-native-svg elements.
|
|
109
85
|
* Mirrors the web `renderNode` contract: unsupported/dangerous tags are
|
|
110
86
|
* skipped with a warning, never a crash.
|
|
111
87
|
*/
|
|
@@ -116,6 +92,8 @@ interface PxRnErrorBoundaryProps {
|
|
|
116
92
|
/** Rendered instead of the children once something has thrown. */
|
|
117
93
|
fallback?: (error: Error) => ReactNode;
|
|
118
94
|
onError?: (error: Error, info?: string) => void;
|
|
95
|
+
/** Where to report the failure (API review §5). Defaults to the console. */
|
|
96
|
+
diag?: PxDiagnostics;
|
|
119
97
|
}
|
|
120
98
|
interface State {
|
|
121
99
|
error: Error | null;
|
|
@@ -170,7 +148,7 @@ declare function openClosedTextPathTargets(doc: PxNode, warnings?: Array<string>
|
|
|
170
148
|
* The compiler densely samples every animated property through core's
|
|
171
149
|
* `calcAnimationValues` — the exact function the web frames engine renders
|
|
172
150
|
* with — so RN playback is value-identical to the web player. Easing, loops,
|
|
173
|
-
* transform composition,
|
|
151
|
+
* transform composition, color interpolation and path morphing are all baked
|
|
174
152
|
* into the samples at compile time; the UI-thread worklet only indexes arrays.
|
|
175
153
|
*/
|
|
176
154
|
interface PxElementTracks {
|
|
@@ -215,7 +193,7 @@ interface CompileTracksOptions {
|
|
|
215
193
|
native?: boolean;
|
|
216
194
|
}
|
|
217
195
|
/**
|
|
218
|
-
* Compiles a
|
|
196
|
+
* Compiles a MATERIALIZED document (run `materializeAllInTree(doc, 'frames')`
|
|
219
197
|
* + `generateNewIds` first) into densely sampled per-element tracks.
|
|
220
198
|
*/
|
|
221
199
|
declare function compileTracks(doc: PxAnimatedSvgDocument, opts?: CompileTracksOptions): PxCompiledTracks;
|
|
@@ -232,7 +210,7 @@ declare function compileTracks(doc: PxAnimatedSvgDocument, opts?: CompileTracksO
|
|
|
232
210
|
declare function sampleProps(tracks: PxElementTracks, tMs: number, stepMs: number, sampleCount: number, native?: boolean): Record<string, string | number | Array<number>>;
|
|
233
211
|
|
|
234
212
|
/**
|
|
235
|
-
* Converts one
|
|
213
|
+
* Converts one normalized wire attribute name (camelCase after core's
|
|
236
214
|
* `getNormalizedProps`, or kebab-case raw) to a react-native-svg prop name.
|
|
237
215
|
* Returns undefined for props that must be dropped.
|
|
238
216
|
*
|
package/dist/index.js
CHANGED
|
@@ -32,12 +32,28 @@ var __objRest = (source, exclude) => {
|
|
|
32
32
|
|
|
33
33
|
// src/PixodeskSvgAnimator.tsx
|
|
34
34
|
import {
|
|
35
|
+
clampSeekMs,
|
|
36
|
+
createDiagnostics as createDiagnostics2,
|
|
37
|
+
createRunClock,
|
|
38
|
+
isValidPlaybackRate,
|
|
39
|
+
progressSpanMs,
|
|
40
|
+
progressToTimeMs,
|
|
41
|
+
PX_RATE_REJECTED,
|
|
42
|
+
PxDiagnosticKind as PxDiagnosticKind2,
|
|
43
|
+
seekCeilingMs,
|
|
44
|
+
timeToProgress,
|
|
45
|
+
reportDocumentDiagnostics,
|
|
35
46
|
generateNewIds,
|
|
36
47
|
getAnimatorConfig as getAnimatorConfig2,
|
|
37
48
|
getDefs,
|
|
38
|
-
|
|
49
|
+
materializeAllInTree,
|
|
50
|
+
resolveTrigger,
|
|
39
51
|
validateNodeEffects,
|
|
40
|
-
|
|
52
|
+
PxTimelineEngine as PxTimelineEngine2,
|
|
53
|
+
PxControlMode,
|
|
54
|
+
resolveControlMode,
|
|
55
|
+
applyAnimatorConfig,
|
|
56
|
+
foldTimelineOverride
|
|
41
57
|
} from "@pixodesk/svg-animator-core";
|
|
42
58
|
import { createElement as createElement2, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
43
59
|
import { Dimensions, Platform, Pressable, View } from "react-native";
|
|
@@ -57,9 +73,9 @@ import Animated, {
|
|
|
57
73
|
import {
|
|
58
74
|
calcAnimationValues,
|
|
59
75
|
getAnimatorConfig,
|
|
60
|
-
|
|
76
|
+
getNormalizedBindings,
|
|
61
77
|
DEFAULT_DURATION_MS,
|
|
62
|
-
|
|
78
|
+
PxTimelineEngine
|
|
63
79
|
} from "@pixodesk/svg-animator-core";
|
|
64
80
|
|
|
65
81
|
// src/PxRnPropNames.ts
|
|
@@ -180,7 +196,7 @@ function compileTracks(doc, opts) {
|
|
|
180
196
|
((_a2 = n == null ? void 0 : n.children) != null ? _a2 : []).forEach(indexTags);
|
|
181
197
|
};
|
|
182
198
|
indexTags(doc);
|
|
183
|
-
const bindings =
|
|
199
|
+
const bindings = getNormalizedBindings(doc, PxTimelineEngine.js) || [];
|
|
184
200
|
const elements = [];
|
|
185
201
|
for (const binding of bindings) {
|
|
186
202
|
const animDef = binding.animate;
|
|
@@ -243,9 +259,8 @@ function sampleProps(tracks, tMs, stepMs, sampleCount, native = false) {
|
|
|
243
259
|
import {
|
|
244
260
|
getNormalizedProps,
|
|
245
261
|
resolveStyle,
|
|
246
|
-
|
|
262
|
+
sanitizeAttributeValue,
|
|
247
263
|
DISALLOWED_SVG_TAGS_LOWER,
|
|
248
|
-
TEXT_ATTR,
|
|
249
264
|
TEXT_CONTENT_ATTR
|
|
250
265
|
} from "@pixodesk/svg-animator-core";
|
|
251
266
|
import { createElement } from "react";
|
|
@@ -364,14 +379,14 @@ var RN_SVG_COMPONENTS = {
|
|
|
364
379
|
|
|
365
380
|
// src/PxRnRender.tsx
|
|
366
381
|
function toRnProps(props, warnings, tag) {
|
|
367
|
-
const
|
|
382
|
+
const normalized = getNormalizedProps(props);
|
|
368
383
|
const out = {};
|
|
369
|
-
for (const key of Object.keys(
|
|
370
|
-
const
|
|
371
|
-
if (
|
|
384
|
+
for (const key of Object.keys(normalized)) {
|
|
385
|
+
const sanitized = sanitizeAttributeValue(key, normalized[key]);
|
|
386
|
+
if (sanitized === void 0) continue;
|
|
372
387
|
const rnKey = toRnPropName(key);
|
|
373
388
|
if (!rnKey) continue;
|
|
374
|
-
out[rnKey] = toRnPropValue(rnKey, String(
|
|
389
|
+
out[rnKey] = toRnPropValue(rnKey, String(sanitized), tag);
|
|
375
390
|
}
|
|
376
391
|
return out;
|
|
377
392
|
}
|
|
@@ -398,12 +413,12 @@ function renderRnNode(node, opts = {}, key) {
|
|
|
398
413
|
for (const [k, v] of Object.entries(resolved)) {
|
|
399
414
|
const rnKey = toRnPropName(k);
|
|
400
415
|
if (!rnKey || rnKey in rnProps) continue;
|
|
401
|
-
const
|
|
402
|
-
if (
|
|
403
|
-
rnProps[rnKey] = toRnPropValue(rnKey, String(
|
|
416
|
+
const sanitized = sanitizeAttributeValue(rnKey, v);
|
|
417
|
+
if (sanitized === void 0) continue;
|
|
418
|
+
rnProps[rnKey] = toRnPropValue(rnKey, String(sanitized), tag);
|
|
404
419
|
}
|
|
405
420
|
}
|
|
406
|
-
const textContent = props[
|
|
421
|
+
const textContent = props[TEXT_CONTENT_ATTR];
|
|
407
422
|
let childElements = void 0;
|
|
408
423
|
if (Array.isArray(children) && children.length > 0) {
|
|
409
424
|
childElements = children.map((ch, i) => {
|
|
@@ -429,6 +444,7 @@ function renderRnNode(node, opts = {}, key) {
|
|
|
429
444
|
|
|
430
445
|
// src/PxRnErrorBoundary.tsx
|
|
431
446
|
import { Component } from "react";
|
|
447
|
+
import { createDiagnostics, PxDiagnosticKind } from "@pixodesk/svg-animator-core";
|
|
432
448
|
var PxRnErrorBoundary = class extends Component {
|
|
433
449
|
constructor() {
|
|
434
450
|
super(...arguments);
|
|
@@ -438,9 +454,9 @@ var PxRnErrorBoundary = class extends Component {
|
|
|
438
454
|
return { error };
|
|
439
455
|
}
|
|
440
456
|
componentDidCatch(error, info) {
|
|
441
|
-
var _a, _b, _c, _d;
|
|
457
|
+
var _a, _b, _c, _d, _e;
|
|
442
458
|
(_c = (_b = this.props).onError) == null ? void 0 : _c.call(_b, error, (_a = info == null ? void 0 : info.componentStack) != null ? _a : void 0);
|
|
443
|
-
|
|
459
|
+
((_d = this.props.diag) != null ? _d : createDiagnostics(void 0, "[PixodeskSvgAnimator]")).warn(PxDiagnosticKind.internal, "render failed: " + ((_e = error == null ? void 0 : error.message) != null ? _e : String(error)));
|
|
444
460
|
}
|
|
445
461
|
componentDidUpdate(prev) {
|
|
446
462
|
if (this.state.error && prev.children !== this.props.children) {
|
|
@@ -599,38 +615,33 @@ var EMPTY_TRACKS = {
|
|
|
599
615
|
sampleCount: 2,
|
|
600
616
|
elements: []
|
|
601
617
|
};
|
|
602
|
-
function compileDocument(doc, overrides) {
|
|
603
|
-
const {
|
|
618
|
+
function compileDocument(doc, overrides, diag) {
|
|
619
|
+
const { timeline, resetTimeline, duration, delay, iterations, startOn } = overrides;
|
|
604
620
|
const warnings = validateNodeEffects(doc);
|
|
605
|
-
for (const w of warnings)
|
|
606
|
-
|
|
621
|
+
for (const w of warnings) diag.warn(PxDiagnosticKind2.document, "effects shape: " + w);
|
|
622
|
+
reportDocumentDiagnostics(doc, "[PixodeskSvgAnimator]");
|
|
623
|
+
const patch = foldTimelineOverride(timeline, { duration, delay, iterations, startOn });
|
|
624
|
+
if (patch !== void 0 || resetTimeline) {
|
|
625
|
+
const applied = applyAnimatorConfig(doc, patch != null ? patch : {}, { resetDefaults: !!resetTimeline });
|
|
626
|
+
for (const w of applied.warnings) diag.warn(PxDiagnosticKind2.usage, "timeline override: " + w);
|
|
627
|
+
doc = applied.doc;
|
|
628
|
+
}
|
|
629
|
+
let prepared = materializeAllInTree(doc, PxTimelineEngine2.native);
|
|
607
630
|
if (NATIVE_SVG_VIEWS) {
|
|
608
631
|
prepared = openClosedTextPathTargets(prepared);
|
|
609
632
|
}
|
|
610
|
-
const animator = getAnimatorConfig2(prepared) || {};
|
|
611
|
-
prepared = __spreadProps(__spreadValues({}, prepared), {
|
|
612
|
-
animator: __spreadProps(__spreadValues({}, animator), {
|
|
613
|
-
duration: duration !== void 0 ? duration : animator.duration,
|
|
614
|
-
delay: delay !== void 0 ? delay : animator.delay,
|
|
615
|
-
iterations: iterations !== void 0 ? iterations : animator.iterations,
|
|
616
|
-
fill: fill !== void 0 ? fill : animator.fill,
|
|
617
|
-
direction: direction !== void 0 ? direction : animator.direction,
|
|
618
|
-
resetOnFinish: resetOnFinish !== void 0 ? resetOnFinish : animator.resetOnFinish
|
|
619
|
-
})
|
|
620
|
-
});
|
|
621
633
|
prepared = generateNewIds(prepared);
|
|
622
634
|
const tracks = compileTracks(prepared, { native: NATIVE_SVG_VIEWS });
|
|
623
635
|
return { doc: prepared, tracks, error: null };
|
|
624
636
|
}
|
|
625
637
|
function PixodeskSvgAnimator({
|
|
626
638
|
doc,
|
|
639
|
+
timeline,
|
|
640
|
+
resetTimeline,
|
|
627
641
|
duration,
|
|
628
642
|
delay,
|
|
629
643
|
iterations,
|
|
630
|
-
|
|
631
|
-
direction,
|
|
632
|
-
resetOnFinish,
|
|
633
|
-
outAction: outActionProp,
|
|
644
|
+
startOn,
|
|
634
645
|
// (`progress` prop aliased — the name is taken by the internal reanimated SharedValue)
|
|
635
646
|
autoplay,
|
|
636
647
|
play,
|
|
@@ -643,24 +654,42 @@ function PixodeskSvgAnimator({
|
|
|
643
654
|
onPause,
|
|
644
655
|
onCancel,
|
|
645
656
|
onFinish,
|
|
657
|
+
onRemove,
|
|
646
658
|
onError,
|
|
647
|
-
fallback
|
|
659
|
+
fallback,
|
|
660
|
+
onWarn,
|
|
661
|
+
silent
|
|
648
662
|
}) {
|
|
649
|
-
var _a, _b
|
|
663
|
+
var _a, _b;
|
|
664
|
+
const timelineKey = typeof timeline === "string" ? timeline : JSON.stringify(timeline != null ? timeline : null);
|
|
665
|
+
const makeDiag = () => createDiagnostics2({
|
|
666
|
+
onWarn,
|
|
667
|
+
// This component's public `onError` is richer — `(error, componentStack?)` — and the
|
|
668
|
+
// error boundary hands it a component stack. Adapt rather than narrow it; the ternary
|
|
669
|
+
// keeps "not given" as undefined, so the console fallback still fires.
|
|
670
|
+
onError: onError ? (d) => {
|
|
671
|
+
var _a2;
|
|
672
|
+
return onError((_a2 = d.error) != null ? _a2 : new Error(d.message));
|
|
673
|
+
} : void 0,
|
|
674
|
+
silent
|
|
675
|
+
}, "[PixodeskSvgAnimator]");
|
|
650
676
|
const compiled = useMemo(() => {
|
|
651
677
|
try {
|
|
652
678
|
return compileDocument(
|
|
653
679
|
doc,
|
|
654
|
-
{
|
|
680
|
+
{ timeline, resetTimeline, duration, delay, iterations, startOn },
|
|
681
|
+
makeDiag()
|
|
655
682
|
);
|
|
656
683
|
} catch (e) {
|
|
657
684
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
658
|
-
|
|
685
|
+
makeDiag().warn(PxDiagnosticKind2.internal, "could not compile the document: " + error.message);
|
|
659
686
|
return { doc: null, tracks: EMPTY_TRACKS, error };
|
|
660
687
|
}
|
|
661
|
-
}, [doc,
|
|
688
|
+
}, [doc, timelineKey, resetTimeline, duration, delay, iterations, startOn]);
|
|
662
689
|
const tracks = compiled.tracks;
|
|
663
|
-
const totalDuration = tracks.duration
|
|
690
|
+
const totalDuration = progressSpanMs(tracks.duration, tracks.iterations);
|
|
691
|
+
const seekCeiling = seekCeilingMs(tracks.duration, tracks.iterations);
|
|
692
|
+
const runClock = useMemo(() => createRunClock(seekCeiling), [seekCeiling]);
|
|
664
693
|
const progress = useSharedValue(0);
|
|
665
694
|
const playingRef = useRef(false);
|
|
666
695
|
const rateRef = useRef(1);
|
|
@@ -672,6 +701,8 @@ function PixodeskSvgAnimator({
|
|
|
672
701
|
};
|
|
673
702
|
const notifyFinish = () => {
|
|
674
703
|
playingRef.current = false;
|
|
704
|
+
runClock.seek(Number.isFinite(seekCeiling) ? seekCeiling : tracks.duration);
|
|
705
|
+
runClock.stop();
|
|
675
706
|
progress.value = restingPosition();
|
|
676
707
|
onFinish == null ? void 0 : onFinish();
|
|
677
708
|
onStop == null ? void 0 : onStop();
|
|
@@ -708,12 +739,15 @@ function PixodeskSvgAnimator({
|
|
|
708
739
|
const api = {
|
|
709
740
|
isPlaying: () => playingRef.current,
|
|
710
741
|
play: () => {
|
|
742
|
+
if (Number.isFinite(seekCeiling) && runClock.now() >= seekCeiling) runClock.seek(0);
|
|
743
|
+
runClock.start(rateRef.current);
|
|
711
744
|
startFrom(progress.value);
|
|
712
745
|
onPlay == null ? void 0 : onPlay();
|
|
713
746
|
},
|
|
714
747
|
pause: () => {
|
|
715
748
|
cancelAnimation(progress);
|
|
716
749
|
playingRef.current = false;
|
|
750
|
+
runClock.stop();
|
|
717
751
|
onPause == null ? void 0 : onPause();
|
|
718
752
|
onStop == null ? void 0 : onStop();
|
|
719
753
|
},
|
|
@@ -721,62 +755,82 @@ function PixodeskSvgAnimator({
|
|
|
721
755
|
cancelAnimation(progress);
|
|
722
756
|
progress.value = 0;
|
|
723
757
|
playingRef.current = false;
|
|
758
|
+
runClock.seek(0);
|
|
759
|
+
runClock.stop();
|
|
724
760
|
onCancel == null ? void 0 : onCancel();
|
|
725
761
|
onStop == null ? void 0 : onStop();
|
|
726
762
|
},
|
|
727
763
|
finish: () => {
|
|
728
764
|
cancelAnimation(progress);
|
|
729
765
|
playingRef.current = false;
|
|
766
|
+
runClock.seek(Number.isFinite(seekCeiling) ? seekCeiling : tracks.duration);
|
|
767
|
+
runClock.stop();
|
|
730
768
|
progress.value = restingPosition();
|
|
731
769
|
onFinish == null ? void 0 : onFinish();
|
|
732
770
|
onStop == null ? void 0 : onStop();
|
|
733
771
|
},
|
|
734
772
|
setPlaybackRate: (rate) => {
|
|
735
|
-
if (!
|
|
736
|
-
|
|
773
|
+
if (!isValidPlaybackRate(rate)) {
|
|
774
|
+
makeDiag().warn(PxDiagnosticKind2.usage, PX_RATE_REJECTED);
|
|
737
775
|
return;
|
|
738
776
|
}
|
|
739
777
|
rateRef.current = rate;
|
|
740
|
-
if (playingRef.current)
|
|
778
|
+
if (playingRef.current) {
|
|
779
|
+
runClock.start(rate);
|
|
780
|
+
startFrom(progress.value);
|
|
781
|
+
}
|
|
741
782
|
},
|
|
742
|
-
|
|
783
|
+
// Ms from the start of the WHOLE run, like every other engine (review §3). This used
|
|
784
|
+
// to return `progress.value`, which is ms within the current iteration — so a slider
|
|
785
|
+
// built on it jumped back to 0 every time the animation repeated.
|
|
786
|
+
getCurrentTime: () => runClock.now(),
|
|
743
787
|
setCurrentTime: (t) => {
|
|
744
788
|
const wasPlaying = playingRef.current;
|
|
745
789
|
cancelAnimation(progress);
|
|
746
790
|
playingRef.current = false;
|
|
747
|
-
const clamped =
|
|
791
|
+
const clamped = clampSeekMs(t, seekCeiling);
|
|
792
|
+
runClock.seek(clamped);
|
|
748
793
|
const withinIteration = tracks.duration > 0 ? clamped % tracks.duration || (clamped === 0 ? 0 : tracks.duration) : 0;
|
|
749
794
|
progress.value = withinIteration;
|
|
750
795
|
if (wasPlaying) startFrom(withinIteration);
|
|
796
|
+
else runClock.stop();
|
|
797
|
+
},
|
|
798
|
+
getCurrentProgress: () => timeToProgress(runClock.now(), tracks.duration, tracks.iterations),
|
|
799
|
+
setCurrentProgress: (p) => {
|
|
800
|
+
api.setCurrentTime(progressToTimeMs(p, tracks.duration, tracks.iterations));
|
|
751
801
|
}
|
|
752
802
|
};
|
|
753
803
|
useImperativeHandle(apiRef, () => api, [compiled]);
|
|
754
|
-
const trigger = compiled.doc ? (_a = getAnimatorConfig2(compiled.doc)) == null ? void 0 : _a.trigger : void 0;
|
|
755
|
-
const
|
|
756
|
-
const
|
|
804
|
+
const trigger = resolveTrigger(compiled.doc ? (_a = getAnimatorConfig2(compiled.doc)) == null ? void 0 : _a.trigger : void 0);
|
|
805
|
+
const effectiveStartOn = trigger.startOn;
|
|
806
|
+
const effectiveOutAction = trigger.outAction;
|
|
807
|
+
const { mode: compMode, warnings: modeWarnings } = resolveControlMode({ progress: progressProp, time, play, pause, autoplay });
|
|
808
|
+
useEffect(() => {
|
|
809
|
+
const diag = makeDiag();
|
|
810
|
+
for (const w of modeWarnings) diag.warn(PxDiagnosticKind2.usage, w);
|
|
811
|
+
}, [modeWarnings.join("|")]);
|
|
757
812
|
useEffect(() => {
|
|
758
|
-
if (
|
|
759
|
-
const seekMs = time !== void 0 ? time : (progressProp != null ? progressProp : 0
|
|
813
|
+
if (compMode === PxControlMode.fixedTime) {
|
|
814
|
+
const seekMs = time !== void 0 ? time : progressToTimeMs(progressProp != null ? progressProp : 0, tracks.duration, tracks.iterations);
|
|
760
815
|
api.setCurrentTime(seekMs);
|
|
761
816
|
return;
|
|
762
817
|
}
|
|
763
|
-
if (
|
|
818
|
+
if (compMode === PxControlMode.play) {
|
|
764
819
|
if (play && !pause) api.play();
|
|
765
820
|
else if (pause) api.pause();
|
|
766
|
-
else if (play === false) api.
|
|
821
|
+
else if (play === false) api.pause();
|
|
767
822
|
else api.play();
|
|
768
823
|
return;
|
|
769
824
|
}
|
|
770
|
-
if (autoplay &&
|
|
825
|
+
if (compMode === PxControlMode.autoplay && effectiveStartOn === "load") {
|
|
771
826
|
api.play();
|
|
772
827
|
}
|
|
773
|
-
}, [compiled, autoplay, play, pause, progressProp, time]);
|
|
828
|
+
}, [compiled, compMode, autoplay, play, pause, progressProp, time]);
|
|
774
829
|
const scrollRef = useRef(null);
|
|
775
830
|
const inViewRef = useRef(false);
|
|
776
831
|
useEffect(() => {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const threshold = (_a2 = trigger == null ? void 0 : trigger.scrollIntoViewThreshold) != null ? _a2 : 0;
|
|
832
|
+
if (!autoplay || effectiveStartOn !== "scrollIntoView") return;
|
|
833
|
+
const threshold = trigger.scrollIntoViewThreshold;
|
|
780
834
|
inViewRef.current = false;
|
|
781
835
|
const check = () => {
|
|
782
836
|
const node = scrollRef.current;
|
|
@@ -792,21 +846,23 @@ function PixodeskSvgAnimator({
|
|
|
792
846
|
if (isIn) {
|
|
793
847
|
if (rateRef.current < 0) api.setPlaybackRate(Math.abs(rateRef.current));
|
|
794
848
|
api.play();
|
|
795
|
-
} else if (
|
|
796
|
-
else if (
|
|
849
|
+
} else if (effectiveOutAction === "reset") api.cancel();
|
|
850
|
+
else if (effectiveOutAction === "reverse") {
|
|
797
851
|
api.setPlaybackRate(-Math.abs(rateRef.current || 1));
|
|
798
852
|
api.play();
|
|
799
|
-
} else if (
|
|
853
|
+
} else if (effectiveOutAction !== "continue") api.pause();
|
|
800
854
|
});
|
|
801
855
|
};
|
|
802
856
|
check();
|
|
803
857
|
const id = setInterval(check, 200);
|
|
804
858
|
return () => clearInterval(id);
|
|
805
|
-
}, [compiled, autoplay,
|
|
859
|
+
}, [compiled, autoplay, effectiveStartOn, effectiveOutAction]);
|
|
806
860
|
useEffect(() => {
|
|
807
861
|
return () => {
|
|
808
862
|
cancelAnimation(progress);
|
|
809
863
|
playingRef.current = false;
|
|
864
|
+
onRemove == null ? void 0 : onRemove();
|
|
865
|
+
onStop == null ? void 0 : onStop();
|
|
810
866
|
};
|
|
811
867
|
}, [compiled]);
|
|
812
868
|
const trackById = useMemo(() => {
|
|
@@ -862,32 +918,33 @@ function PixodeskSvgAnimator({
|
|
|
862
918
|
} catch (e) {
|
|
863
919
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
864
920
|
renderErrorRef.current = error;
|
|
865
|
-
|
|
921
|
+
makeDiag().warn(PxDiagnosticKind2.internal, "could not render the document: " + error.message);
|
|
866
922
|
return null;
|
|
867
923
|
}
|
|
868
924
|
}, [compiled, trackById]);
|
|
869
925
|
useEffect(() => {
|
|
870
|
-
|
|
926
|
+
const diag = makeDiag();
|
|
927
|
+
for (const w of warningsRef.current) diag.warn(PxDiagnosticKind2.platform, w);
|
|
871
928
|
}, [root]);
|
|
872
|
-
const failure = (
|
|
929
|
+
const failure = (_b = compiled.error) != null ? _b : renderErrorRef.current;
|
|
873
930
|
useEffect(() => {
|
|
874
931
|
if (failure) onError == null ? void 0 : onError(failure);
|
|
875
932
|
}, [failure]);
|
|
876
933
|
if (failure) return fallback ? fallback(failure) : null;
|
|
877
934
|
let content = root;
|
|
878
|
-
if (autoplay &&
|
|
935
|
+
if (autoplay && effectiveStartOn === "scrollIntoView" && root) {
|
|
879
936
|
content = /* @__PURE__ */ jsx(View, { ref: scrollRef, collapsable: false, children: root });
|
|
880
|
-
} else if (autoplay &&
|
|
937
|
+
} else if (autoplay && effectiveStartOn === "click" && root) {
|
|
881
938
|
content = /* @__PURE__ */ jsx(
|
|
882
939
|
Pressable,
|
|
883
940
|
{
|
|
884
941
|
onPress: () => {
|
|
885
942
|
if (playingRef.current) {
|
|
886
|
-
if (
|
|
887
|
-
else if (
|
|
943
|
+
if (effectiveOutAction === "reset") api.cancel();
|
|
944
|
+
else if (effectiveOutAction === "reverse") {
|
|
888
945
|
api.setPlaybackRate(-Math.abs(rateRef.current || 1));
|
|
889
946
|
api.play();
|
|
890
|
-
} else if (
|
|
947
|
+
} else if (effectiveOutAction !== "continue") api.pause();
|
|
891
948
|
} else {
|
|
892
949
|
if (rateRef.current < 0) api.setPlaybackRate(Math.abs(rateRef.current));
|
|
893
950
|
api.play();
|
|
@@ -897,7 +954,7 @@ function PixodeskSvgAnimator({
|
|
|
897
954
|
}
|
|
898
955
|
);
|
|
899
956
|
}
|
|
900
|
-
return /* @__PURE__ */ jsx(PxRnErrorBoundary, { onError, fallback, children: content });
|
|
957
|
+
return /* @__PURE__ */ jsx(PxRnErrorBoundary, { onError, fallback, diag: makeDiag(), children: content });
|
|
901
958
|
}
|
|
902
959
|
var PixodeskSvgAnimator_default = PixodeskSvgAnimator;
|
|
903
960
|
export {
|