@vune-ui/animation 0.1.20
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/ARCHITECTURE.md +470 -0
- package/CHANGELOG.md +88 -0
- package/LICENSE +21 -0
- package/PERFORMANCE.md +151 -0
- package/README.md +630 -0
- package/dist/index.d.ts +474 -0
- package/dist/src/canvas/index.d.ts +15 -0
- package/dist/src/canvas/index.js +67 -0
- package/dist/src/constraints/index.d.ts +33 -0
- package/dist/src/constraints/index.js +346 -0
- package/dist/src/core/bezier.js +51 -0
- package/dist/src/core/composition.js +17 -0
- package/dist/src/core/controls.js +22 -0
- package/dist/src/core/default-engine.js +20 -0
- package/dist/src/core/easing.js +58 -0
- package/dist/src/core/engine.js +1031 -0
- package/dist/src/core/frame-budget.js +30 -0
- package/dist/src/core/index.d.ts +43 -0
- package/dist/src/core/index.js +17 -0
- package/dist/src/core/js-spring-batch.js +57 -0
- package/dist/src/core/kinetics.js +140 -0
- package/dist/src/core/math.js +20 -0
- package/dist/src/core/motion-value.js +53 -0
- package/dist/src/core/planner.js +72 -0
- package/dist/src/core/specs.js +70 -0
- package/dist/src/dom/index.d.ts +41 -0
- package/dist/src/dom/index.js +364 -0
- package/dist/src/gesture/index.d.ts +66 -0
- package/dist/src/gesture/index.js +376 -0
- package/dist/src/index.js +53 -0
- package/dist/src/interpolate/color.js +223 -0
- package/dist/src/interpolate/css.d.ts +13 -0
- package/dist/src/interpolate/css.js +34 -0
- package/dist/src/interpolate/index.d.ts +13 -0
- package/dist/src/interpolate/index.js +55 -0
- package/dist/src/interpolate/transform.js +247 -0
- package/dist/src/layout/index.d.ts +56 -0
- package/dist/src/layout/index.js +485 -0
- package/dist/src/material/index.d.ts +9 -0
- package/dist/src/material/index.js +70 -0
- package/dist/src/path/index.d.ts +37 -0
- package/dist/src/path/index.js +527 -0
- package/dist/src/render/frame-batcher.js +52 -0
- package/dist/src/scroll/index.d.ts +55 -0
- package/dist/src/scroll/index.js +233 -0
- package/dist/src/timeline/index.d.ts +147 -0
- package/dist/src/timeline/index.js +849 -0
- package/dist/src/transition/index.d.ts +88 -0
- package/dist/src/transition/index.js +369 -0
- package/dist/src/wasm/index.d.ts +29 -0
- package/dist/src/wasm/index.js +8 -0
- package/dist/src/wasm/loader.js +55 -0
- package/dist/src/wasm/shared-wasm-spring-batch.js +52 -0
- package/dist/src/wasm/wasm-spring-batch.js +52 -0
- package/dist/src/webgl/index.d.ts +22 -0
- package/dist/src/webgl/index.js +94 -0
- package/dist/src/webgpu/index.d.ts +35 -0
- package/dist/src/webgpu/index.js +73 -0
- package/dist/src/webgpu/spring-batch.js +218 -0
- package/dist/src/worker/index.d.ts +17 -0
- package/dist/src/worker/index.js +1 -0
- package/dist/src/worker/shared-spring-worker.js +218 -0
- package/dist/src/worker/shared-worker.js +75 -0
- package/dist/wasm/kernel-scalar.wasm +0 -0
- package/dist/wasm/kernel-shared-scalar.wasm +0 -0
- package/dist/wasm/kernel-shared-simd.wasm +0 -0
- package/dist/wasm/kernel-simd.wasm +0 -0
- package/package.json +113 -0
package/PERFORMANCE.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Performance snapshot
|
|
2
|
+
|
|
3
|
+
Measurements below include a fresh 0.7.0 verification run. The existing spring/Worker/timeline paths are unchanged semantically; new scroll, constraint, and renderer-bridge measurements were collected in the same Node environment. They are microbenchmarks unless explicitly stated and do not include browser layout, paint, compositing, framework overhead, or real input latency.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Node.js v22.16.0
|
|
7
|
+
Clang 17.0.0 wasm32 target
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Dense spring kernel
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm run bench -- 10000 600
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
JS 66.12 ms total | 0.1102 ms/frame | 10000 springs
|
|
18
|
+
WASM-simd 13.65 ms total | 0.0227 ms/frame | 10000 springs
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The design property that matters more than the exact ratio is unchanged: the WASM path evaluates a dense Structure-of-Arrays batch instead of crossing the JS/WASM boundary once per value. After promotion, active spring state remains in WASM linear memory.
|
|
22
|
+
|
|
23
|
+
## Shared Worker path
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm run bench:worker -- 10000 300
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
JS 40.02 ms solver wall | 0.1334 ms/frame | 10000 active springs
|
|
31
|
+
WASM-simd 7.43 ms solver wall | 0.0248 ms/frame | 10000 active springs
|
|
32
|
+
Worker-simd 20.08 ms solver wall | 0.0669 ms/frame | 10000 active springs
|
|
33
|
+
Worker submit 0.00086 ms/frame main-thread submission cost
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Worker wall time is expected to vary with scheduler noise. Its main benefit is moving a large numeric workload away from the UI thread while keeping the same shared state buffers. `Atomics.waitAsync()` is used when available, so supported runtimes do not need a per-frame Worker completion message.
|
|
37
|
+
|
|
38
|
+
## Compiled timeline/keyframe tracks
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm run bench:timeline -- 1000 600
|
|
42
|
+
npm run bench:timeline -- 10000 600
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
Timeline 36.46 ms total | 0.0608 ms/frame | 1000 numeric tracks | 600 frames
|
|
47
|
+
Timeline 336.66 ms total | 0.5611 ms/frame | 10000 numeric tracks | 600 frames
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The timeline benchmark uses three-keyframe numeric tracks with cubic-bezier easing and velocity propagation. Keyframe times and numeric values are compiled into typed arrays. Built-in cubic-bezier curves are compiled once into a shared 256-entry lookup table, avoiding Newton/bisection solving for every track on every frame.
|
|
51
|
+
|
|
52
|
+
The lookup path was verified against the exact cubic-bezier evaluator with a normalized position error below `0.0002` across the built-in non-linear curves. Numeric timeline sampling itself creates no per-frame timeline metadata objects.
|
|
53
|
+
|
|
54
|
+
Timeline drivers receive real elapsed wall time even though spring solvers keep their own defensive `dt` clamp. That lets a finite timeline catch up immediately after a suspended tab. Infinite-repeat callbacks are coalesced into one callback per engine frame with a `crossedIterations` count, so a long suspension cannot create an unbounded callback loop.
|
|
55
|
+
|
|
56
|
+
## SVG path morph
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
npm run bench:path -- 256 10000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
Path preprocess 10.41 ms | 256 segments
|
|
64
|
+
Numeric sample 23.43 ms | 0.00234 ms/frame | 10000 frames
|
|
65
|
+
String format 544.45 ms | 0.54445 ms/frame | 1000 frames
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
After path topology preprocessing, the numeric morph is cheap. For long SVG paths, formatting the `d` string dominates. `PathMorpher.sampleInto()` exists so Canvas/WebGL/custom renderers can consume the reusable numeric buffer without string serialization.
|
|
69
|
+
|
|
70
|
+
## Gesture / direct-manipulation primitives
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npm run bench:gestures -- 500000
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
rubber-band 6.99 ms | 14.0 ns/op
|
|
78
|
+
analytic decay 13.15 ms | 26.3 ns/op
|
|
79
|
+
analytic bounce spring 32.55 ms | 65.1 ns/op
|
|
80
|
+
velocity regression 83.89 ms | 335.6 ns/sample
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`VelocityTracker` stores history in a fixed typed-array ring buffer. Release physics remains caller-thread analytical math because a drag generally owns one or two axes; dispatching that amount of arithmetic to WASM/Worker would cost more than computing it locally.
|
|
84
|
+
|
|
85
|
+
## MotionValue / DOM allocation policy
|
|
86
|
+
|
|
87
|
+
`MotionValue.subscribeValue()` is the value-only hot subscription path. Internal DOM, structured interpolation, and layout-progress bindings use it when available, so ordinary render bindings do not allocate `{ previous, velocity, version }` metadata on each frame. The older detailed `subscribe()` API remains compatible for consumers that need velocity/version metadata.
|
|
88
|
+
|
|
89
|
+
The DOM adapter also keeps one global dirty-element queue, so a frame updating many elements schedules one flush rather than one microtask per element.
|
|
90
|
+
|
|
91
|
+
## Interpretation
|
|
92
|
+
|
|
93
|
+
Exact values are machine- and workload-dependent. Backend thresholds are policy inputs, not universal constants. `FrameBudgetGovernor` exists so an integration can respond to measured main-thread pressure rather than tuning around one benchmark machine.
|
|
94
|
+
|
|
95
|
+
## State transitions and matched layout (0.6.0)
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
npm run bench:transitions -- 10000 1000
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Final median-of-nine verification run:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
state graph numeric 0.2851 ms/frame | 10000 MotionValues
|
|
105
|
+
state graph shared 0.1764 ms/frame | 10000 callback values, 1 progress animation
|
|
106
|
+
shared layout setup 5.3111 ms total | 1000 matched targets
|
|
107
|
+
shared layout write 0.2963 ms/frame | 1000 matched targets, 1 progress animation
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The numeric state-graph case uses long-running timing animations so all 10,000 values stay active for the measured frames. Spring-based state changes use the existing dense spring backend and can therefore promote to WASM/Worker under the normal thresholds.
|
|
111
|
+
|
|
112
|
+
The structured case routes 10,000 callback values through one scalar progress animation. Numeric callback interpolation is inlined (`from + delta * progress`) and current structured values are only snapshotted on interruption/completion instead of being written to a `Map` every frame.
|
|
113
|
+
|
|
114
|
+
Shared-layout setup includes keyed snapshot matching, target measurement, projection construction, decomposition, and the initial write for 1,000 synthetic Element-like targets. Steady-state projection reuses one six-number matrix buffer per item and writes `will-change`/`transform-origin` only at setup, not every frame. This cut the synthetic 1,000-target write path from roughly 0.7 ms/frame during development to about 0.32 ms/frame in the final run.
|
|
115
|
+
|
|
116
|
+
This is still not a browser rendering benchmark: style recalculation, real layout, paint, and compositing are excluded. The important invariant is that one matched group consumes one motion solver entry regardless of target count.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## Scroll tracker and constraint graph (0.7.0)
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
npm run bench:constraints -- 10000 1000
|
|
123
|
+
npm run bench:scroll -- 500000
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Verification run:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
Constraints 10000: 124.61 ms total | 0.1246 ms/frame | 10000 chained relations
|
|
130
|
+
ScrollTracker 500000: 201.23 ms total | 402.5 ns/sample
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The constraint benchmark is intentionally adversarial: 10,000 affine relations form one dependency chain, so every result depends on the previous one and all 10,000 instructions must execute. The graph is already compiled before timing. Steady-state evaluation uses typed value/velocity arrays and a compact topological instruction stream.
|
|
134
|
+
|
|
135
|
+
The scroll benchmark includes `VelocityTracker` regression plus offset/progress `MotionValue` commits. Real DOM scrolling is normally cheaper in aggregate because delivered events are coalesced and scroll metrics are read once per animation frame rather than once per raw event.
|
|
136
|
+
|
|
137
|
+
## Renderer bridges (0.7.0)
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
npm run bench:renderers -- 1000 500
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Synthetic verification run with 1,000 scalar `MotionValue`s changing on every frame:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
Canvas snapshot 1000: 0.0337 ms/frame | one draw callback
|
|
147
|
+
WebGL uniforms 1000: 0.0561 ms/frame | 1000 uniform writes/frame
|
|
148
|
+
WebGPU buffer 1000: 0.0361 ms/frame | one writeBuffer/frame
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
These numbers measure JavaScript adapter overhead against fake renderer APIs, not actual GPU driver cost or rasterization. The useful invariant is submission shape: Canvas gets one retained numeric snapshot and one draw callback, while WebGPU packs all 1,000 scalars into one retained staging array and one buffer upload. WebGL still requires one API uniform write per declared scalar uniform; real high-count data should therefore be packed into UBOs/textures/buffers rather than represented as thousands of standalone uniforms.
|