@cosmictraveler002/anim-kit 1.0.0 → 1.2.0
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 +275 -50
- package/dist/anim-kit.standalone.js +18 -8
- package/dist/anim-kit.standalone.js.map +1 -1
- package/dist/core/gsap.d.ts +2 -1
- package/dist/core/gsap.d.ts.map +1 -1
- package/dist/core/gsap.js +3 -2
- package/dist/core/gsap.js.map +1 -1
- package/dist/core/split.d.ts.map +1 -1
- package/dist/core/split.js +15 -2
- package/dist/core/split.js.map +1 -1
- package/dist/effects/clip-wipe.d.ts +31 -0
- package/dist/effects/clip-wipe.d.ts.map +1 -0
- package/dist/effects/clip-wipe.js +82 -0
- package/dist/effects/clip-wipe.js.map +1 -0
- package/dist/effects/counter.d.ts +9 -0
- package/dist/effects/counter.d.ts.map +1 -1
- package/dist/effects/counter.js +17 -7
- package/dist/effects/counter.js.map +1 -1
- package/dist/effects/cursor-follower.d.ts.map +1 -1
- package/dist/effects/cursor-follower.js +22 -4
- package/dist/effects/cursor-follower.js.map +1 -1
- package/dist/effects/flip-words.d.ts +31 -0
- package/dist/effects/flip-words.d.ts.map +1 -0
- package/dist/effects/flip-words.js +104 -0
- package/dist/effects/flip-words.js.map +1 -0
- package/dist/effects/line-reveal.d.ts +6 -2
- package/dist/effects/line-reveal.d.ts.map +1 -1
- package/dist/effects/line-reveal.js +8 -4
- package/dist/effects/line-reveal.js.map +1 -1
- package/dist/effects/magnetic.d.ts +15 -0
- package/dist/effects/magnetic.d.ts.map +1 -0
- package/dist/effects/magnetic.js +59 -0
- package/dist/effects/magnetic.js.map +1 -0
- package/dist/effects/media-settle.d.ts +30 -0
- package/dist/effects/media-settle.d.ts.map +1 -0
- package/dist/effects/media-settle.js +55 -0
- package/dist/effects/media-settle.js.map +1 -0
- package/dist/effects/roll-text.d.ts +13 -0
- package/dist/effects/roll-text.d.ts.map +1 -0
- package/dist/effects/roll-text.js +91 -0
- package/dist/effects/roll-text.js.map +1 -0
- package/dist/effects/scramble-text.d.ts +17 -0
- package/dist/effects/scramble-text.d.ts.map +1 -0
- package/dist/effects/scramble-text.js +93 -0
- package/dist/effects/scramble-text.js.map +1 -0
- package/dist/effects/unfold-reveal.d.ts +23 -0
- package/dist/effects/unfold-reveal.d.ts.map +1 -0
- package/dist/effects/unfold-reveal.js +53 -0
- package/dist/effects/unfold-reveal.js.map +1 -0
- package/dist/index.d.ts +25 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/styles/anim-kit.css +22 -0
- package/package.json +6 -3
- package/src/core/gsap.ts +3 -2
- package/src/core/split.ts +16 -2
- package/src/effects/clip-wipe.ts +141 -0
- package/src/effects/counter.ts +26 -6
- package/src/effects/cursor-follower.ts +21 -4
- package/src/effects/flip-words.ts +148 -0
- package/src/effects/line-reveal.ts +12 -5
- package/src/effects/magnetic.ts +87 -0
- package/src/effects/media-settle.ts +106 -0
- package/src/effects/roll-text.ts +118 -0
- package/src/effects/scramble-text.ts +122 -0
- package/src/effects/unfold-reveal.ts +92 -0
- package/src/index.ts +33 -2
- package/src/styles/anim-kit.css +22 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ no components: every effect resolves plain DOM selectors, so it works with
|
|
|
9
9
|
**React, Vue, Next, Svelte, Astro or plain HTML**.
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
import { lineReveal, marquee, menuOverlay } from "anim-kit";
|
|
12
|
+
import { lineReveal, marquee, menuOverlay } from "@cosmictraveler002/anim-kit";
|
|
13
13
|
|
|
14
14
|
const destroy = lineReveal("[data-lines]", { mode: "scroll" });
|
|
15
15
|
// …later (route change, HMR, teardown):
|
|
@@ -48,12 +48,12 @@ destroy();
|
|
|
48
48
|
## Install
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
npm install anim-kit
|
|
51
|
+
npm install @cosmictraveler002/anim-kit
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
|
-
import { smoothScroll, horizontalScroll } from "anim-kit";
|
|
56
|
-
import "anim-kit/styles"; // companion stylesheet (plain .css, optional but recommended)
|
|
55
|
+
import { smoothScroll, horizontalScroll } from "@cosmictraveler002/anim-kit";
|
|
56
|
+
import "@cosmictraveler002/anim-kit/styles"; // companion stylesheet (plain .css, optional but recommended)
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
`anim-kit` ships **ESM only** with generated `.d.ts` declarations — no CJS
|
|
@@ -78,14 +78,14 @@ inside the published tarball):
|
|
|
78
78
|
|
|
79
79
|
| Specifier | Resolves to | Use for |
|
|
80
80
|
|---|---|---|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
81
|
+
| `@cosmictraveler002/anim-kit` | `dist/index.js` + `dist/index.d.ts` | the full barrel — 45 exports |
|
|
82
|
+
| `@cosmictraveler002/anim-kit/effects/<name>` | `dist/effects/<name>.js` + `.d.ts` | one effect in isolation (`marquee`, `lineReveal`, …) |
|
|
83
|
+
| `@cosmictraveler002/anim-kit/standalone` | `dist/anim-kit.standalone.js` (types → `index.d.ts`) | the self-contained bundle — same API |
|
|
84
|
+
| `@cosmictraveler002/anim-kit/styles` | `dist/styles/anim-kit.css` | untouched plain CSS |
|
|
85
85
|
|
|
86
86
|
```ts
|
|
87
|
-
import { marquee } from "anim-kit/effects/marquee"; // deep import, no barrel
|
|
88
|
-
import "anim-kit/styles";
|
|
87
|
+
import { marquee } from "@cosmictraveler002/anim-kit/effects/marquee"; // deep import, no barrel
|
|
88
|
+
import "@cosmictraveler002/anim-kit/styles";
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
TypeScript ≥ 4.7 with `moduleResolution: "bundler"` or `"node16"`/`"nodenext"`
|
|
@@ -97,7 +97,7 @@ resolves declarations through the same map — no `typesVersions` shim needed.
|
|
|
97
97
|
|
|
98
98
|
No build step on the consumer's end: `dist/` is served as-is from the npm
|
|
99
99
|
tarball by any npm CDN. Every URL is **version-pinned** — npm versions are
|
|
100
|
-
immutable, so
|
|
100
|
+
immutable, so `@cosmictraveler002/anim-kit@1.2.0` always resolves to exactly that build, forever
|
|
101
101
|
(only a new version creates a new URL; nothing floats unless you ask for a
|
|
102
102
|
range).
|
|
103
103
|
|
|
@@ -108,12 +108,12 @@ plugins anim-kit uses) and `lenis` **inlined** — no import map, one URL, works
|
|
|
108
108
|
identically on jsDelivr and unpkg:
|
|
109
109
|
|
|
110
110
|
```html
|
|
111
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/anim-kit@1.
|
|
111
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/styles/anim-kit.css" />
|
|
112
112
|
|
|
113
113
|
<script type="module">
|
|
114
114
|
import {
|
|
115
115
|
smoothScroll, lineReveal, marquee,
|
|
116
|
-
} from "https://cdn.jsdelivr.net/npm/anim-kit@1.
|
|
116
|
+
} from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/anim-kit.standalone.js";
|
|
117
117
|
|
|
118
118
|
smoothScroll();
|
|
119
119
|
lineReveal("[data-lines]", { mode: "scroll" });
|
|
@@ -121,7 +121,7 @@ identically on jsDelivr and unpkg:
|
|
|
121
121
|
</script>
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
unpkg serves the same file: `https://unpkg.com/anim-kit@1.
|
|
124
|
+
unpkg serves the same file: `https://unpkg.com/@cosmictraveler002/anim-kit@1.2.0/dist/anim-kit.standalone.js`
|
|
125
125
|
|
|
126
126
|
### Option 2 — jsDelivr `+esm`
|
|
127
127
|
|
|
@@ -130,7 +130,7 @@ per version):
|
|
|
130
130
|
|
|
131
131
|
```html
|
|
132
132
|
<script type="module">
|
|
133
|
-
import { lineReveal } from "https://cdn.jsdelivr.net/npm/anim-kit@1.
|
|
133
|
+
import { lineReveal } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/+esm";
|
|
134
134
|
</script>
|
|
135
135
|
```
|
|
136
136
|
|
|
@@ -142,17 +142,18 @@ locally, with CDN URLs — and the way to share one GSAP between anim-kit and
|
|
|
142
142
|
the rest of your page:
|
|
143
143
|
|
|
144
144
|
```html
|
|
145
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/anim-kit@1.
|
|
145
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/styles/anim-kit.css" />
|
|
146
146
|
|
|
147
147
|
<script type="importmap">
|
|
148
148
|
{
|
|
149
149
|
"imports": {
|
|
150
|
-
"anim-kit": "https://cdn.jsdelivr.net/npm/anim-kit@1.
|
|
150
|
+
"@cosmictraveler002/anim-kit": "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/index.js",
|
|
151
151
|
"gsap": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/index.js",
|
|
152
152
|
"gsap/ScrollTrigger": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/ScrollTrigger.js",
|
|
153
153
|
"gsap/SplitText": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/SplitText.js",
|
|
154
154
|
"gsap/Draggable": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/Draggable.js",
|
|
155
155
|
"gsap/CustomEase": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/CustomEase.js",
|
|
156
|
+
"gsap/Flip": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/Flip.js",
|
|
156
157
|
"gsap/ScrollSmoother": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/ScrollSmoother.js",
|
|
157
158
|
"lenis": "https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.mjs"
|
|
158
159
|
}
|
|
@@ -160,13 +161,13 @@ the rest of your page:
|
|
|
160
161
|
</script>
|
|
161
162
|
|
|
162
163
|
<script type="module">
|
|
163
|
-
import { smoothScroll, lineReveal } from "anim-kit";
|
|
164
|
+
import { smoothScroll, lineReveal } from "@cosmictraveler002/anim-kit";
|
|
164
165
|
// per-effect deep imports work here too:
|
|
165
|
-
// import { dragStrip } from "anim-kit/effects/dragStrip";
|
|
166
|
+
// import { dragStrip } from "@cosmictraveler002/anim-kit/effects/dragStrip";
|
|
166
167
|
</script>
|
|
167
168
|
```
|
|
168
169
|
|
|
169
|
-
Swap the host for unpkg (`https://unpkg.com/anim-kit@1.
|
|
170
|
+
Swap the host for unpkg (`https://unpkg.com/@cosmictraveler002/anim-kit@1.2.0/dist/index.js`, …) —
|
|
170
171
|
the file layout is identical. GSAP subpaths are listed one by one because
|
|
171
172
|
import maps match specifiers literally: a trailing-slash prefix map would
|
|
172
173
|
produce extension-less URLs, which CDNs don't serve. The `gsap`/`lenis` pins
|
|
@@ -188,7 +189,7 @@ match `package-lock.json`.
|
|
|
188
189
|
```
|
|
189
190
|
|
|
190
191
|
```js
|
|
191
|
-
import { smoothScroll, lineReveal, marquee, compose } from "anim-kit";
|
|
192
|
+
import { smoothScroll, lineReveal, marquee, compose } from "@cosmictraveler002/anim-kit";
|
|
192
193
|
|
|
193
194
|
const scroller = smoothScroll({ lerp: 0.08, smoothWheel: true });
|
|
194
195
|
|
|
@@ -278,16 +279,20 @@ prompt dock, and backs the `category` / `subcategory` fields on
|
|
|
278
279
|
| Core & setup | Smooth scrolling | `smoothScroll` |
|
|
279
280
|
| Text animations | Line & mask reveals | `lineReveal`, `maskReveal` |
|
|
280
281
|
| Text animations | Per-character scatter | `scatterText` |
|
|
282
|
+
| Text animations | Decode & scramble | `scrambleText` |
|
|
283
|
+
| Text animations | Rolling text | `rollText` |
|
|
281
284
|
| Text animations | Counters | `counter` |
|
|
285
|
+
| Text animations | Layout transfers | `flipWords` |
|
|
282
286
|
| Scroll & media | Pinned galleries | `horizontalScroll`, `stackedCards`, `stackedCardsPinned` |
|
|
283
287
|
| Scroll & media | Parallax & depth | `parallax` |
|
|
284
|
-
| Scroll & media | Heroes & media | `heroShrink` |
|
|
285
|
-
| Scroll & media | Enter reveals | `revealRule` |
|
|
288
|
+
| Scroll & media | Heroes & media | `heroShrink`, `mediaSettle` |
|
|
289
|
+
| Scroll & media | Enter reveals | `revealRule`, `unfoldReveal`, `clipWipe` |
|
|
286
290
|
| Loops & marquees | Marquees | `marquee` |
|
|
287
291
|
| Loops & marquees | Infinite draggables | `dragStrip` |
|
|
288
292
|
| Loops & marquees | Equalizers | `audioBars` |
|
|
289
293
|
| Buttons & links | Liquid fills | `liquidButton` |
|
|
290
294
|
| Buttons & links | Underlines | `underlineLink` |
|
|
295
|
+
| Buttons & links | Magnetic hover | `magnetic` |
|
|
291
296
|
| Navigation & overlays | Menus & nav | `navHide`, `menuOverlay` |
|
|
292
297
|
| Navigation & overlays | Cursors | `cursorFollower` |
|
|
293
298
|
| Intros & transitions | Preloaders | `preloader` |
|
|
@@ -308,7 +313,7 @@ that does not exist.
|
|
|
308
313
|
#### `initGSAP()`
|
|
309
314
|
|
|
310
315
|
Registers the plugins anim-kit relies on (`ScrollTrigger`, `SplitText`,
|
|
311
|
-
`Draggable`, `CustomEase`, `ScrollSmoother`) and the studio's custom eases.
|
|
316
|
+
`Draggable`, `CustomEase`, `Flip`, `ScrollSmoother`) and the studio's custom eases.
|
|
312
317
|
Idempotent; called for you by every effect.
|
|
313
318
|
|
|
314
319
|
#### `EASES`
|
|
@@ -346,7 +351,8 @@ Central reduced-motion gate. If the user prefers reduced motion and
|
|
|
346
351
|
|
|
347
352
|
### Text animations
|
|
348
353
|
|
|
349
|
-
Typography in motion — masked lines, rising masks, per-character scatter,
|
|
354
|
+
Typography in motion — masked lines, rising masks, per-character scatter,
|
|
355
|
+
decode reveals, rolling words, tickers, layout transfers.
|
|
350
356
|
|
|
351
357
|
#### `lineReveal(target, options?) => destroy`
|
|
352
358
|
|
|
@@ -356,12 +362,14 @@ source site.
|
|
|
356
362
|
```ts
|
|
357
363
|
lineReveal("[data-hero-text]", { mode: "immediate", delay: 0.35 }); // above the fold
|
|
358
364
|
lineReveal("[data-lines]", { mode: "scroll" }); // reverses on leave
|
|
365
|
+
lineReveal("[data-headline]", { split: "chars", stagger: 0.03 }); // per-character rise
|
|
359
366
|
```
|
|
360
367
|
|
|
361
368
|
| Option | Default | Notes |
|
|
362
369
|
| --------- | --------------- | --------------------------------------- |
|
|
363
370
|
| `mode` | `"scroll"` | `"scroll"` or `"immediate"` |
|
|
364
|
-
| `
|
|
371
|
+
| `split` | `"lines"` | `"lines"` or `"chars"` (per-character masked rise) |
|
|
372
|
+
| `stagger` | `0.1` | seconds between lines (`0.03` for chars) |
|
|
365
373
|
| `duration`| `1` | seconds |
|
|
366
374
|
| `ease` | `"power4.out"` | |
|
|
367
375
|
| `delay` | `0` | seconds |
|
|
@@ -369,7 +377,8 @@ lineReveal("[data-lines]", { mode: "scroll" }); // reverses
|
|
|
369
377
|
| `end` | `"bottom 10%"` | ScrollTrigger end |
|
|
370
378
|
|
|
371
379
|
**DOM:** any block of text — headings with `<br>` hard breaks work. Produces
|
|
372
|
-
`.ak-line-mask > .ak-line` per line
|
|
380
|
+
`.ak-line-mask > .ak-line` per line (or `.ak-char-mask > .ak-char` with
|
|
381
|
+
`split: "chars"`); `destroy()` restores the original HTML.
|
|
373
382
|
|
|
374
383
|
#### `maskReveal(target, options?) => destroy`
|
|
375
384
|
|
|
@@ -420,6 +429,91 @@ scatterText("[data-scatter-pin]", {
|
|
|
420
429
|
`destroy()`). Line measurement waits for `document.fonts.ready` so travel
|
|
421
430
|
distance is correct with webfonts.
|
|
422
431
|
|
|
432
|
+
#### `scrambleText(target, options?) => destroy`
|
|
433
|
+
|
|
434
|
+
The decode / cipher reveal: each character churns through the charset and
|
|
435
|
+
settles on its final glyph, left to right. Letters scramble; digits,
|
|
436
|
+
punctuation and spaces stay put; case is preserved.
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
scrambleText("[data-scramble]"); // once, on enter
|
|
440
|
+
scrambleText("[data-headline]", { mode: "immediate", delay: 0.2 }); // right away
|
|
441
|
+
scrambleText(".nav-link", { mode: "hover", durationPerChar: 0.12 }); // on hover
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
| Option | Default | Notes |
|
|
445
|
+
| --- | --- | --- |
|
|
446
|
+
| `mode` | `"scroll"` | `"scroll"` (once on enter), `"immediate"`, or `"hover"` (re-scrambles on pointerenter) |
|
|
447
|
+
| `charset` | `"abcdefghijklmnopqrstuvwxyz"` | glyphs letters churn through |
|
|
448
|
+
| `durationPerChar` | `0.18` | seconds each character scrambles |
|
|
449
|
+
| `stagger` | `0.04` | seconds between character starts |
|
|
450
|
+
| `delay` | `0` | seconds before the timeline starts |
|
|
451
|
+
| `start` | `"top 80%"` | ScrollTrigger start (`mode: "scroll"`) |
|
|
452
|
+
|
|
453
|
+
**DOM:** plain-text elements only — the effect rewrites `textContent` while
|
|
454
|
+
scrambling and restores the original exactly on `destroy()`.
|
|
455
|
+
|
|
456
|
+
#### `rollText(target, options?) => destroy`
|
|
457
|
+
|
|
458
|
+
The rolling word rotator: two or more rows stacked into a hidden overflow box
|
|
459
|
+
one row tall, rolling to the next on an interval. The first row is cloned at
|
|
460
|
+
the end so the wrap is seamless (same trick as `marquee()`).
|
|
461
|
+
|
|
462
|
+
```html
|
|
463
|
+
<span class="ak-roll" data-roll>
|
|
464
|
+
<span>Design</span><span>Build</span><span>Motion</span>
|
|
465
|
+
</span>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
```ts
|
|
469
|
+
rollText("[data-roll]", { interval: 2.2, duration: 0.6 });
|
|
470
|
+
rollText("[data-roll-rev]", { direction: "down" }); // walk rows in reverse
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
| Option | Default | Notes |
|
|
474
|
+
| --- | --- | --- |
|
|
475
|
+
| `interval` | `2.2` | seconds each row is shown (including the roll) |
|
|
476
|
+
| `duration` | `0.6` | roll duration, seconds |
|
|
477
|
+
| `ease` | `"power4.inOut"` | GSAP ease for the roll |
|
|
478
|
+
| `direction` | `"up"` | `"up"` or `"down"` |
|
|
479
|
+
|
|
480
|
+
**DOM:** rows are direct children of the target; the effect stacks them as
|
|
481
|
+
blocks itself. `destroy()` unwraps the rows, removes the clone and restores
|
|
482
|
+
every inline style — markup comes back byte-identical.
|
|
483
|
+
|
|
484
|
+
#### `flipWords(target, { to }, options?) => destroy`
|
|
485
|
+
|
|
486
|
+
Words measured in one layout, moved into another and animated from where
|
|
487
|
+
they stood — the FLIP technique: a column of words fans out into a row,
|
|
488
|
+
driven by scroll progress or played once.
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
// Both blocks share one grid cell so the stage never reflows mid-flight:
|
|
492
|
+
flipWords("[data-flip-from]", { to: "[data-flip-to]", scrub: 0.6 });
|
|
493
|
+
|
|
494
|
+
// One-shot on enter (reverses on leave-back), or immediately:
|
|
495
|
+
flipWords("[data-flip-from]", { to: "[data-flip-to]", mode: "scroll" });
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
| Option | Default | Notes |
|
|
499
|
+
| --- | --- | --- |
|
|
500
|
+
| `to` | — (required) | destination block; every word is moved into it |
|
|
501
|
+
| `words` | source children | `[data-flip-word]` matches, else the source's element children |
|
|
502
|
+
| `duration` | `1.4` | seconds for one word's travel |
|
|
503
|
+
| `ease` | `"power4.inOut"` | GSAP ease |
|
|
504
|
+
| `stagger` | `0.2` | seconds between word starts |
|
|
505
|
+
| `scale` | `0.2` | mid-flight squash each word pops through (`0` disables) |
|
|
506
|
+
| `mode` | `"scroll"` | or `"immediate"` to play at once |
|
|
507
|
+
| `scrub` | unset | number = scrub smoothing seconds, `true` = immediate |
|
|
508
|
+
| `start / end` | `"top 75%"` / `"bottom 45%"` | ScrollTrigger positions |
|
|
509
|
+
|
|
510
|
+
**DOM:** put the source and destination blocks in the same grid cell
|
|
511
|
+
(`grid-area: 1 / 1`, a `min-height` on both keeps the stage stable). The words
|
|
512
|
+
live in the destination from init but render at their source positions until
|
|
513
|
+
the timeline runs, so scrubbing reverses perfectly. `destroy()` kills the
|
|
514
|
+
timeline, puts every word back in its original parent and restores the
|
|
515
|
+
inline transform.
|
|
516
|
+
|
|
423
517
|
#### `counter(target, options?) => destroy`
|
|
424
518
|
|
|
425
519
|
Tabular number ticker.
|
|
@@ -428,6 +522,7 @@ Tabular number ticker.
|
|
|
428
522
|
counter("[data-count]", { to: 240, duration: 3, suffix: "+" });
|
|
429
523
|
counter("[data-count-scroll]", { to: 98, onScroll: true }); // waits for view
|
|
430
524
|
counter("[data-count-pad]", { to: 42, pad: 3 }); // 000 → 042
|
|
525
|
+
counter("[data-progress]", { progress: true, pad: 2, suffix: "%" }); // scrubs 00% → 100%
|
|
431
526
|
```
|
|
432
527
|
|
|
433
528
|
| Option | Default |
|
|
@@ -438,7 +533,8 @@ counter("[data-count-pad]", { to: 42, pad: 3 }); // 000 →
|
|
|
438
533
|
| `pad` | `0` (none) |
|
|
439
534
|
| `suffix` | `""` |
|
|
440
535
|
| `onScroll` | `false` |
|
|
441
|
-
| `
|
|
536
|
+
| `progress` | `false` — scrub the value from scroll progress instead of a timed tween |
|
|
537
|
+
| `start` / `end` | `"top 90%"` / `"bottom top"` — ScrollTrigger positions (`end` with `progress`) |
|
|
442
538
|
| `onComplete` | `(value) => {}` |
|
|
443
539
|
|
|
444
540
|
---
|
|
@@ -457,6 +553,61 @@ revealRule("[data-rule]", { duration: 1, delay: 0.2 }); // default ease: EASES.r
|
|
|
457
553
|
|
|
458
554
|
**DOM:** any element that should animate `width: 0 → 100%` when it enters.
|
|
459
555
|
|
|
556
|
+
#### `unfoldReveal(target, options?) => destroy`
|
|
557
|
+
|
|
558
|
+
Blocks that grow open from an edge: `scaleY: 0 → 1` from the top (or bottom)
|
|
559
|
+
for a vertical unfold, `scaleX: 0 → 1` from the left for a horizontal one —
|
|
560
|
+
targets stagger together off the first match's trigger.
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
unfoldReveal("[data-unfold]", { axis: "y", origin: "top" });
|
|
564
|
+
unfoldReveal("[data-unfold-x]", { axis: "x", origin: "left", duration: 1.2 });
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
| Option | Default | Notes |
|
|
568
|
+
| --- | --- | --- |
|
|
569
|
+
| `axis` | `"y"` | `"y"` → `scaleY`, `"x"` → `scaleX` |
|
|
570
|
+
| `origin` | `"top"` / `"left"` | `transformOrigin`, defaults per axis |
|
|
571
|
+
| `duration` | `0.7` | seconds |
|
|
572
|
+
| `ease` | `"power3.out"` | GSAP ease |
|
|
573
|
+
| `stagger` | `0.08` | seconds between targets |
|
|
574
|
+
| `delay` | `0` | seconds |
|
|
575
|
+
| `mode` | `"scroll"` | or `"immediate"` to play at once |
|
|
576
|
+
| `start` | `"top 85%"` | ScrollTrigger start |
|
|
577
|
+
| `replay` | `false` | re-unfold when leaving / re-entering |
|
|
578
|
+
|
|
579
|
+
`destroy()` clears `transform` + `transform-origin`, so elements rest exactly
|
|
580
|
+
as authored.
|
|
581
|
+
|
|
582
|
+
#### `clipWipe(target, options?) => destroy`
|
|
583
|
+
|
|
584
|
+
A `clip-path: inset()` reveal: the element is collapsed behind one edge,
|
|
585
|
+
corner (or inside a frame margin) and the inset animates to zero so it wipes
|
|
586
|
+
into view.
|
|
587
|
+
|
|
588
|
+
```ts
|
|
589
|
+
clipWipe("[data-clip]", { from: "left" }); // inset(0 100% 0 0) → 0
|
|
590
|
+
clipWipe("[data-frame]", { from: "frame", inset: 12 }); // opens out of a frame
|
|
591
|
+
clipWipe("[data-corner]", { from: "bottom-right" }); // opens toward the opposite corner
|
|
592
|
+
clipWipe("[data-scrub]", { from: "frame", scrub: 0.5 }); // bound to scroll progress
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
| Option | Default | Notes |
|
|
596
|
+
| --- | --- | --- |
|
|
597
|
+
| `from` | `"left"` | `"left"` / `"right"` / `"top"` / `"bottom"`, a corner (`"top-left"` / `"top-right"` / `"bottom-left"` / `"bottom-right"`), or `"frame"` |
|
|
598
|
+
| `inset` | `15` | frame margin in % (`from: "frame"`) |
|
|
599
|
+
| `duration` | `1` | seconds |
|
|
600
|
+
| `ease` | `"power3.out"` | GSAP ease |
|
|
601
|
+
| `stagger` | `0.08` | seconds between targets |
|
|
602
|
+
| `mode` | `"scroll"` | or `"immediate"` to play at once |
|
|
603
|
+
| `start` | `"top 85%"` | ScrollTrigger start |
|
|
604
|
+
| `end` | `"top 20%"` | ScrollTrigger end (scrub mode) |
|
|
605
|
+
| `scrub` | unset | number = scrub smoothing seconds, `true` = immediate: tie the wipe to scroll progress |
|
|
606
|
+
| `replay` | `false` | re-wipe when leaving / re-entering |
|
|
607
|
+
|
|
608
|
+
Works on images, video, blocks and text. `destroy()` removes the inline
|
|
609
|
+
`clip-path`, restoring the authored (visible) state.
|
|
610
|
+
|
|
460
611
|
#### `parallax(target, options?) => destroy`
|
|
461
612
|
|
|
462
613
|
`data-speed` parallax over everything inside `target`.
|
|
@@ -546,6 +697,34 @@ heroShrink("[data-hero-media]", { offsetY: "49vh", scale: 0.23, scrub: 1 });
|
|
|
546
697
|
// options: offsetX "0px", start "top top", end "bottom top"
|
|
547
698
|
```
|
|
548
699
|
|
|
700
|
+
#### `mediaSettle(target, options?) => destroy`
|
|
701
|
+
|
|
702
|
+
Images and video that arrive slightly oversized and ease down to size as the
|
|
703
|
+
section enters — content lands instead of popping in. Set `scrub` to bind the
|
|
704
|
+
settle to scroll progress, or `replay: true` to reverse back to `from` on
|
|
705
|
+
leave-back and replay on every re-enter.
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
mediaSettle("[data-settle]", { from: 1.15, duration: 1.5 }); // on enter
|
|
709
|
+
mediaSettle("[data-settle-scrub]", { scrub: 0.5, from: 1.2 }); // scroll-bound
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
| Option | Default | Notes |
|
|
713
|
+
| --- | --- | --- |
|
|
714
|
+
| `from` | `1.15` | starting scale — settles down to 1 |
|
|
715
|
+
| `duration` | `1.5` | seconds (enter mode) |
|
|
716
|
+
| `ease` | `"power2.out"` | GSAP ease (enter mode) |
|
|
717
|
+
| `origin` | `"center"` | `transformOrigin` |
|
|
718
|
+
| `stagger` | `0.06` | seconds between targets |
|
|
719
|
+
| `mode` | `"scroll"` | or `"immediate"` to play at once |
|
|
720
|
+
| `start` / `end` | `"top 75%"` / `"bottom top"` | ScrollTrigger positions |
|
|
721
|
+
| `scrub` | unset | number = scrub smoothing seconds, `true` = immediate |
|
|
722
|
+
| `replay` | `false` | re-settle when leaving / re-entering (enter mode) |
|
|
723
|
+
|
|
724
|
+
`destroy()` clears `transform` — media returns to its authored scale. Unlike
|
|
725
|
+
`heroShrink()` (which scrubs media down as it *leaves*), `mediaSettle()` plays
|
|
726
|
+
the entrance.
|
|
727
|
+
|
|
549
728
|
---
|
|
550
729
|
|
|
551
730
|
### Loops & marquees
|
|
@@ -638,7 +817,7 @@ would rewrite `height` *after* teardown) and clears the inline height.
|
|
|
638
817
|
|
|
639
818
|
### Buttons & links
|
|
640
819
|
|
|
641
|
-
Hover affordances for CTAs and inline links.
|
|
820
|
+
Hover affordances for CTAs and inline links — fills, underlines, magnetic pulls.
|
|
642
821
|
|
|
643
822
|
#### `liquidButton(target, options?) => destroy`
|
|
644
823
|
|
|
@@ -682,6 +861,29 @@ Pure CSS under the hood — it just adds/removes the `.ak-underline` class whose
|
|
|
682
861
|
`::after` sweep is styled by the companion stylesheet, and `destroy()` removes
|
|
683
862
|
the class again.
|
|
684
863
|
|
|
864
|
+
#### `magnetic(target, options?) => destroy`
|
|
865
|
+
|
|
866
|
+
Buttons and links that lean toward the pointer while hovered — following a
|
|
867
|
+
fraction of the pull with a tilt — then spring back to rest with an elastic
|
|
868
|
+
snap on leave.
|
|
869
|
+
|
|
870
|
+
```ts
|
|
871
|
+
magnetic("[data-magnet]", { strength: 0.5, rotation: 10, scale: 1.04 });
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
| Option | Default | Notes |
|
|
875
|
+
| --- | --- | --- |
|
|
876
|
+
| `strength` | `0.4` | how far the element follows the pointer — fraction of its own box |
|
|
877
|
+
| `rotation` | `8` | max tilt in degrees at full pull (`0` disables) |
|
|
878
|
+
| `scale` | `1` | scale held while the pointer is over the element (`1` = none) |
|
|
879
|
+
| `duration` | `1.2` | spring-back duration, seconds |
|
|
880
|
+
| `ease` | `"elastic.out(1, 0.35)"` | spring-back ease |
|
|
881
|
+
|
|
882
|
+
**DOM:** pass a list (selector, array, NodeList) — each element gets its own
|
|
883
|
+
listeners and its own pull. Keep CSS transitions off `transform` for magnetic
|
|
884
|
+
elements (GSAP animates transform directly). `destroy()` removes the
|
|
885
|
+
listeners, kills in-flight tweens and restores the inline transform.
|
|
886
|
+
|
|
685
887
|
---
|
|
686
888
|
|
|
687
889
|
### Navigation & overlays
|
|
@@ -725,7 +927,12 @@ Curtain uses `EASES.curtain` (`.76,0,.24,1`).
|
|
|
725
927
|
|
|
726
928
|
#### `cursorFollower(zone, options?) => destroy`
|
|
727
929
|
|
|
728
|
-
Spring-followed cursor tag, e.g. "▶ Play Showreel" over a video.
|
|
930
|
+
Spring-followed cursor tag, e.g. "▶ Play Showreel" over a video. The tag is
|
|
931
|
+
pinned `position: fixed` and driven in viewport space (`clientX/Y + offset`),
|
|
932
|
+
so it can live anywhere in the DOM — it only reacts while the pointer is over
|
|
933
|
+
the zone. Style it with `opacity: 0` and light text (`color: #fff`): the
|
|
934
|
+
`exclusion` blend inverts the tag against whatever is behind it, which only
|
|
935
|
+
reads with a light source colour.
|
|
729
936
|
|
|
730
937
|
```ts
|
|
731
938
|
cursorFollower("[data-showreel]", {
|
|
@@ -827,14 +1034,15 @@ Types: `TargetLike`, `Destroy`, `CommonOptions`.
|
|
|
827
1034
|
## Styling
|
|
828
1035
|
|
|
829
1036
|
```ts
|
|
830
|
-
import "anim-kit/styles"; // → dist/styles/anim-kit.css
|
|
1037
|
+
import "@cosmictraveler002/anim-kit/styles"; // → dist/styles/anim-kit.css
|
|
831
1038
|
```
|
|
832
1039
|
|
|
833
1040
|
The companion stylesheet supplies:
|
|
834
1041
|
|
|
835
1042
|
- **Design tokens:** `--ak-primary`, `--ak-curtain`, `--ak-reveal`, `--ak-out`
|
|
836
|
-
- **Text masks:** `.ak-line-mask`, `.ak-line`, `.ak-word`, `.ak-space`
|
|
1043
|
+
- **Text masks:** `.ak-line-mask`, `.ak-line`, `.ak-char-mask`, `.ak-char`, `.ak-word`, `.ak-space`
|
|
837
1044
|
- **Heading masks:** `.ak-mask`, `.ak-mask__inner`
|
|
1045
|
+
- **Rolling text:** `.ak-roll`, `.ak-roll__inner`
|
|
838
1046
|
- **Liquid button:** `.ak-liquid`, `.ak-liquid__wave`, `.ak-liquid__label`
|
|
839
1047
|
- **Underline:** `.ak-underline`
|
|
840
1048
|
- **Marquee:** `.ak-marquee`, `.ak-marquee__viewport`, `.ak-marquee__track`
|
|
@@ -924,15 +1132,30 @@ npm run typecheck # tsc --noEmit (what CI runs)
|
|
|
924
1132
|
```
|
|
925
1133
|
|
|
926
1134
|
The demo page wires the whole effect set against one document —
|
|
927
|
-
`demo/index.html` + `demo/demo.js
|
|
928
|
-
|
|
929
|
-
instead.)
|
|
1135
|
+
`demo/index.html` + `demo/demo.js` — and every effect in the catalogue has
|
|
1136
|
+
an on-page *copy prompt* chip (the demo smoke enforces the two-way mapping).
|
|
1137
|
+
(`split()` is exercised by the unit smoke instead.)
|
|
1138
|
+
|
|
1139
|
+
Two pages share that wiring:
|
|
1140
|
+
|
|
1141
|
+
- **`demo/index.html`** (default — `npm run demo` → `/demo/`) — import map on
|
|
1142
|
+
local files (`/node_modules/...`, `/dist/index.js`): fast iteration, works
|
|
1143
|
+
offline, and the page `demo-smoke` drives.
|
|
1144
|
+
- **`demo/anim_kit_live.html`** (`/demo/anim_kit_live.html`) — byte-for-byte
|
|
1145
|
+
the same page wired to the **version-pinned CDN** (jsdelivr `@<version>` for
|
|
1146
|
+
the package, `gsap@3.15.0`, `lenis@1.3.26`): the exact path visitors copy
|
|
1147
|
+
from the prompts and docs, so CDN wiring gets the same manual QA as the
|
|
1148
|
+
local build. `demo-smoke` fails if the two files drift apart or the pin
|
|
1149
|
+
stops matching the release version, and the page only loads once that
|
|
1150
|
+
version is published to npm.
|
|
930
1151
|
|
|
931
1152
|
### Copy-prompt API
|
|
932
1153
|
|
|
933
1154
|
The demo server doubles as a **prompt server**: every effect has a ready-to-
|
|
934
|
-
paste *"how to implement this with anim-kit"* prompt — markup,
|
|
935
|
-
|
|
1155
|
+
paste *"how to implement this with anim-kit"* prompt — markup, a numbered
|
|
1156
|
+
copy-paste procedure that runs straight from the version-pinned CDN (no
|
|
1157
|
+
build step, plus the npm import for bundlers), initialisation call, options
|
|
1158
|
+
table, teardown and gotchas:
|
|
936
1159
|
|
|
937
1160
|
```bash
|
|
938
1161
|
curl http://localhost:4321/api/prompts # { count, categories, prompts: [{ id, title, summary, category, subcategory, text }] }
|
|
@@ -940,7 +1163,7 @@ curl http://localhost:4321/api/prompts/marquee # one prompt, text/plain
|
|
|
940
1163
|
```
|
|
941
1164
|
|
|
942
1165
|
On the page, every labelled section carries a **copy prompt** chip, and the
|
|
943
|
-
floating **⧉ prompts (
|
|
1166
|
+
floating **⧉ prompts (29)** button at the bottom right opens the full
|
|
944
1167
|
catalogue grouped by [effect category](#effect-categories) — one click copies
|
|
945
1168
|
an effect's prompt (the prompt states its category), *copy all* puts the
|
|
946
1169
|
entire set on the clipboard. The catalogue lives in `scripts/prompts.mjs`:
|
|
@@ -951,28 +1174,30 @@ and slotting the effect into a subcategory.
|
|
|
951
1174
|
**Unit smoke** (`scripts/smoke.mjs`) runs the built bundle in **jsdom** and
|
|
952
1175
|
asserts:
|
|
953
1176
|
|
|
954
|
-
1. all
|
|
1177
|
+
1. all 45 exports are present;
|
|
955
1178
|
2. plugins (`ScrollTrigger`, `SplitText`, `Draggable`, `CustomEase`,
|
|
956
|
-
`ScrollSmoother`) and the 4 custom eases are registered;
|
|
1179
|
+
`Flip`, `ScrollSmoother`) and the 4 custom eases are registered;
|
|
957
1180
|
3. every effect no-ops safely on missing targets;
|
|
958
|
-
4.
|
|
1181
|
+
4. 24 effects mount on real markup and unmount cleanly;
|
|
959
1182
|
5. `preloader` ticks in both the positional and options-object call forms;
|
|
960
|
-
6. `lineReveal` actually splits into masked lines and
|
|
961
|
-
destroy;
|
|
962
|
-
7. `
|
|
1183
|
+
6. `lineReveal` actually splits into masked lines (and per-character masks
|
|
1184
|
+
with `split: "chars"`) and restores markup on destroy;
|
|
1185
|
+
7. `scrambleText` restores its text, `rollText` wraps/unwraps its rows, and
|
|
1186
|
+
`counter({ progress: true })` renders a scrubbed readout;
|
|
1187
|
+
8. `utils`, `compose` and `guard` behave per contract.
|
|
963
1188
|
|
|
964
1189
|
**Demo smoke** (`scripts/demo-smoke.mjs`) loads the real `demo/index.html` and
|
|
965
1190
|
executes the real `demo/demo.js` wiring against it, then asserts the effects
|
|
966
1191
|
actually *did* something (hero split, preloader counter ticking, marquee track
|
|
967
1192
|
duplicated, per-call liquid directions, menu/theme/smooth-scroll handles in
|
|
968
|
-
their initial state), that ~
|
|
1193
|
+
their initial state), that ~50 ScrollTriggers + a Draggable were created, that
|
|
969
1194
|
no console errors were logged, that `dragStrip` tiled its content for the
|
|
970
1195
|
seamless loop, that every effect referenced on the page has a `/api/prompts`
|
|
971
1196
|
entry, that the taxonomy classifies every effect exactly once, that the
|
|
972
1197
|
prompt dock renders one group per category with every effect listed once,
|
|
973
1198
|
and that teardown leaves **zero** live ScrollTriggers, Draggables or
|
|
974
1199
|
page-element tweens behind while restoring the original markup (marquee and
|
|
975
|
-
drag-strip clones removed).
|
|
1200
|
+
drag-strip clones removed, rolling rows unwrapped, scrambled text restored).
|
|
976
1201
|
|
|
977
1202
|
> jsdom is used deliberately: GSAP's CSSPlugin/Draggable probe element
|
|
978
1203
|
> style/computed values during registration, which a hand-rolled DOM stub
|
|
@@ -992,9 +1217,9 @@ anim-kit/
|
|
|
992
1217
|
│ │ ├─ guard.ts reduced-motion gate
|
|
993
1218
|
│ │ ├─ util.ts toArray/one/onReady/compose/raf
|
|
994
1219
|
│ │ └─ types.ts TargetLike / Destroy / CommonOptions
|
|
995
|
-
│ ├─ effects/ one file per effect (
|
|
1220
|
+
│ ├─ effects/ one file per effect (24 files, 28 effect functions)
|
|
996
1221
|
│ ├─ styles/anim-kit.css companion stylesheet
|
|
997
|
-
│ └─ index.ts barrel —
|
|
1222
|
+
│ └─ index.ts barrel — 45 exports
|
|
998
1223
|
├─ demo/ visual demo (import map, no bundler)
|
|
999
1224
|
├─ scripts/
|
|
1000
1225
|
│ ├─ serve.mjs static server + /api/prompts (:4321)
|
|
@@ -1007,7 +1232,7 @@ anim-kit/
|
|
|
1007
1232
|
├─ LICENSE MIT
|
|
1008
1233
|
└─ dist/ build output
|
|
1009
1234
|
├─ index.js / *.d.ts per-file ESM + declarations (tsc)
|
|
1010
|
-
├─ effects/*.js one module per effect → anim-kit/effects/* subpaths
|
|
1235
|
+
├─ effects/*.js one module per effect → @cosmictraveler002/anim-kit/effects/* subpaths
|
|
1011
1236
|
├─ anim-kit.standalone.js self-contained CDN bundle (gsap+lenis inlined)
|
|
1012
1237
|
└─ styles/anim-kit.css plain CSS, copied verbatim
|
|
1013
1238
|
```
|
|
@@ -1019,7 +1244,7 @@ push/PR) and `.github/workflows/release.yml` (tag `v*` → `npm publish
|
|
|
1019
1244
|
|
|
1020
1245
|
Each effect is an independent module — if you only need the marquee, import
|
|
1021
1246
|
`marquee` and the bundler drops the rest, or deep-import
|
|
1022
|
-
|
|
1247
|
+
`@cosmictraveler002/anim-kit/effects/marquee` to skip the barrel entirely.
|
|
1023
1248
|
|
|
1024
1249
|
---
|
|
1025
1250
|
|