react-voodoo 2.6.0 → 2.6.1

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,274 @@
1
+ # react-voodoo
2
+
3
+ Additive, swipeable, SSR-ready animation engine for React.
4
+
5
+ [![npm](https://img.shields.io/npm/v/react-voodoo)](https://www.npmjs.com/package/react-voodoo)
6
+ [![license](https://img.shields.io/badge/license-CC--BY--ND--4.0%20OR%20AGPL--3.0-blue)](#license)
7
+
8
+ ## Overview
9
+
10
+ react-voodoo drives animations by writing CSS **directly to the DOM**, bypassing React's render loop entirely. Scroll positions, drag gestures, and programmatic tweens all feed into the same additive accumulator, so multiple concurrent animations compose without conflict.
11
+
12
+ The engine is built on [tween-axis](../tween-axis/README.md) and uses its WebAssembly backend for hot-path property accumulation with zero JS-boundary crossings per frame.
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install react-voodoo
20
+ ```
21
+
22
+ Requires React 16, 17, or 18.
23
+
24
+ ---
25
+
26
+ ## Core concepts
27
+
28
+ ### Delta-based accumulation
29
+
30
+ Every animation operates on **deltas** — the change from the previous timeline position — rather than absolute target values. On each scroll or animation frame, `goTo(newPos, tweenRefMaps)` emits deltas that are added into a numeric accumulator (`tweenRefMaps`). A separate mux pass then converts those numbers into CSS strings and writes them to `node.style` directly.
31
+
32
+ This architecture enables:
33
+ - Multiple axes and one-shot animations to animate the same property simultaneously (additive composition).
34
+ - Direct DOM writes that skip React's reconciler entirely.
35
+ - Predictive inertia and physics-based scrolling with no render overhead.
36
+
37
+ ### CSS demux / mux
38
+
39
+ Tween descriptors specify CSS properties in their `apply` object. Before registering with `TweenAxis`, the `deMuxLine` pass converts each CSS value into a set of flat numeric keys (e.g. `transform_0_translateX_9`). On every frame the inverse `muxToCss` pass reconstructs the CSS strings (including `calc()` for multi-unit values) and writes them to the DOM.
40
+
41
+ ### WASM acceleration
42
+
43
+ For tween descriptors without event callbacks and with a standard easing function, `CssTweenAxis` (the internal axis class) automatically switches the process to `PROC_WASM` mode. The WebAssembly engine accumulates the property deltas directly inside WASM — no JS function call per property per frame. Results are flushed back into `tweenRefMaps` in a tight loop after `goTo()` returns.
44
+
45
+ Descriptors that use custom easing functions or event callbacks (`entering`/`moving`/`leaving`) are transparently handled by the JS path (`PROC_RESULT`), so there is no behavioural change — WASM acceleration applies automatically where possible.
46
+
47
+ ---
48
+
49
+ ## Components
50
+
51
+ ### `<Component>` / `asTweener`
52
+
53
+ The root animation engine. Wrap your animated tree with it to create a `Tweener` context.
54
+
55
+ ```jsx
56
+ import { Component } from "react-voodoo";
57
+ // or with the HOC:
58
+ import { tweener as asTweener } from "react-voodoo";
59
+
60
+ @asTweener
61
+ class MyScene extends React.Component { ... }
62
+ ```
63
+
64
+ ### `<Node>`
65
+
66
+ An animatable React element. Registers its CSS tween descriptors with the parent `Tweener`.
67
+
68
+ ```jsx
69
+ import { Node } from "react-voodoo";
70
+
71
+ <Node
72
+ id="card"
73
+ style={{ position: "absolute" }}
74
+ tweenLines={[
75
+ {
76
+ from: 0, duration: 500,
77
+ apply: { opacity: 1, transform: [{ translateY: "100px" }] },
78
+ easeFn: "easeQuadInOut",
79
+ }
80
+ ]}
81
+ />
82
+ ```
83
+
84
+ ### `<Axis>`
85
+
86
+ A scrollable animation timeline. Attach it to a scroll axis by `id`.
87
+
88
+ ```jsx
89
+ import { Axis } from "react-voodoo";
90
+
91
+ <Axis
92
+ id="scrollY"
93
+ size={1000}
94
+ defaultPosition={0}
95
+ inertia={{ snapToBounds: true, wayPoints: [{ at: 0 }, { at: 500 }] }}
96
+ />
97
+ ```
98
+
99
+ ### `<Draggable>`
100
+
101
+ Maps touch/mouse drag gestures to an axis position.
102
+
103
+ ```jsx
104
+ import { Draggable } from "react-voodoo";
105
+
106
+ <Draggable axisId="scrollY" />
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Hooks
112
+
113
+ ### `useVoodoo(opts?)`
114
+
115
+ Primary hook. Creates or inherits a `Tweener` instance and returns `[tweener, ViewBox]`.
116
+
117
+ ```jsx
118
+ import { useVoodoo } from "react-voodoo";
119
+
120
+ function Scene() {
121
+ const [tweener, ViewBox] = useVoodoo();
122
+ return <ViewBox>{...}</ViewBox>;
123
+ }
124
+ ```
125
+
126
+ ### `useTweener()`
127
+
128
+ Read-only access to the nearest parent `Tweener`.
129
+
130
+ ```jsx
131
+ import { useTweener } from "react-voodoo";
132
+
133
+ function Inner() {
134
+ const tweener = useTweener();
135
+ // tweener.scrollTo("scrollY", 500, 300);
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Tween descriptor
142
+
143
+ ```js
144
+ {
145
+ from: 0, // Start position on the axis (omit for sequential chaining)
146
+ duration: 500, // Length of this segment
147
+ target: "nodeId", // ID of the Node to animate
148
+ apply: {
149
+ opacity: 1,
150
+ transform: [{ translateX: "100px", translateY: "-50%" }],
151
+ // Multi-unit values use CSS calc():
152
+ width: ["50%", "10vw", "-50px"],
153
+ },
154
+ easeFn: "easeQuadInOut", // String key into TweenAxis.EasingFunctions (d3-ease)
155
+ entering: (delta) => {}, // Called when the process activates
156
+ moving: (pos, prev, delta) => {},
157
+ leaving: (delta) => {},
158
+ }
159
+ ```
160
+
161
+ ### Supported CSS properties
162
+
163
+ All standard animatable CSS properties are supported. Complex properties have dedicated demuxers:
164
+
165
+ | Property | Notes |
166
+ |----------|-------|
167
+ | `transform` | Array of layer objects; each key is a transform function (`translateX`, `rotate`, etc.) |
168
+ | `boxShadow` | Full multi-shadow support |
169
+ | `filter` | CSS filter functions |
170
+ | `backgroundColor` | RGBA component interpolation |
171
+ | `textShadow` | Multi-shadow support |
172
+ | `opacity`, `zIndex` | Numeric |
173
+ | `width`, `height`, `top`, `left`, `right`, `bottom`, margins, paddings, borders | Length with unit |
174
+ | SVG attributes (`cx`, `cy`, `r`, `x`, `y`, …) | Applied via `setAttribute` |
175
+
176
+ ### Multi-unit values
177
+
178
+ Arrays of CSS values are resolved with `calc()`:
179
+
180
+ ```js
181
+ width: ["50%", "10vw", "-50px"]
182
+ // → calc(50% + 10vw - 50px)
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Axis configuration
188
+
189
+ ```js
190
+ {
191
+ id: "scrollY",
192
+ defaultPosition: 0,
193
+ size: 1000, // Timeline length (matches tween descriptor positions)
194
+ bounds: { min: 0, max: 1000 },
195
+ inertia: {
196
+ snapToBounds: true,
197
+ wayPoints: [{ at: 0 }, { at: 500 }, { at: 1000 }],
198
+ willSnap: (index, wp) => {},
199
+ onSnap: (index, wp) => {},
200
+ },
201
+ }
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Tweener API (imperative)
207
+
208
+ The `Tweener` instance is accessible via `useVoodoo`, `useTweener`, or `tweenerOptions.ref`.
209
+
210
+ ### `tweener.scrollTo(axisId, position, durationMs?, easing?, noEvents?, tick?, cb?)`
211
+
212
+ Animate an axis to a position over time.
213
+
214
+ ### `tweener.addScrollableAnim(anim, axisId?)`
215
+
216
+ Register a tween line (array of descriptors) on a scroll axis at runtime. Returns the `CssTweenAxis` instance.
217
+
218
+ ### `tweener.rmScrollableAnim(sl, axisId?)`
219
+
220
+ Remove a previously registered tween line.
221
+
222
+ ### `tweener.pushAnim(descriptors, durationMs, cb?, keepResults?)`
223
+
224
+ Run a one-shot animation that plays to completion and then optionally releases its CSS values.
225
+
226
+ ### `tweener.watchAxis(axisId, listener)`
227
+
228
+ Subscribe to scroll position changes on an axis. Returns an unsubscribe function.
229
+
230
+ ### `tweener.getScrollPos(axisId)`
231
+
232
+ Return the current scroll position of an axis.
233
+
234
+ ---
235
+
236
+ ## WASM acceleration in CssTweenAxis
237
+
238
+ `CssTweenAxis` (the internal class used by every axis) automatically activates PROC_WASM for eligible tween descriptors:
239
+
240
+ **Eligible** (WASM path):
241
+ - No event callbacks (`entering` / `moving` / `leaving`).
242
+ - `easeFn` is `undefined`/`null` (linear) **or** one of the 10 mapped d3-ease functions.
243
+
244
+ **Not eligible** (JS path, no change in behaviour):
245
+ - Descriptors with event callbacks.
246
+ - Descriptors using custom easing functions not in the built-in set.
247
+
248
+ The 10 d3-ease functions that map to WASM built-ins:
249
+
250
+ | d3-ease name | WASM easing |
251
+ |--------------|-------------|
252
+ | *(none / linear)* | `EASE_LINEAR` |
253
+ | `easeQuadIn` / `easeQuadOut` / `easeQuadInOut` | `EASE_IN/OUT/INOUT_QUAD` |
254
+ | `easeCubicIn` / `easeCubicOut` / `easeCubicInOut` | `EASE_IN/OUT/INOUT_CUBIC` |
255
+ | `easeExpIn` / `easeExpOut` / `easeExpInOut` | `EASE_IN/OUT/INOUT_EXPO` |
256
+
257
+ No configuration is required — the selection happens automatically during axis construction.
258
+
259
+ ---
260
+
261
+ ## Building
262
+
263
+ ```bash
264
+ cd react-voodoo
265
+ npm run build # production build via lpack
266
+ npm run devLib # watch mode
267
+ npm run setupLayers # initialise lpack layer config
268
+ ```
269
+
270
+ ---
271
+
272
+ ## License
273
+
274
+ Dual-licensed: **CC-BY-ND-4.0** or **AGPL-3.0-only**. See [LICENSE](./LICENSE) for details.