@thednp/tween 0.0.1 → 0.0.2
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 +112 -109
- package/dist/preact/preact.d.mts +62 -0
- package/dist/preact/preact.d.mts.map +1 -0
- package/dist/preact/preact.mjs +181 -0
- package/dist/preact/preact.mjs.map +1 -0
- package/dist/react/react.d.mts +62 -0
- package/dist/react/react.d.mts.map +1 -0
- package/dist/react/react.mjs +183 -0
- package/dist/react/react.mjs.map +1 -0
- package/dist/solid/solid.d.mts +60 -0
- package/dist/solid/solid.d.mts.map +1 -0
- package/dist/solid/solid.mjs +157 -0
- package/dist/solid/solid.mjs.map +1 -0
- package/dist/svelte/svelte.d.mts.map +1 -0
- package/dist/svelte/svelte.mjs.map +1 -0
- package/dist/svelte/tween.svelte.d.ts +58 -0
- package/dist/svelte/tween.svelte.js +156 -0
- package/dist/tween/index.d.mts +939 -0
- package/dist/tween/index.d.mts.map +1 -0
- package/dist/tween/index.mjs +1929 -0
- package/dist/tween/index.mjs.map +1 -0
- package/dist/tween.min.js +8 -0
- package/dist/tween.min.js.map +1 -0
- package/dist/vue/vue.d.mts +61 -0
- package/dist/vue/vue.d.mts.map +1 -0
- package/dist/vue/vue.mjs +157 -0
- package/dist/vue/vue.mjs.map +1 -0
- package/package.json +56 -40
- package/dist/index.cjs +0 -621
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -184
- package/dist/index.d.mts +0 -184
- package/dist/index.mjs +0 -610
- package/dist/index.mjs.map +0 -1
- package/dist/react.cjs +0 -61
- package/dist/react.cjs.map +0 -1
- package/dist/react.d.cts +0 -9
- package/dist/react.d.mts +0 -9
- package/dist/react.mjs +0 -55
- package/dist/react.mjs.map +0 -1
- package/dist/solid.cjs +0 -81
- package/dist/solid.cjs.map +0 -1
- package/dist/solid.d.cts +0 -9
- package/dist/solid.d.mts +0 -9
- package/dist/solid.mjs +0 -75
- package/dist/solid.mjs.map +0 -1
- package/dist/tween.iife.js +0 -2
- package/dist/tween.iife.js.map +0 -1
- package/wiki/Easing.md +0 -58
- package/wiki/React.md +0 -255
- package/wiki/Solid.md +0 -149
- package/wiki/Timeline.md +0 -207
- package/wiki/Tween.md +0 -230
package/README.md
CHANGED
|
@@ -1,30 +1,75 @@
|
|
|
1
1
|
## @thednp/tween
|
|
2
|
+
|
|
2
3
|
[](https://coveralls.io/github/thednp/tween)
|
|
3
|
-
[](https://www.npmjs.com/package/@thednp/tween)
|
|
5
|
+
[](https://github.com/thednp/tween/actions/workflows/ci.yml)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
A Typescript sourced tweening engine forked from the excellent [@tweenjs/tweenjs](https://github.com/tweenjs/tween.js). This package has some strongly opinionated implementations for the best performance and DX.
|
|
9
|
+
|
|
10
|
+
Major popular frameworks supported:
|
|
11
|
+
|
|
12
|
+
[<img width="32" height="32" src="wiki/assets/react.svg" alt="React" />](wiki/React.md)
|
|
13
|
+
[<img width="32" height="32" src="wiki/assets/solid.svg" alt="SolidJS" />](wiki/Solid.md)
|
|
14
|
+
[<img width="32" height="32" src="wiki/assets/preact.svg" alt="Preact" />](wiki/Preact.md)
|
|
15
|
+
[<img width="32" height="32" src="wiki/assets/svelte.svg" alt="Svelte" />](wiki/Svelte.md)
|
|
16
|
+
[<img width="32" height="32" src="wiki/assets/vue.svg" alt="Vue" />](wiki/Vue.md)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Why @thednp/tween?
|
|
20
|
+
|
|
21
|
+
It seems **we got it all wrong regarding tweening** since the beginning. We've been working with unpredictable patterns and high risk for errors. We've been doing too much value checking during hot update loop or sometimes got too close to the DOM, to the point that makes it hard to integrate in modern tech. What if... we can change all that?
|
|
7
22
|
|
|
8
|
-
|
|
23
|
+
**Why you would like it?**
|
|
9
24
|
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
25
|
+
* It's really easy to use thanks to the built in support for popular UI frameworks via SSR compatible hooks/primitives/composables (hooks only provide initial values required for server rendering runtime and initialization is skipped entirely, hooks only work with hydration of server rendered HTML or single page apps - SPA).
|
|
26
|
+
* It's the solution for a predictable outcome: validate values before and never at the update runtime, explicit specification instead of a guessing game, no object lookup or any kind of overhead.
|
|
27
|
+
* While you can do quite complex animations with CSS3 alone, it's really poor DX to go back and forth and write complex animations to which you have little to no control.
|
|
28
|
+
* The package comes with a feature rich validation system, all to make sure your animations run smooth and never break your app. Values aren't valid? Animation won't start. Missed a configuration step? You will be provided with feedback.
|
|
29
|
+
* SVG math can be troublesome because of its own coordinates system, SVG transforms are also a thing, but with a little learning and our tweening engine you can do anything you really want.
|
|
13
30
|
|
|
14
31
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
32
|
+
## Features
|
|
33
|
+
* The package includes `Tween` class for creating most simple tween objects, with most essential controls, callbacks and methods for sequencing.
|
|
34
|
+
* For more complex scheduling and easier sequencing this package has a simple to use `Timeline` class with similar controls, callbacks, but most importantly it allows setting **per-property** duration, start time / delay and easing.
|
|
35
|
+
* There are also a series of custom hooks/primitives for popular frameworks like React/SolidJS/Svelte/Preact/Vue (more to come), with proper documentation and detailed user guides.
|
|
36
|
+
* Yoyo and generally reverse playback works naturally with inverted easing function (without a `reverseEasing` option) and without re-assigning a `valuesStart` object on repeat iteration end like the original library.
|
|
37
|
+
* By default, only `number` values are supported, which is fine for most use cases, but both `Tween` and `Timeline` provide a way to extend beyond the original prototype. You can use the built-in [extensions](wiki/Extend.md) or creare your own to provide per property validation and interpolation with your design specification. The only limit is that objects are limited to a one level nesting and must be plain objects.
|
|
38
|
+
* All values are **always** validated on initialization from a given `initialValues` object, which, once validated, it becomes the source of truth to the type of values coming later from `to()` / `from()`.
|
|
39
|
+
* Automatic `requestAnimationFrame` loop (you won't need to handle it yourself), it starts when you call `tween.start()` or `timeline.play()` and stops when all tweens are complete.
|
|
40
|
+
* The package has some micro-optimizations for maximum performance:
|
|
41
|
+
- All loops are executed with `while`;
|
|
42
|
+
- No value validation of any kind during the update loop;
|
|
43
|
+
- Supported frameworks (via hooks/primitives/composables) use a `miniStore` designed to store tween values and trigger updates/effects in your UI efficiently, with zero GC pressure, no object lookup or re-assignment, just pure linear interpolation;
|
|
44
|
+
- The hot update runtime consists of tuples, to optimize GC presure and eliminate object lookup.
|
|
25
45
|
|
|
26
46
|
|
|
27
|
-
###
|
|
47
|
+
### Take a minute to check a quick demo
|
|
48
|
+
|
|
49
|
+
[<img width="32" height="32" src="wiki/assets/react.svg" alt="React" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/react)
|
|
50
|
+
[<img width="32" height="32" src="wiki/assets/solid.svg" alt="SolidJS" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/solid)
|
|
51
|
+
[<img width="32" height="32" src="wiki/assets/preact.svg" alt="Preact" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/preact)
|
|
52
|
+
[<img width="32" height="32" src="wiki/assets/svelte.svg" alt="Svelte" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/svelte)
|
|
53
|
+
[<img width="32" height="32" src="wiki/assets/vue.svg" alt="Vue" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/vue)
|
|
54
|
+
|
|
55
|
+
Your favorite framework isn't listed? [Let us know](https://github.com/thednp/tween/issues/new)!
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Documentation
|
|
59
|
+
* [Tween Guide](wiki/Tween.md) - the official `Tween` documentation
|
|
60
|
+
* [Timeline Guide](wiki/Timeline.md) - the official `Timeline` documentation
|
|
61
|
+
* [Easing Guide](wiki/Easing.md) - the easing functions documentation
|
|
62
|
+
* [Extend Guide](wiki/Extend.md) - the extensions documentation
|
|
63
|
+
* [React Guide](wiki/React.md) - the React documentation (custom hooks)
|
|
64
|
+
* [SolidJS Guide](wiki/Solid.md) - the SolidJS documentation (custom primitives, tips)
|
|
65
|
+
* [Vue Guide](wiki/Vue.md) - the Vue documentation (composables, tips)
|
|
66
|
+
* [Preact Guide](wiki/Preact.md) - the Preact documentation (custom hooks, tips)
|
|
67
|
+
* [Svelte Guide](wiki/Svelte.md) - the Svelte documentation (runes)
|
|
68
|
+
* [The original Tween.js User Guide](https://github.com/tweenjs/tween.js/blob/main/docs/user_guide.md) can also provide valuable tips.
|
|
69
|
+
* [Troubleshooting](wiki/Troubleshooting) - a quick check on issues and how to solve them.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
### Installation
|
|
28
73
|
```
|
|
29
74
|
npm install @thednp/tween
|
|
30
75
|
```
|
|
@@ -44,6 +89,8 @@ bun add @thednp/tween
|
|
|
44
89
|
|
|
45
90
|
### Usage
|
|
46
91
|
|
|
92
|
+
To use `Tween` and `Timeline` with UI frameworks please check the dedicated sections: [React](wiki/React.md), [SolidJS](wiki/Solid.md), [Svelte](wiki/Svelte.md), [Preact](wiki/Preact.md) and [Vue](wiki/Vue.md).
|
|
93
|
+
|
|
47
94
|
#### Using Tween
|
|
48
95
|
```ts
|
|
49
96
|
import { Tween, Easing } from '@thednp/tween';
|
|
@@ -54,13 +101,11 @@ const target = document.getElementById('my-target');
|
|
|
54
101
|
// define a tween
|
|
55
102
|
const tween = new Tween({ x: 0 })
|
|
56
103
|
.duration(1.5) // duration/delay accept seconds (e.g., 1.5 = 1.5s)
|
|
57
|
-
.onUpdate((obj, elapsed
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
Object.assign(target.style, { translate: obj.x + "px"});
|
|
104
|
+
.onUpdate((obj, elapsed) => {
|
|
105
|
+
// manipulate the DOM directly
|
|
106
|
+
Object.assign(target.style, { translate: obj.x + "px" });
|
|
61
107
|
// monitor progress of the tween
|
|
62
108
|
console.log(`Tween progress: ${Math.floor(elapsed * 100)}%`)
|
|
63
|
-
// do other stuff with the `eased` value
|
|
64
109
|
});
|
|
65
110
|
|
|
66
111
|
// override any value on the fly
|
|
@@ -72,6 +117,7 @@ const moveRight = () => tween
|
|
|
72
117
|
.start(); // start the tween
|
|
73
118
|
|
|
74
119
|
const moveLeft = () => tween
|
|
120
|
+
.from({ x: 150 }) // set a different from
|
|
75
121
|
.to({ x: -150 }) // set a different to
|
|
76
122
|
.easing(Easing.Elastic.Out) // override easing
|
|
77
123
|
.duration(1.5) // override duration in seconds
|
|
@@ -86,6 +132,8 @@ button2.onclick = moveLeft;
|
|
|
86
132
|
|
|
87
133
|
// The engine does requestAnimationFrame/cancelAnimationFrame for you
|
|
88
134
|
```
|
|
135
|
+
For an extended guide, check the [Tween Wiki](wiki/Tween.md).
|
|
136
|
+
|
|
89
137
|
|
|
90
138
|
#### Using Timeline
|
|
91
139
|
```ts
|
|
@@ -99,145 +147,100 @@ const myTimeline = new Timeline({ x: 0, y: 0 })
|
|
|
99
147
|
.to({ x: 150, duration: 2.5, easing: Easing.Elastic.Out })
|
|
100
148
|
.to({ y: 150, duration: 1.5, easing: Easing.Elastic.Out }, "-=1")
|
|
101
149
|
.onUpdate((obj, elapsed) => {
|
|
102
|
-
//
|
|
103
|
-
// OR manipulate the DOM directly
|
|
150
|
+
// manipulate the DOM directly
|
|
104
151
|
Object.assign(target.style, {
|
|
105
152
|
translate: obj.x + "px " + obj.y + "px",
|
|
106
153
|
});
|
|
107
|
-
// monitor progress of the
|
|
154
|
+
// monitor progress of the timeline
|
|
108
155
|
console.log(`Timeline progress: ${Math.floor(elapsed * 100)}%`)
|
|
109
156
|
});
|
|
110
157
|
|
|
111
158
|
// trigger any time
|
|
112
|
-
const button = document.getElementById('my-button
|
|
159
|
+
const button = document.getElementById('my-button');
|
|
113
160
|
|
|
114
161
|
button.onclick = myTimeline.play();
|
|
115
162
|
|
|
116
163
|
// The engine does requestAnimationFrame/cancelAnimationFrame for you
|
|
117
164
|
```
|
|
118
|
-
|
|
119
|
-
|
|
165
|
+
For an extended guide, check the [Timeline Wiki](wiki/Timeline.md).
|
|
120
166
|
|
|
121
|
-
### What is different from original?
|
|
122
167
|
|
|
123
|
-
|
|
124
|
-
This `Tween` version is very small, maybe half the size of the current original version. It was developed to create easy to use hooks for UI frameworks like Solid/React and enable customizable animations.
|
|
125
|
-
In fact this is closer to the earlier versions of the original TWEEN.Tween.js.
|
|
168
|
+
### What is Different from Original?
|
|
126
169
|
|
|
170
|
+
#### Looking Forward
|
|
171
|
+
On the surface, most changes seem superficial, but our `Tween` version is very different from the current source version. It was developed to create easy to use hooks for major popular UI frameworks and deliver a trully predictable outcome.
|
|
127
172
|
|
|
128
173
|
#### New Features
|
|
129
|
-
This package comes with `Timeline`, which works like a regular `Tween` under the hood, but it provides additional control methods like `
|
|
174
|
+
This package comes with `Timeline`, which works like a regular `Tween` under the hood, but it provides additional control methods like `seek()` or `label()` and allows per property easing, duration and start time / delay options.
|
|
130
175
|
|
|
131
|
-
Both `Tween` and `Timeline` have a
|
|
132
|
-
|
|
133
|
-
#### Other Notable Changes
|
|
134
|
-
* Some features like `yoyo`, `repeat`, `repeatDelay`, `chain` and
|
|
135
|
-
* callback options like `onRepeat` or `onEveryStart`, `onFirstStart` are **not** implemented;
|
|
136
|
-
* `duration()` and `delay()` methods accept values in seconds and convert them to milliseconds;
|
|
137
|
-
* The `pause()` and `resume()` methods have **not** been implemented in `Tween`, but they are implemented in `Timeline`;
|
|
138
|
-
* The update loop which consists of `requestAnimationFrame` / `cancelAnimationFrame` calls is automatic and is integrated in the `Tween` methods;
|
|
139
|
-
* The `onUpdate` callback also uses the value calculated by the easing function as the third parameter of your callback;
|
|
140
|
-
* The original Tween.js array interpolation is **not** supported, however we have a static method to add custom interpolators.
|
|
176
|
+
Both `Tween` and `Timeline` have a method to add a custom **per property extension** that consists of a function to validate and another to interpolate, which is a unique way to extend beyond the original design and very different from the original library.
|
|
141
177
|
|
|
178
|
+
**Great DX**: Along with type safety, `Tween` and `Timeline` will validate your values on initialization or (re)configuration to enforce it. Calling `Tween.to()`, `Tween.from()` or `Timeline.to()` will use the **validation system** to provide feedback on what went wrong and how to fix, in most cases issues with invalid/incompatible values are mapped internally and only cleared once validated/re-validated. This is to make sure to **never crash** your app.
|
|
142
179
|
|
|
143
|
-
|
|
144
|
-
* The
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* [
|
|
149
|
-
*
|
|
180
|
+
#### Other Notable Changes
|
|
181
|
+
* The `chain` feature is **not** implemented;
|
|
182
|
+
* Callback options like `onEveryStart`, `onFirstStart` are **not** implemented;
|
|
183
|
+
* The `duration()`, `delay()`, `repeatDelay()` or `seek()` methods accept values in seconds and convert them to milliseconds;
|
|
184
|
+
* The update loop which consists of `requestAnimationFrame` is automatic and a queue system is integrated in the `Tween` and `Timeline` methods;
|
|
185
|
+
* The original [Tween.js](https://tweenjs.github.io/tween.js/examples/06_array_interpolation.html) array interpolation is **not** supported;
|
|
186
|
+
* Deeply nested objects are **not supported**, actively discouraged, objects in general are known to have very bad performance metrics;
|
|
187
|
+
* Dynamic end values like the original [Tween.js](https://tweenjs.github.io/tween.js/examples/07_dynamic_to.html) cannot be supported due to the intrinsic changes in the update runtime.
|
|
150
188
|
|
|
151
189
|
|
|
152
190
|
### Technical Notes & Design Choices
|
|
153
191
|
|
|
154
|
-
**@thednp/tween** is intentionally designed as a **
|
|
155
|
-
|
|
156
|
-
#### Why declarative state-based animation?
|
|
192
|
+
**@thednp/tween** is intentionally designed as a **state-first** tweening engine. Here's why certain choices were made and how the system works under the hood.
|
|
157
193
|
|
|
158
|
-
|
|
194
|
+
#### Mini-Stores for Supported Frameworks
|
|
159
195
|
|
|
160
|
-
|
|
196
|
+
Each supported framework make use of a highly specialized `miniStore` to hold tween values and update your UI. This is to ensure great DX and eliminate GC pressure. Even React can work amazing. Check the [Ministore wiki](wiki/Ministore.md) for details.
|
|
161
197
|
|
|
162
|
-
|
|
163
|
-
- The engine interpolates **from current state** (captured at `.start()` / `.play()`)
|
|
164
|
-
- No DOM reading/parsing ever happens
|
|
165
|
-
- No per browser handling/processing
|
|
198
|
+
#### How the Global Update Loop Works
|
|
166
199
|
|
|
167
|
-
**
|
|
168
|
-
- Zero runtime style/attribute parsing → much faster startup & safer in concurrent React/Solid renders
|
|
169
|
-
- Perfect for **state-based UI** (React, SolidJS, Vue, Svelte, etc.) — animate your app state, not the DOM directly
|
|
170
|
-
- No surprises from inherited styles, CSS transitions, or computed values
|
|
171
|
-
- Works equally well with **Canvas**, **SVG**, **Three.js**, **WebGL**, or **pure data** — no DOM dependency at all
|
|
172
|
-
- Considerably smaller bundle size (no style parsing code)
|
|
173
|
-
- More power to you due to the simplicity or the prototype
|
|
200
|
+
All tween objects and timelines share **one single `requestAnimationFrame` loop** managed by `Runtime.ts`.
|
|
174
201
|
|
|
175
|
-
|
|
176
|
-
- You must manage state yourself (which is the norm in modern frameworks anyway)
|
|
177
|
-
- You must process complex values yourself (values for SVG path morph)
|
|
178
|
-
|
|
179
|
-
This makes `@thednp/tween` feel more like **a reactive state interpolator** than a traditional DOM tweener.
|
|
180
|
-
|
|
181
|
-
#### How the global update loop works
|
|
182
|
-
|
|
183
|
-
All animations share **one single `requestAnimationFrame` loop** managed by `Runtime.ts`.
|
|
184
|
-
|
|
185
|
-
- When you call `.start()` / `.play()`, the instance is added to a global `Queue`
|
|
202
|
+
- When you call `tween.start()` / `timeline.play()`, the instance is added to a global `Queue`
|
|
186
203
|
- `Runtime()` runs every frame → calls `.update(time)` on every queued item
|
|
187
204
|
- If `.update()` returns `false` (finished/stopped), the item is removed from the Queue
|
|
188
|
-
- When `Queue` becomes empty → `cancelAnimationFrame` is called automatically → loop stops completely
|
|
205
|
+
- When `Queue` becomes empty → `cancelAnimationFrame` is called automatically → loop stops completely.
|
|
189
206
|
|
|
190
207
|
**Benefits**:
|
|
191
|
-
- Only **one**
|
|
208
|
+
- Only **one** `requestAnimationFrame` subscription for the entire app — extremely efficient
|
|
192
209
|
- No manual start/stop of animation loop per tween/timeline
|
|
193
210
|
- Zero overhead when nothing is animating
|
|
194
211
|
|
|
195
212
|
This shared loop is why you never need to worry about starting/stopping individual `requestAnimationFrame` calls.
|
|
196
213
|
|
|
197
|
-
|
|
214
|
+
|
|
215
|
+
#### Async Nature of `requestAnimationFrame`
|
|
198
216
|
|
|
199
217
|
All updates are **async** by nature:
|
|
200
218
|
|
|
201
|
-
1. You call
|
|
202
|
-
2. Next `
|
|
219
|
+
1. You call `tween.start()` / `timeline.play()` → instance is queued (added to the main update loop)
|
|
220
|
+
2. Next `requestAnimationFrame` tick → `Runtime()` calls `instance.update(time)` → interpolates values → determines whether to call `cancelAnimationFrame` or continue
|
|
203
221
|
3. DOM/state updates happen **on the next frame(s)** — never synchronous
|
|
204
222
|
|
|
205
223
|
This means:
|
|
206
224
|
- Visual changes are always **smooth** and **tied to the display refresh rate**
|
|
207
|
-
-
|
|
208
|
-
- No risk of partial/inconsistent frames (all calculations happen before paint)
|
|
225
|
+
- Very low risk of partial/inconsistent frames (most calculations happen before paint, depending on the stack size and GC queue)
|
|
209
226
|
|
|
210
227
|
#### Server-Side Rendering (SSR) compatibility
|
|
211
228
|
|
|
212
|
-
|
|
229
|
+
**@thednp/tween** is **SSR-safe** out of the box:
|
|
213
230
|
|
|
214
231
|
- No DOM access anywhere in the core
|
|
215
232
|
- `requestAnimationFrame` / `cancelAnimationFrame` are only called in browser (via `Runtime()`)
|
|
216
|
-
- `now()` defaults to `performance.now()
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
Just make sure to **not call `tween.start()` / `timeline.play()`** during SSR (e.g. wrap in `if (typeof window !== 'undefined')` or use `useEffect`).
|
|
220
|
-
|
|
221
|
-
#### Other important differences from classic tween libraries
|
|
233
|
+
- `now()` defaults to `performance.now()`, but you can switch to `Date.now()` to create safe fallbacks in Node
|
|
234
|
+
- All supported UI frameworks have consistent guards to prevent execution during server rendering, but also provide values required in the rendered HTML.
|
|
222
235
|
|
|
223
|
-
|
|
224
|
-
|------------------------------------|-------------------------------------------|--------------------------------------------|
|
|
225
|
-
| Animation target | DOM elements & CSS | Any JS object (state, data, Canvas, etc.) |
|
|
226
|
-
| Value reading | Parses current style/attr at runtime | Captures current JS values at start |
|
|
227
|
-
| Performance on startup | Slower (parsing + computation) | Fastest (no parsing) |
|
|
228
|
-
| State-based UI compatibility | Requires extra glue code | Native — ideal for React/Solid/Vue/Svelte |
|
|
229
|
-
| Global vs per-instance config | Plugins/global easing | Per-instance `.use()` (recommended) |
|
|
230
|
-
| Bundle size | Larger (DOM utils, parsing, plugins) | Very small (~2–4 KB minzipped) |
|
|
231
|
-
| Runtime loop | Per-tween or managed | Single shared global loop (most efficient) |
|
|
236
|
+
Just make sure to **not call `tween.start()` / `timeline.play()`** during SSR (if you're using custom hooks outside those included in this package).
|
|
232
237
|
|
|
233
|
-
#### Additional notes
|
|
234
238
|
|
|
235
|
-
|
|
236
|
-
- **Repeat & yoyo** — not built-in on `Tween` (use `Timeline` for sequencing/repeats)
|
|
237
|
-
- **Custom interpolators** — register per instance with `.use('prop', fn)` (prevents conflicts in large apps)
|
|
238
|
-
- **Testing** — `setNow()` allows perfect time control in tests (override `now()` to fake progression)
|
|
239
|
+
#### Working Around Limitations
|
|
239
240
|
|
|
240
|
-
|
|
241
|
+
- **Chaining** — use callbacks to start other `Tween` / `Timeline` (like the above, use `onComplete` callback to trigger the start of other tween/timeline instances)
|
|
242
|
+
- **Custom extensions** — register per instance per property with `.use('propName', extensionConfig)` eliminates the guessing game completely, your values are validated and interpolated exactly how you want it.
|
|
243
|
+
- The original Tween.js **array interpolation** - we have a way to add custom interpolators, might worth a try integrating them later.
|
|
241
244
|
|
|
242
245
|
|
|
243
246
|
### Contributing
|
|
@@ -256,4 +259,4 @@ This is a work in progress. For any issue or unclear guides, please [file an iss
|
|
|
256
259
|
|
|
257
260
|
|
|
258
261
|
### License
|
|
259
|
-
**@thednp/tween** is released under [MIT License](
|
|
262
|
+
**@thednp/tween** is released under [MIT License](LICENSE).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @thednp/tween hooks for Preact v0.0.2 (https://github.com/thednp/tween)
|
|
3
|
+
* Copyright 2026 © thednp
|
|
4
|
+
* Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
|
|
5
|
+
*/
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
import { Timeline, Tween, TweenProps } from "@thednp/tween";
|
|
9
|
+
|
|
10
|
+
//#region src/preact/miniStore.d.ts
|
|
11
|
+
declare function useMiniStore<T extends TweenProps>(initialValue: T): T;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/preact/index.d.ts
|
|
14
|
+
/**
|
|
15
|
+
* Hook for updating values with Tween.
|
|
16
|
+
*
|
|
17
|
+
* **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.
|
|
18
|
+
* This has two important aspects: never configure or start update loop in SSR
|
|
19
|
+
* and only configure or start the loop when component is mounted in the client.
|
|
20
|
+
*
|
|
21
|
+
* @param initialValues - Initial tween values
|
|
22
|
+
* @returns [store, tween] Tuple of reactive store and Tween instance
|
|
23
|
+
* @example
|
|
24
|
+
* const App = () => {
|
|
25
|
+
* const [state, tween] = useTween({ x: 0, y: 0 })
|
|
26
|
+
*
|
|
27
|
+
* useEffect(() => {
|
|
28
|
+
* tween.to({ x: 100, y: 100 }, 1000).start()
|
|
29
|
+
* }, [])
|
|
30
|
+
*
|
|
31
|
+
* return (
|
|
32
|
+
* <div style={{ translate: `${state.x}px ${state.y}px` }} />
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
declare function useTween<T extends TweenProps>(initialValues: T): readonly [T, Tween<T>];
|
|
37
|
+
/**
|
|
38
|
+
* Hook for sequencing values update with Timeline.
|
|
39
|
+
*
|
|
40
|
+
* **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.
|
|
41
|
+
* This has two important aspects: never configure or start update loop in SSR
|
|
42
|
+
* and only configure or start the loop when component is mounted in the client.
|
|
43
|
+
*
|
|
44
|
+
* @param initialValues - Initial tween values
|
|
45
|
+
* @returns [store, timeline] Tuple of reactive store and Timeline instance
|
|
46
|
+
* @example
|
|
47
|
+
* const App = () => {
|
|
48
|
+
* const [state, timeline] = useTimeline({ x: 0, y: 0 })
|
|
49
|
+
*
|
|
50
|
+
* useEffect(() => {
|
|
51
|
+
* timeline.to({ x: 100, y: 100 }).start()
|
|
52
|
+
* }, [])
|
|
53
|
+
*
|
|
54
|
+
* return (
|
|
55
|
+
* <div style={{ translate: `${state.x}px ${state.y}px` }} />
|
|
56
|
+
* );
|
|
57
|
+
* }
|
|
58
|
+
*/
|
|
59
|
+
declare function useTimeline<T extends TweenProps>(initialValues: T): readonly [T, Timeline<T>];
|
|
60
|
+
//#endregion
|
|
61
|
+
export { Timeline, Tween, useMiniStore, useTimeline, useTween };
|
|
62
|
+
//# sourceMappingURL=preact.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preact.d.mts","names":[],"sources":["../../src/preact/miniStore.ts","../../src/preact/index.ts"],"mappings":";;;;;;;;;;iBA0JgB,YAAA,WAAuB,UAAA,CAAA,CAAY,YAAA,EAAc,CAAA,GAAC,CAAA;;;AAAlE;;;;;;;;;;;;;;;;;;ACxHA;;;;ADwHA,iBCxHgB,QAAA,WAAmB,UAAA,CAAA,CAAY,aAAA,EAAe,CAAA,aAAC,CAAA,EAAA,KAAA,CAAA,CAAA;;;;;;;;;;;;;;;AA2C/D;;;;;;;;iBAAgB,WAAA,WAAsB,UAAA,CAAA,CAAY,aAAA,EAAe,CAAA,aAAC,CAAA,EAAA,QAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @thednp/tween hooks for Preact v0.0.2 (https://github.com/thednp/tween)
|
|
3
|
+
* Copyright 2026 © thednp
|
|
4
|
+
* Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
|
|
5
|
+
*/
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
import { useEffect, useRef, useState } from "preact/hooks";
|
|
9
|
+
import { Timeline, Tween, dummyInstance, isArray, isPlainObject, isServer, objectHasProp } from "@thednp/tween";
|
|
10
|
+
|
|
11
|
+
//#region src/preact/miniStore.ts
|
|
12
|
+
const STATE_PROXY = "_proxy";
|
|
13
|
+
const proxyProps = {
|
|
14
|
+
value: 1,
|
|
15
|
+
enumerable: false,
|
|
16
|
+
configurable: false,
|
|
17
|
+
writable: false
|
|
18
|
+
};
|
|
19
|
+
function defineArrayProxy(index, value, target, sourceLen, notifyListeners) {
|
|
20
|
+
const itemIsLast = index === sourceLen - 1;
|
|
21
|
+
if (isArray(value)) {
|
|
22
|
+
const subArray = [];
|
|
23
|
+
const valueLen = value.length;
|
|
24
|
+
value.forEach((itm, idx) => {
|
|
25
|
+
const subItemIsLast = itemIsLast && idx === valueLen - 1;
|
|
26
|
+
let currentItem = itm;
|
|
27
|
+
Object.defineProperty(subArray, idx, {
|
|
28
|
+
get: () => currentItem,
|
|
29
|
+
set: (newValue) => {
|
|
30
|
+
currentItem = newValue;
|
|
31
|
+
if (subItemIsLast) notifyListeners();
|
|
32
|
+
},
|
|
33
|
+
enumerable: true
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
target[index] = subArray;
|
|
37
|
+
} else {
|
|
38
|
+
let currentValue = value;
|
|
39
|
+
const getter = () => currentValue;
|
|
40
|
+
const setter = (newVal) => {
|
|
41
|
+
currentValue = newVal;
|
|
42
|
+
if (itemIsLast) notifyListeners();
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperties(target, { [index]: {
|
|
45
|
+
get: getter,
|
|
46
|
+
set: setter,
|
|
47
|
+
enumerable: true
|
|
48
|
+
} });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function defineStateProxy(key, value, target, notifyListeners) {
|
|
52
|
+
const valueIsArray = isArray(value);
|
|
53
|
+
let currentValue = value;
|
|
54
|
+
const getter = () => currentValue;
|
|
55
|
+
let setter;
|
|
56
|
+
if (valueIsArray) {
|
|
57
|
+
const arrayProxy = [];
|
|
58
|
+
const valLength = value.length;
|
|
59
|
+
for (let i = 0; i < valLength; i++) defineArrayProxy(i, value[i], arrayProxy, valLength, notifyListeners);
|
|
60
|
+
currentValue = arrayProxy;
|
|
61
|
+
} else setter = (newValue) => {
|
|
62
|
+
if (currentValue !== newValue) {
|
|
63
|
+
currentValue = newValue;
|
|
64
|
+
notifyListeners();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
Object.defineProperties(target, {
|
|
68
|
+
[STATE_PROXY]: proxyProps,
|
|
69
|
+
[key]: {
|
|
70
|
+
get: getter,
|
|
71
|
+
set: setter,
|
|
72
|
+
enumerable: true
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function createMiniState(obj, parentReceiver, notifyListeners) {
|
|
77
|
+
if (objectHasProp(obj, STATE_PROXY)) return obj;
|
|
78
|
+
for (const [key, value] of Object.entries(obj)) if (isPlainObject(value)) parentReceiver[key] = createMiniState(value, {}, notifyListeners);
|
|
79
|
+
else defineStateProxy(key, value, parentReceiver, notifyListeners);
|
|
80
|
+
return parentReceiver;
|
|
81
|
+
}
|
|
82
|
+
function miniStore(init) {
|
|
83
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
84
|
+
const notifyListeners = () => {
|
|
85
|
+
listeners.forEach((listener) => listener(store));
|
|
86
|
+
};
|
|
87
|
+
const store = createMiniState(init, {}, notifyListeners);
|
|
88
|
+
return {
|
|
89
|
+
get state() {
|
|
90
|
+
return store;
|
|
91
|
+
},
|
|
92
|
+
subscribe: (listener) => {
|
|
93
|
+
listeners.add(listener);
|
|
94
|
+
return () => {
|
|
95
|
+
listeners.delete(listener);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function useMiniStore(initialValue) {
|
|
101
|
+
const storeRef = useRef(null);
|
|
102
|
+
const [, setVersion] = useState(0);
|
|
103
|
+
if (!storeRef.current) storeRef.current = miniStore(initialValue);
|
|
104
|
+
useEffect(() => storeRef.current.subscribe(() => setVersion((v) => v === 2 ? 0 : v + 1)), []);
|
|
105
|
+
return storeRef.current.state;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/preact/index.ts
|
|
110
|
+
/**
|
|
111
|
+
* Hook for updating values with Tween.
|
|
112
|
+
*
|
|
113
|
+
* **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.
|
|
114
|
+
* This has two important aspects: never configure or start update loop in SSR
|
|
115
|
+
* and only configure or start the loop when component is mounted in the client.
|
|
116
|
+
*
|
|
117
|
+
* @param initialValues - Initial tween values
|
|
118
|
+
* @returns [store, tween] Tuple of reactive store and Tween instance
|
|
119
|
+
* @example
|
|
120
|
+
* const App = () => {
|
|
121
|
+
* const [state, tween] = useTween({ x: 0, y: 0 })
|
|
122
|
+
*
|
|
123
|
+
* useEffect(() => {
|
|
124
|
+
* tween.to({ x: 100, y: 100 }, 1000).start()
|
|
125
|
+
* }, [])
|
|
126
|
+
*
|
|
127
|
+
* return (
|
|
128
|
+
* <div style={{ translate: `${state.x}px ${state.y}px` }} />
|
|
129
|
+
* );
|
|
130
|
+
* }
|
|
131
|
+
*/
|
|
132
|
+
function useTween(initialValues) {
|
|
133
|
+
if (isServer) return [initialValues, dummyInstance];
|
|
134
|
+
const store = useMiniStore(initialValues);
|
|
135
|
+
const tweenRef = useRef(null);
|
|
136
|
+
if (!tweenRef.current) tweenRef.current = new Tween(store);
|
|
137
|
+
const dispose = () => {
|
|
138
|
+
tweenRef.current.stop();
|
|
139
|
+
tweenRef.current.clear();
|
|
140
|
+
};
|
|
141
|
+
useEffect(() => dispose, []);
|
|
142
|
+
return [store, tweenRef.current];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Hook for sequencing values update with Timeline.
|
|
146
|
+
*
|
|
147
|
+
* **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.
|
|
148
|
+
* This has two important aspects: never configure or start update loop in SSR
|
|
149
|
+
* and only configure or start the loop when component is mounted in the client.
|
|
150
|
+
*
|
|
151
|
+
* @param initialValues - Initial tween values
|
|
152
|
+
* @returns [store, timeline] Tuple of reactive store and Timeline instance
|
|
153
|
+
* @example
|
|
154
|
+
* const App = () => {
|
|
155
|
+
* const [state, timeline] = useTimeline({ x: 0, y: 0 })
|
|
156
|
+
*
|
|
157
|
+
* useEffect(() => {
|
|
158
|
+
* timeline.to({ x: 100, y: 100 }).start()
|
|
159
|
+
* }, [])
|
|
160
|
+
*
|
|
161
|
+
* return (
|
|
162
|
+
* <div style={{ translate: `${state.x}px ${state.y}px` }} />
|
|
163
|
+
* );
|
|
164
|
+
* }
|
|
165
|
+
*/
|
|
166
|
+
function useTimeline(initialValues) {
|
|
167
|
+
if (isServer) return [initialValues, dummyInstance];
|
|
168
|
+
const store = useMiniStore(initialValues);
|
|
169
|
+
const timelineRef = useRef(null);
|
|
170
|
+
if (!timelineRef.current) timelineRef.current = new Timeline(store);
|
|
171
|
+
const dispose = () => {
|
|
172
|
+
timelineRef.current.stop();
|
|
173
|
+
timelineRef.current.clear();
|
|
174
|
+
};
|
|
175
|
+
useEffect(() => dispose, []);
|
|
176
|
+
return [store, timelineRef.current];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
export { Timeline, Tween, useMiniStore, useTimeline, useTween };
|
|
181
|
+
//# sourceMappingURL=preact.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preact.mjs","names":[],"sources":["../../src/preact/miniStore.ts","../../src/preact/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"preact/hooks\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\ntype Listener<T> = (state: T) => void;\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T],\n target: T | ArrayVal,\n notifyListeners: () => void,\n) {\n const valueIsArray = isArray(value);\n let currentValue = value as ArrayVal | ArrayVal[];\n\n const getter = () => currentValue;\n let setter;\n\n if (valueIsArray) {\n // Build array proxy structure\n const arrayProxy: ArrayVal | ArrayVal[] = [];\n const valLength = value.length;\n\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(\n i,\n (value as ArrayVal)[i],\n arrayProxy as ArrayVal,\n valLength,\n notifyListeners,\n );\n }\n currentValue = arrayProxy;\n } else {\n setter = (newValue: typeof currentValue) => {\n if (currentValue !== newValue) {\n currentValue = newValue;\n notifyListeners();\n }\n };\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps,\n notifyListeners: () => void,\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n parentReceiver[key] = createMiniState(value, {}, notifyListeners);\n } else {\n defineStateProxy(key, value, parentReceiver, notifyListeners);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n const listeners = new Set<Listener<T>>();\n const notifyListeners = () => {\n listeners.forEach((listener) => listener(store));\n };\n\n const store = createMiniState(init, {}, notifyListeners) as T;\n\n return {\n get state() {\n return store;\n },\n subscribe: (listener: Listener<T>) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport function useMiniStore<T extends TweenProps>(initialValue: T) {\n const storeRef = useRef<ReturnType<typeof miniStore<T>>>(null);\n const [, setVersion] = useState(0);\n\n // istanbul ignore else @preserve\n if (!storeRef.current) {\n storeRef.current = miniStore(initialValue);\n }\n\n useEffect(\n () =>\n storeRef.current!.subscribe(() => setVersion((v) => v === 2 ? 0 : v + 1)),\n [],\n );\n\n return storeRef.current!.state;\n}\n","import { useEffect, useRef } from \"preact/hooks\";\nimport {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { useMiniStore } from \"./miniStore.ts\";\n\nexport { Timeline, Tween, useMiniStore };\n\n/**\n * Hook for updating values with Tween.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = useTween({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * tween.to({ x: 100, y: 100 }, 1000).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = useMiniStore(initialValues);\n const tweenRef = useRef<Tween<T>>(null);\n\n // istanbul ignore else @preserve\n if (!tweenRef.current) {\n tweenRef.current = new Tween(store);\n }\n\n const dispose = () => {\n tweenRef.current!.stop();\n tweenRef.current!.clear();\n };\n useEffect(() => dispose, []);\n\n return [store, tweenRef.current] as [T, Tween<T>];\n}\n\n/**\n * Hook for sequencing values update with Timeline.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = useTimeline({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * timeline.to({ x: 100, y: 100 }).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = useMiniStore(initialValues);\n const timelineRef = useRef<Timeline<T>>(null);\n\n // istanbul ignore else @preserve\n if (!timelineRef.current) {\n timelineRef.current = new Timeline(store);\n }\n\n const dispose = () => {\n timelineRef.current!.stop();\n timelineRef.current!.clear();\n };\n useEffect(() => dispose, []);\n\n return [store, timelineRef.current] as [T, Timeline<T>];\n}\n"],"mappings":"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"}
|