@pixodesk/svg-animator-core 1.0.21 โ†’ 1.0.24

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 ADDED
@@ -0,0 +1,155 @@
1
+ # animator-core
2
+
3
+ [![CI](https://github.com/pixodesk/pixodesk-svg-animator/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/pixodesk/pixodesk-svg-animator/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Platform-neutral core of the Pixodesk SVG animator: the document schema, the
7
+ effect materialisers, the interpolation engine and the path sampler โ€” with **no
8
+ DOM dependency at all**. It is what every player shares, so the web player and
9
+ the React Native player produce identical values from the same document.
10
+
11
+ # ๐Ÿšง **Status - This project is currently under development.**
12
+
13
+ ## Do I need this package?
14
+
15
+ Usually **no**. If you just want to play an animation, install a player:
16
+
17
+ | You are building for | Install |
18
+ |---|---|
19
+ | Browser (vanilla JS) | [`@pixodesk/svg-animator-web`](../svg-animator-web/README.md) |
20
+ | React | [`@pixodesk/svg-animator-react`](../svg-animator-react/README.md) |
21
+ | Vue | [`@pixodesk/svg-animator-vue`](../svg-animator-vue/README.md) |
22
+ | React Native | [`@pixodesk/svg-animator-rn`](../svg-animator-rn/README.md) ๐Ÿงช |
23
+
24
+ Each of those depends on this package and re-exports what you need.
25
+
26
+ Install it **directly** when you want to work with documents rather than play
27
+ them โ€” validating them, transforming them, flattening them for a renderer of
28
+ your own, or computing values at a given time without rendering anything.
29
+
30
+ ```bash
31
+ npm install @pixodesk/svg-animator-core
32
+ ```
33
+
34
+ ## Why it exists
35
+
36
+ A player has to answer two very different questions:
37
+
38
+ 1. **What should be on screen at time _t_?** โ€” schema, effects, easing,
39
+ interpolation, path sampling. Pure computation, identical on every platform.
40
+ 2. **How do I put it there?** โ€” DOM elements, WAAPI, `react-native-svg`.
41
+ Platform-specific.
42
+
43
+ Everything in category 1 lives here. The package compiles without the TypeScript
44
+ `dom` library, so a stray `document` reference is a build error rather than a
45
+ runtime crash on a non-browser platform.
46
+
47
+ ## What's inside
48
+
49
+ | Area | Exports |
50
+ |---|---|
51
+ | **Schema & types** | `PxAnimatedSvgDocumentSchema`, `PxNodeSchema`, `PxEffectsSchema`, โ€ฆ plus every `Px*` TypeScript type and the `px` schema builder |
52
+ | **Validation** | `isPxElementFileFormat`, `isPxElementFileFormatDeep`, `validateNodeEffects` |
53
+ | **Materialisers** | `materialiseAllInTree`, `applyPlayerEffects`, `materialiseInternalLoopsInTree`, `materialiseMotionPathsInTree`, `materialiseAnimatedUseInstances` |
54
+ | **Interpolation** | `calcAnimationValues`, `interpolateValue`, `getNormalisedBindings` |
55
+ | **Sampling / geometry** | `createPathSampler`, `evaluateMotionPathSegment`, bezier helpers, `cubicBezier`, `splitEasing` |
56
+ | **Text** | `materialiseGlyphText`, `layoutGlyphTextChars`, `extendedPathForBrowser` |
57
+ | **Node helpers** | `getNormalizedProps`, `sanitiseAttributeValue`, `resolveStyle`, `generateNewIds` |
58
+ | **Playback engine** | `createBasicFrameLoopAnimator` + the `PxPlatformAdapter` interface |
59
+ | **Wire enums** | `PxAnimatorMode`, `PxAnimatorEngine`, `PxLoopExtend`, `PxTrimSubPaths`, `PxMaskType`, `PxCloneType`, `PxUnits`, `PxGradientType`, `PxGradientUnits`, `PxGradientSpreadMethod`, `PxPathOverflow`, `PxLengthAdjust`, `PxTextPathMethod`, `PxTextPathSpacing` โ€” every two-or-more-way wire selector is a named enum, not a bare string |
60
+
61
+ ### Validating a document
62
+
63
+ `isPxElementFileFormat(json)` is the cheap shallow gate (is this a Px document at all?);
64
+ `isPxElementFileFormatDeep(json)` runs the full schema. For per-field diagnostics, call a schema's
65
+ `isValid` with a context:
66
+
67
+ ```ts
68
+ import { PxAnimatedSvgDocumentSchema, type PxValidationContext } from '@pixodesk/svg-animator-core';
69
+
70
+ const ctx: PxValidationContext = { errors: [], warnings: [], strict: true };
71
+ const ok = PxAnimatedSvgDocumentSchema.isValid(doc, ctx, []);
72
+ if (!ok) console.error(ctx.errors); // ["children[0].effects.trimPath.range: โ€ฆ", โ€ฆ]
73
+ ```
74
+
75
+ **Two modes, two different questions:**
76
+
77
+ | mode | question it answers | undeclared keys |
78
+ |---|---|---|
79
+ | default (`strict` absent/false) | *is this document repairable?* โ€” what `sanitize` would accept | ignored, so unknown future fields stay forward-compatible |
80
+ | `strict: true` | *is this document well-formed?* โ€” the wire shape locked to its schema | reported as errors on closed objects |
81
+
82
+ Use default in production readers and `strict` in tests and tooling. Two notes on strict, both
83
+ deliberate: it reaches **inside unions** (a union member is checked in the caller's mode, though its
84
+ per-branch errors are not reported unless every branch fails), and it **ignores keys whose value is
85
+ `undefined`** โ€” those cannot survive `JSON.stringify`, so strict judges the document rather than the
86
+ in-memory object that produced it.
87
+
88
+ ## The materialisation pipeline
89
+
90
+ `materialiseAllInTree(doc, engine)` is the single entry point that turns a
91
+ lightweight editor document into a flat tree any renderer can walk:
92
+
93
+ 1. **Effects** โ€” `node.effects` (transformBy, repeater, maskedBy, trimPath,
94
+ clone/retime, gradients, textPath) become real nodes, wrappers and defs.
95
+ 2. **Loops** โ€” each property's `loop` is expanded into explicit keyframes.
96
+ 3. **Motion paths** โ€” tangented `transform` keyframes plus `autoOrient` are
97
+ sampled into plain `{translate, rotate}` keyframes.
98
+ 4. **Animated `<use>`** โ€” replaced by `<g>` + a deep clone with fresh ids.
99
+
100
+ Steps 3 and 4 run when `engine` is `waapi`. Pass `waapi` for **any renderer
101
+ without live `<use>` propagation** โ€” that includes `react-native-svg` โ€” and
102
+ `frames` only for the DOM, which resolves `<use>` references natively.
103
+
104
+ ```ts
105
+ import {
106
+ materialiseAllInTree, generateNewIds, calcAnimationValues,
107
+ getNormalisedBindings, PxAnimatorEngine,
108
+ } from '@pixodesk/svg-animator-core';
109
+
110
+ // Flatten once โ€ฆ
111
+ const flat = generateNewIds(materialiseAllInTree(doc, PxAnimatorEngine.waapi));
112
+
113
+ // โ€ฆ then ask for values at any time, with no renderer involved.
114
+ for (const binding of getNormalisedBindings(flat, PxAnimatorEngine.frames) ?? []) {
115
+ const values = calcAnimationValues(binding.animate, 500); // t = 500 ms
116
+ console.log(binding.id, values); // โ†’ { opacity: '0.5', transform: 'translate(โ€ฆ)' }
117
+ }
118
+ ```
119
+
120
+ This is exactly how the React Native player precomputes its animation tracks, and
121
+ how the frames engine renders each tick in the browser โ€” same function, same
122
+ numbers.
123
+
124
+ ## Writing your own player
125
+
126
+ Implement `PxPlatformAdapter` and hand it to `createBasicFrameLoopAnimator`; the
127
+ engine handles timing, delay, direction, iterations, fill, playback rate and the
128
+ lifecycle callbacks, then calls you with plain attribute writes.
129
+
130
+ ```ts
131
+ import { createBasicFrameLoopAnimator, type PxPlatformAdapter } from '@pixodesk/svg-animator-core';
132
+
133
+ const adapter: PxPlatformAdapter = {
134
+ isConnected: () => true,
135
+ setAttribute: (id, attrName, value) => { /* apply to your element */ },
136
+ };
137
+
138
+ const api = createBasicFrameLoopAnimator(flatDoc, adapter, {
139
+ onFinish: () => console.log('done'),
140
+ });
141
+ api.play();
142
+ ```
143
+
144
+ Frame scheduling resolves `requestAnimationFrame` from `globalThis` at call time
145
+ and falls back to `setTimeout`, so the engine works in browsers, React Native and
146
+ test environments with faked timers.
147
+
148
+ ## Versioning
149
+
150
+ Every package in this repo is released in lockstep. A player depends on the
151
+ matching core version (`^x.y.z`), so upgrading a player upgrades the core with it.
152
+
153
+ ## License
154
+
155
+ [MIT](../../LICENSE) ยฉ [Pixodesk](https://pixodesk.com)