@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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 thednp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ ## @thednp/tween
2
+ [![Coverage Status](https://coveralls.io/repos/github/thednp/tween/badge.svg)](https://coveralls.io/github/thednp/tween)
3
+ [![ci](https://github.com/thednp/tween/actions/workflows/ci.yml/badge.svg)](https://github.com/thednp/tween/actions/workflows/ci.yml)
4
+ [![typescript version](https://img.shields.io/badge/typescript-5.9.3-brightgreen)](https://www.typescriptlang.org/)
5
+ [![vitest version](https://img.shields.io/badge/vitest-4.0.17-brightgreen)](https://vitest.dev/)
6
+ [![vite version](https://img.shields.io/badge/vite-7.3.1-brightgreen)](https://github.com/vitejs)
7
+
8
+ A Typescript sourced `Tween` engine forked from the excellent [@tweenjs/tweenjs](https://github.com/tweenjs/tween.js).
9
+
10
+ * The package includes `Tween` class for creating most simple tween objects, with most essential controls over the update loop and simple methods for sequencing.
11
+ * Another major addition to this package is a simple to use `Timeline` class which enables an advanced control over the update loop, most importantly a **per-property** control for duration and easing.
12
+ * There are also a series of custom hooks/promitives for popular frameworks like React/SolidJS (more to come), with proper documentation and detailed user guides.
13
+
14
+
15
+ ### Features
16
+ - Built in custom hooks/primitives for popular frameworks like React/SolidJS
17
+ - Simple and powerful `Timeline` class for advanced sequencing/overlaps
18
+ - Lightweight fork of tween.js (~half the size)
19
+ - Chainable API for proper DX
20
+ - Easy to extend via custom interpolators
21
+ - Duration/delay in seconds
22
+ - Automatic rAF loop (starts and stops automatically)
23
+ - TypeScript-native, zero dependencies
24
+ - Tested with Vitest 100% code coverage
25
+
26
+
27
+ ### Install
28
+ ```
29
+ npm install @thednp/tween
30
+ ```
31
+
32
+ ```
33
+ pnpm add @thednp/tween
34
+ ```
35
+
36
+ ```
37
+ deno add @thednp/tween
38
+ ```
39
+
40
+ ```
41
+ bun add @thednp/tween
42
+ ```
43
+
44
+
45
+ ### Usage
46
+
47
+ #### Using Tween
48
+ ```ts
49
+ import { Tween, Easing } from '@thednp/tween';
50
+
51
+ // find some target
52
+ const target = document.getElementById('my-target');
53
+
54
+ // define a tween
55
+ const tween = new Tween({ x: 0 })
56
+ .duration(1.5) // duration/delay accept seconds (e.g., 1.5 = 1.5s)
57
+ .onUpdate((obj, elapsed, eased) => {
58
+ // update App state
59
+ // OR manipulate the DOM directly
60
+ Object.assign(target.style, { translate: obj.x + "px"});
61
+ // monitor progress of the tween
62
+ console.log(`Tween progress: ${Math.floor(elapsed * 100)}%`)
63
+ // do other stuff with the `eased` value
64
+ });
65
+
66
+ // override any value on the fly
67
+ const moveRight = () => tween
68
+ .from({ x: 0 }) // override/reset start values
69
+ .to({ x: 150 }) // override end values
70
+ .easing(Easing.Quadratic.Out) // set a special easing function for every case
71
+ .duration(1.5) // set duration as well in seconds
72
+ .start(); // start the tween
73
+
74
+ const moveLeft = () => tween
75
+ .to({ x: -150 }) // set a different to
76
+ .easing(Easing.Elastic.Out) // override easing
77
+ .duration(1.5) // override duration in seconds
78
+ .start(); // start the tween
79
+
80
+ // trigger any time
81
+ const button1 = document.getElementById('my-button-1');
82
+ const button2 = document.getElementById('my-button-2');
83
+
84
+ button1.onclick = moveRight;
85
+ button2.onclick = moveLeft;
86
+
87
+ // The engine does requestAnimationFrame/cancelAnimationFrame for you
88
+ ```
89
+
90
+ #### Using Timeline
91
+ ```ts
92
+ import { Timeline, Easing } from '@thednp/tween';
93
+
94
+ // find some target
95
+ const target = document.getElementById('my-target');
96
+
97
+ // define a timeline
98
+ const myTimeline = new Timeline({ x: 0, y: 0 })
99
+ .to({ x: 150, duration: 2.5, easing: Easing.Elastic.Out })
100
+ .to({ y: 150, duration: 1.5, easing: Easing.Elastic.Out }, "-=1")
101
+ .onUpdate((obj, elapsed) => {
102
+ // update App state
103
+ // OR manipulate the DOM directly
104
+ Object.assign(target.style, {
105
+ translate: obj.x + "px " + obj.y + "px",
106
+ });
107
+ // monitor progress of the tween
108
+ console.log(`Timeline progress: ${Math.floor(elapsed * 100)}%`)
109
+ });
110
+
111
+ // trigger any time
112
+ const button = document.getElementById('my-button-1');
113
+
114
+ button.onclick = myTimeline.play();
115
+
116
+ // The engine does requestAnimationFrame/cancelAnimationFrame for you
117
+ ```
118
+ To use `Tween` or `Timeline` with frameworks please check [React](wiki/React.md), [SolidJS](wiki/Solid.md) (more to come).
119
+
120
+
121
+ ### What is different from original?
122
+
123
+ #### Back to Base
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.
126
+
127
+
128
+ #### New Features
129
+ This package comes with `Timeline`, which works like a regular `Tween` under the hood, but it provides additional control methods like `pause()`, `resume()` `seek()`.
130
+
131
+ Both `Tween` and `Timeline` have a static method to add custom interpolators, which is a unique way to extend beyond the original design and very different from the original library.
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.
141
+
142
+
143
+ ### Guides and Resources
144
+ * The original Tween.js [User Guide](https://github.com/tweenjs/tween.js/blob/main/docs/user_guide.md)
145
+ * [Tween.md](wiki/Tween.md) - our official `Tween` guide
146
+ * [Timeline.md](wiki/Timeline.md) - our official `Timeline` guide
147
+ * [Easing.md](wiki/Easing.md) - an extensive guide on easing functions.
148
+ * [React.md](wiki/React.md) - use `Tween` / `Timeline` with React with custom hooks.
149
+ * [Solid.md](wiki/Solid.md) - use `Tween` / `Timeline` with SolidJS with primitives.
150
+
151
+
152
+ ### Technical Notes & Design Choices
153
+
154
+ **@thednp/tween** is intentionally designed as a **lightweight, state-first** tweening engine. Here's why certain choices were made and how the system works under the hood.
155
+
156
+ #### Why declarative state-based animation?
157
+
158
+ Most classic animation libraries (GSAP, KUTE.js, Velocity.js) are **imperative**: they read current DOM styles/attributes at runtime, parse them, compute differences, and write back updates.
159
+
160
+ **@thednp/tween** keeps with the original Tween.js, which is the opposite approach — it's **purely state-driven**:
161
+
162
+ - You provide **target state values** (numbers, arrays, objects)
163
+ - The engine interpolates **from current state** (captured at `.start()` / `.play()`)
164
+ - No DOM reading/parsing ever happens
165
+ - No per browser handling/processing
166
+
167
+ **Advantages**:
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
174
+
175
+ **Trade-offs**:
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`
186
+ - `Runtime()` runs every frame → calls `.update(time)` on every queued item
187
+ - 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
189
+
190
+ **Benefits**:
191
+ - Only **one** rAF subscription for the entire app — extremely efficient
192
+ - No manual start/stop of animation loop per tween/timeline
193
+ - Zero overhead when nothing is animating
194
+
195
+ This shared loop is why you never need to worry about starting/stopping individual `requestAnimationFrame` calls.
196
+
197
+ #### Async nature of requestAnimationFrame
198
+
199
+ All updates are **async** by nature:
200
+
201
+ 1. You call `.start()` / `.play()` → instance queued
202
+ 2. Next `rAF` tick → `Runtime()` calls `.update(time)` → interpolates → calls `onUpdate`
203
+ 3. DOM/state updates happen **on the next frame(s)** — never synchronous
204
+
205
+ This means:
206
+ - Visual changes are always **smooth** and **tied to the display refresh rate**
207
+ - You can safely call `.to()`, `.duration()`, etc. **during** an animation — changes apply on the next frame
208
+ - No risk of partial/inconsistent frames (all calculations happen before paint)
209
+
210
+ #### Server-Side Rendering (SSR) compatibility
211
+
212
+ `@thednp/tween` is **SSR-safe** out of the box:
213
+
214
+ - No DOM access anywhere in the core
215
+ - `requestAnimationFrame` / `cancelAnimationFrame` are only called in browser (via `Runtime()`)
216
+ - `now()` defaults to `performance.now()` or `Date.now()` — safe fallbacks in Node
217
+ - Hooks/primitives only start animation on client (via mount effects)
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
222
+
223
+ | Feature/Aspect | Classic (GSAP/KUTE/Velocity) | @thednp/tween |
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) |
232
+
233
+ #### Additional notes
234
+
235
+ - **No pause/resume on single Tween** — use `.stop()` + `.startFromLast()` for pause-like behavior (keeps it simple)
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
+
240
+ We believe this combination of **small size**, **state-first design**, **shared loop**, and **per-instance flexibility** makes **@thednp/tween** uniquely suitable for modern component-based UIs in 2026.
241
+
242
+
243
+ ### Contributing
244
+ This is a work in progress. For any issue or unclear guides, please [file an issue](https://github.com/thednp/tween/issues/new) and help make this guide better. Or feel free to submit a PR! Thank you!
245
+
246
+ **How to contribute**:
247
+ * fork the project
248
+ * change code/docs & update tests
249
+ * submit PR.
250
+
251
+
252
+ ### Credits
253
+ * @sole for the creation and maintaining of the original [tween.js](https://github.com/tweenjs/tween.js)
254
+ * @dalisoft for his excellent [es6-tween](https://github.com/tweenjs/es6-tween)
255
+ * CreateJS for their excellent [TweenJS](https://github.com/CreateJS/TweenJS)
256
+
257
+
258
+ ### License
259
+ **@thednp/tween** is released under [MIT License](LICENCE).