@thednp/tween 0.0.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/LICENSE +21 -0
- package/README.md +259 -0
- package/dist/index.cjs +621 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +184 -0
- package/dist/index.d.mts +184 -0
- package/dist/index.mjs +610 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react.cjs +61 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +9 -0
- package/dist/react.d.mts +9 -0
- package/dist/react.mjs +55 -0
- package/dist/react.mjs.map +1 -0
- package/dist/solid.cjs +81 -0
- package/dist/solid.cjs.map +1 -0
- package/dist/solid.d.cts +9 -0
- package/dist/solid.d.mts +9 -0
- package/dist/solid.mjs +75 -0
- package/dist/solid.mjs.map +1 -0
- package/dist/tween.iife.js +2 -0
- package/dist/tween.iife.js.map +1 -0
- package/package.json +96 -0
- package/wiki/Easing.md +58 -0
- package/wiki/React.md +255 -0
- package/wiki/Solid.md +149 -0
- package/wiki/Timeline.md +207 -0
- package/wiki/Tween.md +230 -0
package/wiki/Tween.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
## Tween
|
|
2
|
+
|
|
3
|
+
A tiny, ultra-fast single-object tween engine. Simple, pure and flexible from/to animation with proper DX, chaining methods and support for nested objects. It's the core building block for more complex animations and a lightweight alternative to heavier tween libraries.
|
|
4
|
+
Perfect for reactive stores (SolidJS, Svelte, React, etc), SVG/Canvas animations, or anything needing a single precise tween without overhead.
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
* Chainable methods for best DX
|
|
8
|
+
* Proper relative-to-current start values (captured at `.start()` unless `.from()` used)
|
|
9
|
+
* Nested objects supported out-of-box
|
|
10
|
+
* Custom interpolators via `Tween.use()`
|
|
11
|
+
* Arrays, tuples, colors, paths via included interpolators
|
|
12
|
+
* Callbacks: `onUpdate` (with `elapsed` and eased `progress`), `onComplete`, `onStop`
|
|
13
|
+
* Manual `.update()` for custom timing loops
|
|
14
|
+
* ~150 lines, blazing fast
|
|
15
|
+
* `requestAnimationFrame` loop handled automatically when `.start()` called.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { Tween, Easing, interpolateArray } from '@thednp/tween';
|
|
22
|
+
|
|
23
|
+
// initial state
|
|
24
|
+
const obj = { x: 0, y: 0, rotate: 0, rgb: [255,0,0], opacity: 1 };
|
|
25
|
+
|
|
26
|
+
// Create tween
|
|
27
|
+
const tween1 = new Tween(obj)
|
|
28
|
+
// Optional: register interpolators (arrays work great for colors, vectors)
|
|
29
|
+
.use('rgb', interpolateArray)
|
|
30
|
+
.to({ x: 100, y: 200, rotate: 360, rgb: [0,255,0], opacity: 0.5 })
|
|
31
|
+
.duration(2) // 2 seconds
|
|
32
|
+
.delay(0.5) // 0.5s delay
|
|
33
|
+
.easing(Easing.Elastic.Out)
|
|
34
|
+
.onUpdate((state, elapsed, eased) => {
|
|
35
|
+
// elapsed: raw [0-1] progress
|
|
36
|
+
// eased: progress after easing function
|
|
37
|
+
// update DOM / store / canvas directly
|
|
38
|
+
console.log('Progress:', eased, state);
|
|
39
|
+
})
|
|
40
|
+
.onComplete((state) => {
|
|
41
|
+
console.log('Done!', state);
|
|
42
|
+
})
|
|
43
|
+
.start(); // begins animation right away
|
|
44
|
+
|
|
45
|
+
// Or chain from current values later
|
|
46
|
+
const tween2 = new Tween(obj)
|
|
47
|
+
.duration(1)
|
|
48
|
+
.easing(Easing.Back.InOut);
|
|
49
|
+
|
|
50
|
+
tween2.to({ x: 300 }).start();
|
|
51
|
+
// later:
|
|
52
|
+
tween2.to({ y: 400 }).startFromLast(); // continues from current values
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### API
|
|
56
|
+
|
|
57
|
+
#### `new Tween(initialValues)`
|
|
58
|
+
Creates a new **Tween** instance targeting the provided object, which means this object is updated during the update runtime.
|
|
59
|
+
|
|
60
|
+
#### `.to(endValues)`
|
|
61
|
+
Sets the target **end** values. Can be called multiple times; latest wins.
|
|
62
|
+
|
|
63
|
+
#### `.from(startValues)`
|
|
64
|
+
Explicitly sets **start** values (overrides auto-capture at start).
|
|
65
|
+
|
|
66
|
+
#### `.duration(seconds = 1)`
|
|
67
|
+
Sets animation duration in seconds (converted internally to ms).
|
|
68
|
+
|
|
69
|
+
#### `.delay(seconds = 0)`
|
|
70
|
+
Sets start delay in seconds. More complex arrangements might require delaying a tween before it actually starts running.
|
|
71
|
+
|
|
72
|
+
You can do that using the `delay` method:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
tween.delay(1.5)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This tween will start updating 1.5 seconds after the `start()` method has been called.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
#### `.easing(function = linear)`
|
|
82
|
+
Sets the easing function (from Easing object, custom or external). `Tween` will perform the interpolation between values (i.e. the easing) in a linear manner by default, so the change will be directly proportional to the elapsed time. This is predictable but also quite uninteresting visually wise.
|
|
83
|
+
|
|
84
|
+
This behaviour can be easily changed using the `easing()` method. For example:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { Tween, Easing } from '@thednp/tween'
|
|
88
|
+
// ...
|
|
89
|
+
tween.easing(Easing.Quadratic.In)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This will result in the tween slowly starting to change towards the final value, accelerating towards the middle, and then quickly reaching its final value. In contrast, `Easing.Quadratic.Out` would start changing quickly towards the value, but then slow down as it approaches the final value.
|
|
93
|
+
|
|
94
|
+
#### `.start(time?, overrideStart?)`
|
|
95
|
+
Starts the update loop and fires the `onStart` callback.
|
|
96
|
+
|
|
97
|
+
**Parameters**:
|
|
98
|
+
* Optional `time` - If you use it, the tween won't start until that particular moment in time; otherwise it will start as soon as possible (i.e. on the next call to `tween.update()`).
|
|
99
|
+
|
|
100
|
+
* Optional `overrideStart` forces re-capture of current values, which means that when `true`, a tween that we previously used will start from the values in the target object, instead of starting from the beginning. Useful for stopping a tween, then starting another one that will continue from the current location.
|
|
101
|
+
|
|
102
|
+
#### `.startFromLast(time?)`
|
|
103
|
+
Convenience: starts and forces re-capture of current values (for sequential tweens).
|
|
104
|
+
|
|
105
|
+
#### `.stop()`
|
|
106
|
+
Stops animation and fires `onStop` callback. Stopping a tween that was never started or that has already been stopped has no effect. No errors are thrown either.
|
|
107
|
+
|
|
108
|
+
#### `.update(time?, autoStart?)`
|
|
109
|
+
Updates the state and fires the `onUpdate` callback. Returns true if still active.
|
|
110
|
+
|
|
111
|
+
Individual tweens have an `update()` method to so that they can be updated over time in an animation loop, and on each update they will apply updated values to their target object.
|
|
112
|
+
|
|
113
|
+
The global update loop is handled automatically once you call `start()`:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// your defined tween
|
|
117
|
+
const tween = new Tween(someObject).to(/*...*/)
|
|
118
|
+
|
|
119
|
+
// later or anytime
|
|
120
|
+
tween.start()
|
|
121
|
+
```
|
|
122
|
+
When no active `Tween` objects remain, the global update loop stops automatically.
|
|
123
|
+
|
|
124
|
+
### Callbacks
|
|
125
|
+
|
|
126
|
+
Another powerful feature is to be able to run your own functions at specific times in each tween's life cycle. This is usually required when changing properties is not enough.
|
|
127
|
+
|
|
128
|
+
For example, suppose you're trying to animate some object whose properties can't be accessed directly but require you to call a setter instead. You can use an `update` callback to read the new updated values and then manually call the setters. All callbacks are passed the tweened `object` as the first parameter, and the second parameter as the [0-1] elapsed (or progress).
|
|
129
|
+
|
|
130
|
+
#### `.onStart(callback)`
|
|
131
|
+
Callback receives (`object`) parameter and is fired right before the tween starts animating, after any delay time specified by the `delay()` method.
|
|
132
|
+
|
|
133
|
+
It's great for synchronising to other events or triggering actions you want to happen when a tween starts.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
#### `.onUpdate(callback)`
|
|
137
|
+
Add a callback which receives (`object`, `elapsed`[0-1 raw], `value`[0-1 after easing]) parameters.
|
|
138
|
+
|
|
139
|
+
Executed each time the tween is updated, after the values have been actually updated.
|
|
140
|
+
|
|
141
|
+
#### `.onComplete(callback)`
|
|
142
|
+
A callback which receives (`object`) parameter when finished. Executed when a tween is finished normally (i.e. not stopped).
|
|
143
|
+
|
|
144
|
+
#### `.onStop(callback)`
|
|
145
|
+
A callback which receives (`object`) parameter and is fired when calling `stop()`, but not when it is completed normally.
|
|
146
|
+
|
|
147
|
+
#### Custom Interpolators
|
|
148
|
+
The `.use(propName: string, interpolationFunction: InterpolatorFunction)` allows you to add custom interpolator functions for your instance.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### Tween State
|
|
152
|
+
#### `.isPlaying`
|
|
153
|
+
Getter: `boolean` whether currently running.
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
### Custom Interpolators
|
|
157
|
+
|
|
158
|
+
Same as [Timeline](Timeline.md) - use the provided `interpolateArray` and `interpolatePath`.
|
|
159
|
+
|
|
160
|
+
**Example for colors**:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { Tween, interpolateArray } from "@thednp/tween";
|
|
164
|
+
|
|
165
|
+
new Tween({ rgb: [255,0,0] })
|
|
166
|
+
.use('rgb', interpolateArray)
|
|
167
|
+
.to({ rgb: [0,255,0] })
|
|
168
|
+
.onUpdate((state) => {
|
|
169
|
+
// update App state
|
|
170
|
+
// OR update DOM elements directly
|
|
171
|
+
Object.assign(
|
|
172
|
+
target.style,
|
|
173
|
+
{ "background-color": "rgb(" + state.rgb.join(",") + ")" }),
|
|
174
|
+
});
|
|
175
|
+
.duration(1.5)
|
|
176
|
+
.start();
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Example for SVG path**
|
|
180
|
+
|
|
181
|
+
The `interpolatePath` interpolator adds SVG morph capability and assumes compatible paths (same segment count/types and coordinate counts — use [svg-path-commander](https://github.com/thednp/svg-path-commander) to process if needed).
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { Tween, interpolatePath } from "@thednp/tween";
|
|
185
|
+
|
|
186
|
+
// Use a fast `PathArray` to string
|
|
187
|
+
// For faster performance use `pathToString` from svg-path-commander
|
|
188
|
+
function pathToString(path: ["M" | "C" | "L", ...number[]][]) {
|
|
189
|
+
return p.map(([c, ...args]) => c + args.join(",")).join(" ");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const path = document.getElementById("my-path");
|
|
193
|
+
// "M0,0 L600,0 L600,300 L600,600 L0,600 Z"
|
|
194
|
+
const square = [
|
|
195
|
+
["M", 0, 0],
|
|
196
|
+
["L", 600, 0],
|
|
197
|
+
["L", 600, 300], // mid
|
|
198
|
+
["L", 600, 600],
|
|
199
|
+
["L", 0, 600],
|
|
200
|
+
["Z"],
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
// "M0,0 L300,150 L600,300 L300,450 L0,600 Z"
|
|
204
|
+
const triangle = [
|
|
205
|
+
["M", 150, 0],
|
|
206
|
+
["L", 300, 150], // mid
|
|
207
|
+
["L", 450, 300],
|
|
208
|
+
["L", 300, 450], // mid
|
|
209
|
+
["L", 150, 600],
|
|
210
|
+
["Z"],
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
const tween = new Tween({ path: square })
|
|
214
|
+
// you can use any property name you want
|
|
215
|
+
.use('path', interpolatePath)
|
|
216
|
+
// `d` might be a good choice as well
|
|
217
|
+
.to({ path: triangle })
|
|
218
|
+
.onUpdate(state => {
|
|
219
|
+
// update App state
|
|
220
|
+
// OR update DOM elements directly
|
|
221
|
+
path.setAttribute('d', pathToString(state.path));
|
|
222
|
+
})
|
|
223
|
+
.duration(2)
|
|
224
|
+
.start();
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Notes**
|
|
228
|
+
* The example provides ready-made `PathArray` objects, they usually require prior preparation manually or using some script to [equalize segments](https://minus-ze.ro/posts/morphing-arbitrary-paths-in-svg/);
|
|
229
|
+
* Continuous `path` updates between multiple shapes requires that **all** path values are compatible, which means they all have same amount of segments and all segments are of the same type (ideal are `[[M, x, y], ...[L, x, y]], ` OR `[[M, x, y], ...[C, cx1, cy1, cx2, cy2, x, y]], `);
|
|
230
|
+
* Our [svg-path-commander](https://github.com/thednp/svg-path-commander/) provides all the tools necessary to process path strings, optimize and even equalize segments (work in progress).
|