@cosmictraveler002/anim-kit 1.0.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/LICENSE +21 -0
- package/README.md +1033 -0
- package/dist/anim-kit.standalone.js +117 -0
- package/dist/anim-kit.standalone.js.map +1 -0
- package/dist/core/gsap.d.ts +44 -0
- package/dist/core/gsap.d.ts.map +1 -0
- package/dist/core/gsap.js +71 -0
- package/dist/core/gsap.js.map +1 -0
- package/dist/core/guard.d.ts +3 -0
- package/dist/core/guard.d.ts.map +1 -0
- package/dist/core/guard.js +18 -0
- package/dist/core/guard.js.map +1 -0
- package/dist/core/smooth-scroll.d.ts +45 -0
- package/dist/core/smooth-scroll.d.ts.map +1 -0
- package/dist/core/smooth-scroll.js +98 -0
- package/dist/core/smooth-scroll.js.map +1 -0
- package/dist/core/split.d.ts +24 -0
- package/dist/core/split.d.ts.map +1 -0
- package/dist/core/split.js +117 -0
- package/dist/core/split.js.map +1 -0
- package/dist/core/types.d.ts +21 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/util.d.ts +19 -0
- package/dist/core/util.d.ts.map +1 -0
- package/dist/core/util.js +73 -0
- package/dist/core/util.js.map +1 -0
- package/dist/effects/counter.d.ts +52 -0
- package/dist/effects/counter.d.ts.map +1 -0
- package/dist/effects/counter.js +106 -0
- package/dist/effects/counter.js.map +1 -0
- package/dist/effects/cursor-follower.d.ts +19 -0
- package/dist/effects/cursor-follower.d.ts.map +1 -0
- package/dist/effects/cursor-follower.js +76 -0
- package/dist/effects/cursor-follower.js.map +1 -0
- package/dist/effects/drag-strip.d.ts +17 -0
- package/dist/effects/drag-strip.d.ts.map +1 -0
- package/dist/effects/drag-strip.js +186 -0
- package/dist/effects/drag-strip.js.map +1 -0
- package/dist/effects/hero-shrink.d.ts +15 -0
- package/dist/effects/hero-shrink.d.ts.map +1 -0
- package/dist/effects/hero-shrink.js +40 -0
- package/dist/effects/hero-shrink.js.map +1 -0
- package/dist/effects/horizontal-scroll.d.ts +17 -0
- package/dist/effects/horizontal-scroll.d.ts.map +1 -0
- package/dist/effects/horizontal-scroll.js +87 -0
- package/dist/effects/horizontal-scroll.js.map +1 -0
- package/dist/effects/line-reveal.d.ts +34 -0
- package/dist/effects/line-reveal.d.ts.map +1 -0
- package/dist/effects/line-reveal.js +71 -0
- package/dist/effects/line-reveal.js.map +1 -0
- package/dist/effects/liquid-button.d.ts +19 -0
- package/dist/effects/liquid-button.d.ts.map +1 -0
- package/dist/effects/liquid-button.js +52 -0
- package/dist/effects/liquid-button.js.map +1 -0
- package/dist/effects/logo-reveal.d.ts +15 -0
- package/dist/effects/logo-reveal.d.ts.map +1 -0
- package/dist/effects/logo-reveal.js +49 -0
- package/dist/effects/logo-reveal.js.map +1 -0
- package/dist/effects/marquee.d.ts +13 -0
- package/dist/effects/marquee.d.ts.map +1 -0
- package/dist/effects/marquee.js +143 -0
- package/dist/effects/marquee.js.map +1 -0
- package/dist/effects/mask-reveal.d.ts +36 -0
- package/dist/effects/mask-reveal.d.ts.map +1 -0
- package/dist/effects/mask-reveal.js +102 -0
- package/dist/effects/mask-reveal.js.map +1 -0
- package/dist/effects/menu-overlay.d.ts +32 -0
- package/dist/effects/menu-overlay.d.ts.map +1 -0
- package/dist/effects/menu-overlay.js +150 -0
- package/dist/effects/menu-overlay.js.map +1 -0
- package/dist/effects/nav-hide.d.ts +13 -0
- package/dist/effects/nav-hide.d.ts.map +1 -0
- package/dist/effects/nav-hide.js +68 -0
- package/dist/effects/nav-hide.js.map +1 -0
- package/dist/effects/parallax.d.ts +11 -0
- package/dist/effects/parallax.d.ts.map +1 -0
- package/dist/effects/parallax.js +48 -0
- package/dist/effects/parallax.js.map +1 -0
- package/dist/effects/preloader.d.ts +33 -0
- package/dist/effects/preloader.d.ts.map +1 -0
- package/dist/effects/preloader.js +128 -0
- package/dist/effects/preloader.js.map +1 -0
- package/dist/effects/scatter-text.d.ts +20 -0
- package/dist/effects/scatter-text.d.ts.map +1 -0
- package/dist/effects/scatter-text.js +87 -0
- package/dist/effects/scatter-text.js.map +1 -0
- package/dist/effects/stacked-cards.d.ts +24 -0
- package/dist/effects/stacked-cards.d.ts.map +1 -0
- package/dist/effects/stacked-cards.js +106 -0
- package/dist/effects/stacked-cards.js.map +1 -0
- package/dist/effects/theme-reveal.d.ts +35 -0
- package/dist/effects/theme-reveal.d.ts.map +1 -0
- package/dist/effects/theme-reveal.js +93 -0
- package/dist/effects/theme-reveal.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/anim-kit.css +361 -0
- package/package.json +80 -0
- package/src/core/gsap.ts +79 -0
- package/src/core/guard.ts +18 -0
- package/src/core/smooth-scroll.ts +143 -0
- package/src/core/split.ts +145 -0
- package/src/core/types.ts +30 -0
- package/src/core/util.ts +79 -0
- package/src/effects/counter.ts +192 -0
- package/src/effects/cursor-follower.ts +104 -0
- package/src/effects/drag-strip.ts +228 -0
- package/src/effects/hero-shrink.ts +69 -0
- package/src/effects/horizontal-scroll.ts +123 -0
- package/src/effects/line-reveal.ts +109 -0
- package/src/effects/liquid-button.ts +75 -0
- package/src/effects/logo-reveal.ts +76 -0
- package/src/effects/marquee.ts +157 -0
- package/src/effects/mask-reveal.ts +148 -0
- package/src/effects/menu-overlay.ts +218 -0
- package/src/effects/nav-hide.ts +90 -0
- package/src/effects/parallax.ts +68 -0
- package/src/effects/preloader.ts +187 -0
- package/src/effects/scatter-text.ts +129 -0
- package/src/effects/stacked-cards.ts +154 -0
- package/src/effects/theme-reveal.ts +144 -0
- package/src/index.ts +98 -0
- package/src/styles/anim-kit.css +361 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* anim-kit — companion stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* Most of the studio's motion is JS-driven, but a handful of effects are pure
|
|
5
|
+
* CSS (liquid button, underline sweep, marquee masks) and every effect needs a
|
|
6
|
+
* resting state that is safe with `prefers-reduced-motion`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* ------------------------------------------------------------------ */
|
|
10
|
+
/* Design tokens */
|
|
11
|
+
/* ------------------------------------------------------------------ */
|
|
12
|
+
|
|
13
|
+
:root {
|
|
14
|
+
--ak-primary: #6c5ce7;
|
|
15
|
+
--ak-curtain: cubic-bezier(0.76, 0, 0.24, 1);
|
|
16
|
+
--ak-reveal: cubic-bezier(0.165, 0.84, 0.44, 1);
|
|
17
|
+
--ak-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* ------------------------------------------------------------------ */
|
|
21
|
+
/* Text splitting — masks */
|
|
22
|
+
/* ------------------------------------------------------------------ */
|
|
23
|
+
|
|
24
|
+
/* SplitText / split() output: one mask per line so lines can slide up. */
|
|
25
|
+
.ak-line,
|
|
26
|
+
.ak-line-mask {
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
display: block;
|
|
29
|
+
/* Negate the descender clipping every masked-line setup has to fight. */
|
|
30
|
+
padding-bottom: 0.12em;
|
|
31
|
+
margin-bottom: -0.12em;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.ak-line > *,
|
|
35
|
+
.ak-line-mask > * {
|
|
36
|
+
display: block;
|
|
37
|
+
will-change: transform;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ak-char,
|
|
41
|
+
.ak-word {
|
|
42
|
+
display: inline-block;
|
|
43
|
+
will-change: transform;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.ak-space {
|
|
47
|
+
display: inline-block;
|
|
48
|
+
white-space: pre;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ------------------------------------------------------------------ */
|
|
52
|
+
/* Mask reveal (heading lines) */
|
|
53
|
+
/* ------------------------------------------------------------------ */
|
|
54
|
+
|
|
55
|
+
.ak-mask {
|
|
56
|
+
display: block;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ak-mask__inner {
|
|
61
|
+
display: block;
|
|
62
|
+
will-change: transform;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ------------------------------------------------------------------ */
|
|
66
|
+
/* Liquid button */
|
|
67
|
+
/* ------------------------------------------------------------------ */
|
|
68
|
+
|
|
69
|
+
.ak-liquid {
|
|
70
|
+
position: relative;
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
isolation: isolate;
|
|
76
|
+
padding: 0.4em 1em;
|
|
77
|
+
border: 0;
|
|
78
|
+
background: var(--ak-foreground, #111);
|
|
79
|
+
color: var(--ak-background, #fff);
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
transition: color 700ms ease-out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ak-liquid__wave {
|
|
85
|
+
position: absolute;
|
|
86
|
+
left: 50%;
|
|
87
|
+
top: 50%;
|
|
88
|
+
height: 160%;
|
|
89
|
+
width: 160%;
|
|
90
|
+
pointer-events: none;
|
|
91
|
+
transform: translate(-50%, 120%);
|
|
92
|
+
transition-property: transform;
|
|
93
|
+
transition-timing-function: ease-out;
|
|
94
|
+
transition-duration: var(--ak-liquid-duration, 900ms);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Rising from the bottom → the curved edge peaks on the way up. */
|
|
98
|
+
[data-ak-liquid="up"] .ak-liquid__wave {
|
|
99
|
+
transform: translate(-50%, 120%);
|
|
100
|
+
}
|
|
101
|
+
[data-ak-liquid="up"]:hover .ak-liquid__wave,
|
|
102
|
+
[data-ak-liquid="up"]:focus-visible .ak-liquid__wave {
|
|
103
|
+
transform: translate(-50%, -66.666%);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Falling in from the top. */
|
|
107
|
+
[data-ak-liquid="down"] .ak-liquid__wave {
|
|
108
|
+
transform: translate(-50%, -120%);
|
|
109
|
+
}
|
|
110
|
+
[data-ak-liquid="down"]:hover .ak-liquid__wave,
|
|
111
|
+
[data-ak-liquid="down"]:focus-visible .ak-liquid__wave {
|
|
112
|
+
transform: translate(-50%, -66.666%);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.ak-liquid__wave path {
|
|
116
|
+
fill: var(--ak-liquid-fill, var(--ak-primary));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ak-liquid:hover,
|
|
120
|
+
.ak-liquid:focus-visible {
|
|
121
|
+
color: var(--ak-liquid-label, #fff);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.ak-liquid__label {
|
|
125
|
+
position: relative;
|
|
126
|
+
z-index: 10;
|
|
127
|
+
display: inline-flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
gap: 0.5em;
|
|
130
|
+
white-space: nowrap;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ------------------------------------------------------------------ */
|
|
134
|
+
/* Underline sweep */
|
|
135
|
+
/* ------------------------------------------------------------------ */
|
|
136
|
+
|
|
137
|
+
.ak-underline {
|
|
138
|
+
position: relative;
|
|
139
|
+
display: inline-block;
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.ak-underline::after {
|
|
144
|
+
content: "";
|
|
145
|
+
position: absolute;
|
|
146
|
+
bottom: 0;
|
|
147
|
+
left: 0;
|
|
148
|
+
height: 1px;
|
|
149
|
+
width: 0;
|
|
150
|
+
background: currentColor;
|
|
151
|
+
transition: width 300ms var(--ak-out);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.ak-underline:hover::after,
|
|
155
|
+
.ak-underline:focus-visible::after {
|
|
156
|
+
width: 100%;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* ------------------------------------------------------------------ */
|
|
160
|
+
/* Marquee */
|
|
161
|
+
/* ------------------------------------------------------------------ */
|
|
162
|
+
|
|
163
|
+
.ak-marquee {
|
|
164
|
+
position: relative;
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
width: 100%;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.ak-marquee__viewport {
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.ak-marquee__track {
|
|
174
|
+
display: flex;
|
|
175
|
+
width: max-content;
|
|
176
|
+
will-change: transform;
|
|
177
|
+
backface-visibility: hidden;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Fade the first/last 10vw into the page background. */
|
|
181
|
+
.ak-marquee::before,
|
|
182
|
+
.ak-marquee::after {
|
|
183
|
+
content: "";
|
|
184
|
+
position: absolute;
|
|
185
|
+
top: 0;
|
|
186
|
+
bottom: 0;
|
|
187
|
+
width: 10vw;
|
|
188
|
+
z-index: 2;
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
}
|
|
191
|
+
.ak-marquee::before {
|
|
192
|
+
left: 0;
|
|
193
|
+
background: linear-gradient(to right, var(--ak-background, #fff) 0%, transparent 100%);
|
|
194
|
+
}
|
|
195
|
+
.ak-marquee::after {
|
|
196
|
+
right: 0;
|
|
197
|
+
background: linear-gradient(to left, var(--ak-background, #fff) 0%, transparent 100%);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* ------------------------------------------------------------------ */
|
|
201
|
+
/* Drag strip */
|
|
202
|
+
/* ------------------------------------------------------------------ */
|
|
203
|
+
|
|
204
|
+
.ak-drag-track {
|
|
205
|
+
display: flex;
|
|
206
|
+
gap: 1rem;
|
|
207
|
+
width: max-content;
|
|
208
|
+
cursor: grab;
|
|
209
|
+
user-select: none;
|
|
210
|
+
touch-action: pan-y;
|
|
211
|
+
will-change: transform;
|
|
212
|
+
}
|
|
213
|
+
.ak-drag-track:active {
|
|
214
|
+
cursor: grabbing;
|
|
215
|
+
}
|
|
216
|
+
.ak-drag-track > * {
|
|
217
|
+
transform-origin: 50% 100%;
|
|
218
|
+
will-change: transform;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ------------------------------------------------------------------ */
|
|
222
|
+
/* Menu overlay */
|
|
223
|
+
/* ------------------------------------------------------------------ */
|
|
224
|
+
|
|
225
|
+
.menu-overlay {
|
|
226
|
+
position: fixed;
|
|
227
|
+
inset: 0;
|
|
228
|
+
width: 100vw;
|
|
229
|
+
height: 100svh;
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
justify-content: flex-start;
|
|
233
|
+
background: var(--ak-primary);
|
|
234
|
+
z-index: 9999;
|
|
235
|
+
pointer-events: none;
|
|
236
|
+
will-change: clip-path;
|
|
237
|
+
/* Matches the collapsed-at-bottom resting state JS sets first. */
|
|
238
|
+
clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.menu-overlay-bar {
|
|
242
|
+
position: fixed;
|
|
243
|
+
top: 0;
|
|
244
|
+
left: 0;
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: space-between;
|
|
248
|
+
width: 100vw;
|
|
249
|
+
padding: 20px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.menu-link {
|
|
253
|
+
overflow: hidden;
|
|
254
|
+
padding-left: 20px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.menu-overlay .menu-link h1,
|
|
258
|
+
.menu-overlay .menu-link a {
|
|
259
|
+
display: block;
|
|
260
|
+
text-transform: uppercase;
|
|
261
|
+
font-size: 5vw;
|
|
262
|
+
font-weight: 400;
|
|
263
|
+
line-height: 0.9;
|
|
264
|
+
will-change: transform;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
@media (max-width: 767px) {
|
|
268
|
+
.menu-overlay .menu-link h1,
|
|
269
|
+
.menu-overlay .menu-link a {
|
|
270
|
+
font-size: 16vw;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* ------------------------------------------------------------------ */
|
|
275
|
+
/* Stacked cards */
|
|
276
|
+
/* ------------------------------------------------------------------ */
|
|
277
|
+
|
|
278
|
+
.ak-stack-viewport {
|
|
279
|
+
position: sticky;
|
|
280
|
+
top: 0;
|
|
281
|
+
height: 100vh;
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
justify-content: center;
|
|
285
|
+
overflow: hidden;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.ak-stack-cards {
|
|
289
|
+
display: flex;
|
|
290
|
+
gap: 1rem;
|
|
291
|
+
height: 100vh;
|
|
292
|
+
width: 100%;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.ak-card {
|
|
298
|
+
will-change: transform;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* ------------------------------------------------------------------ */
|
|
302
|
+
/* Counter */
|
|
303
|
+
/* ------------------------------------------------------------------ */
|
|
304
|
+
|
|
305
|
+
.ak-counter {
|
|
306
|
+
font-variant-numeric: tabular-nums;
|
|
307
|
+
font-feature-settings: "tnum" 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* ------------------------------------------------------------------ */
|
|
311
|
+
/* Equaliser */
|
|
312
|
+
/* ------------------------------------------------------------------ */
|
|
313
|
+
|
|
314
|
+
.ak-eq {
|
|
315
|
+
display: flex;
|
|
316
|
+
align-items: center;
|
|
317
|
+
gap: 0.25rem;
|
|
318
|
+
height: 18px;
|
|
319
|
+
width: 100%;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.ak-eq > * {
|
|
323
|
+
width: 1px;
|
|
324
|
+
height: 4px;
|
|
325
|
+
border-radius: 999px;
|
|
326
|
+
background: currentColor;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* ------------------------------------------------------------------ */
|
|
330
|
+
/* Reduced motion — show final states, never animate */
|
|
331
|
+
/* ------------------------------------------------------------------ */
|
|
332
|
+
|
|
333
|
+
@media (prefers-reduced-motion: reduce) {
|
|
334
|
+
.ak-line,
|
|
335
|
+
.ak-line-mask,
|
|
336
|
+
.ak-mask {
|
|
337
|
+
overflow: visible;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.ak-liquid__wave,
|
|
341
|
+
.ak-underline::after,
|
|
342
|
+
.ak-marquee__track,
|
|
343
|
+
.ak-drag-track,
|
|
344
|
+
.ak-card,
|
|
345
|
+
.menu-overlay {
|
|
346
|
+
transition: none !important;
|
|
347
|
+
animation: none !important;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.ak-liquid__wave {
|
|
351
|
+
transform: translate(-50%, 120%) !important;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
[data-ak-liquid]:hover .ak-liquid__wave {
|
|
355
|
+
transform: translate(-50%, -66.666%) !important;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.menu-overlay {
|
|
359
|
+
display: none;
|
|
360
|
+
}
|
|
361
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cosmictraveler002/anim-kit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Modular, framework-agnostic animation library inspired by dzinrstudio.com — GSAP + ScrollTrigger + Lenis effects packaged as composable ES modules.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./standalone": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/anim-kit.standalone.js",
|
|
18
|
+
"default": "./dist/anim-kit.standalone.js"
|
|
19
|
+
},
|
|
20
|
+
"./effects/*": {
|
|
21
|
+
"types": "./dist/effects/*.d.ts",
|
|
22
|
+
"import": "./dist/effects/*.js",
|
|
23
|
+
"default": "./dist/effects/*.js"
|
|
24
|
+
},
|
|
25
|
+
"./effects/*.js": {
|
|
26
|
+
"types": "./dist/effects/*.d.ts",
|
|
27
|
+
"import": "./dist/effects/*.js",
|
|
28
|
+
"default": "./dist/effects/*.js"
|
|
29
|
+
},
|
|
30
|
+
"./styles": "./dist/styles/anim-kit.css",
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"src"
|
|
36
|
+
],
|
|
37
|
+
"sideEffects": [
|
|
38
|
+
"*.css"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-assets.mjs && tsup",
|
|
42
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
43
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
44
|
+
"demo": "node scripts/serve.mjs",
|
|
45
|
+
"test": "npm run build && node scripts/smoke.mjs && node scripts/demo-smoke.mjs",
|
|
46
|
+
"smoke": "node scripts/smoke.mjs && node scripts/demo-smoke.mjs",
|
|
47
|
+
"prepublishOnly": "npm run build && npm run smoke"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"animation",
|
|
51
|
+
"gsap",
|
|
52
|
+
"scrolltrigger",
|
|
53
|
+
"lenis",
|
|
54
|
+
"smooth-scroll",
|
|
55
|
+
"marquee",
|
|
56
|
+
"parallax",
|
|
57
|
+
"preloader"
|
|
58
|
+
],
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/Cosmictraveler002/Ani-Kit.git"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/Cosmictraveler002/Ani-Kit#readme",
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/Cosmictraveler002/Ani-Kit/issues"
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"gsap": "^3.13.0",
|
|
73
|
+
"lenis": "^1.3.11"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"jsdom": "^30.1.1",
|
|
77
|
+
"tsup": "^8.5.1",
|
|
78
|
+
"typescript": "^5.7.3"
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/core/gsap.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central GSAP bootstrap: registers every plugin anim-kit touches, and the
|
|
3
|
+
* custom eases that give the DZ!NR feel.
|
|
4
|
+
*
|
|
5
|
+
* Importing this module twice is safe — registration is idempotent.
|
|
6
|
+
*/
|
|
7
|
+
import { gsap } from "gsap";
|
|
8
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
9
|
+
import { SplitText } from "gsap/SplitText";
|
|
10
|
+
import { Draggable } from "gsap/Draggable";
|
|
11
|
+
import { CustomEase } from "gsap/CustomEase";
|
|
12
|
+
import { ScrollSmoother } from "gsap/ScrollSmoother";
|
|
13
|
+
import { toArray } from "./util.js";
|
|
14
|
+
import type { TargetLike } from "./types.js";
|
|
15
|
+
|
|
16
|
+
export { gsap, ScrollTrigger, SplitText, Draggable, CustomEase, ScrollSmoother };
|
|
17
|
+
|
|
18
|
+
/** Named eases recovered from dzinrstudio.com's bundles. */
|
|
19
|
+
export const EASES = {
|
|
20
|
+
/** `.76,0,.2,1` — the studio's signature menu / nav curtain ease. */
|
|
21
|
+
curtain: "ak-curtain",
|
|
22
|
+
/** SVG path used for the stacked service-card cascade. */
|
|
23
|
+
cardStack: "ak-card-stack",
|
|
24
|
+
/** `.165,.84,.44,1` — their headline mask reveal. */
|
|
25
|
+
reveal: "ak-reveal",
|
|
26
|
+
/** `.895,.03,.685,.22` — preloader logo scale-out. */
|
|
27
|
+
preloadOut: "ak-preload-out",
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
let initted = false;
|
|
31
|
+
|
|
32
|
+
/** Register all plugins + custom eases exactly once. */
|
|
33
|
+
export function initGSAP(): void {
|
|
34
|
+
if (initted || typeof window === "undefined") return;
|
|
35
|
+
initted = true;
|
|
36
|
+
|
|
37
|
+
gsap.registerPlugin(ScrollTrigger, SplitText, Draggable, CustomEase);
|
|
38
|
+
|
|
39
|
+
// ScrollSmoother is a bonus for people who prefer it over Lenis; it is only
|
|
40
|
+
// registered so `ScrollSmoother.create()` works if you call it yourself.
|
|
41
|
+
try {
|
|
42
|
+
gsap.registerPlugin(ScrollSmoother);
|
|
43
|
+
} catch {
|
|
44
|
+
/* not fatal — smoother is optional */
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!CustomEase.get(EASES.curtain)) {
|
|
48
|
+
CustomEase.create(EASES.curtain, ".76,0,.2,1");
|
|
49
|
+
CustomEase.create(EASES.cardStack, "M0,0 C0,0 0.098,0.613 0.5,0.5 0.899,0.386 1,1 1,1");
|
|
50
|
+
CustomEase.create(EASES.reveal, ".165,.84,.44,1");
|
|
51
|
+
CustomEase.create(EASES.preloadOut, ".895,.03,.685,.22");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Keep ScrollTrigger honest when images/fonts land late.
|
|
55
|
+
ScrollTrigger.config({ ignoreMobileResize: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
initGSAP();
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Kill every tween that animates `target`.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately **not** `gsap.killTweensOf()`. That forwards to
|
|
64
|
+
* `tween.kill(targets, …)`, which walks each tween's *initialised*
|
|
65
|
+
* prop-tween lookup — but a tween created between ticker frames is still
|
|
66
|
+
* lazy (`_initted === false`), its lookup is empty, and the kill silently
|
|
67
|
+
* no-ops. Such a tween then fires *after* teardown (e.g. an equaliser batch
|
|
68
|
+
* rewriting `height` post-destroy). The no-arg `tween.kill()` takes the
|
|
69
|
+
* full-kill path and removes both initialised and lazy tweens from the
|
|
70
|
+
* timeline.
|
|
71
|
+
*
|
|
72
|
+
* Internal helper — effects import it from `core/gsap.js`; it is not part of
|
|
73
|
+
* the public barrel.
|
|
74
|
+
*/
|
|
75
|
+
export function killTweens(target: TargetLike | Array<Element | null | undefined>): void {
|
|
76
|
+
const raw: Array<Element | null | undefined> = Array.isArray(target) ? target : toArray(target);
|
|
77
|
+
const list = raw.filter((el): el is Element => el instanceof Element);
|
|
78
|
+
gsap.getTweensOf(list).forEach((t) => t.kill());
|
|
79
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reduced-motion helper.
|
|
3
|
+
*
|
|
4
|
+
* Every effect calls `guard(options, run)`.
|
|
5
|
+
* - normal motion -> runs `run()` and returns its destroy fn
|
|
6
|
+
* - reduced motion -> returns a no-op so nothing animates. Effects that need
|
|
7
|
+
* content visible in both states should render the final
|
|
8
|
+
* state in CSS (see styles/anim-kit.css).
|
|
9
|
+
*/
|
|
10
|
+
import { initGSAP } from "./gsap.js";
|
|
11
|
+
import { prefersReducedMotion } from "./util.js";
|
|
12
|
+
import type { CommonOptions, Destroy } from "./types.js";
|
|
13
|
+
|
|
14
|
+
export function guard(options: CommonOptions, run: () => Destroy): Destroy {
|
|
15
|
+
initGSAP();
|
|
16
|
+
if (prefersReducedMotion() && !options.force) return () => {};
|
|
17
|
+
return run();
|
|
18
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lenis smooth scrolling wired into GSAP ScrollTrigger.
|
|
3
|
+
*
|
|
4
|
+
* Uses the canonical Lenis + GSAP recipe:
|
|
5
|
+
* lenis.on("scroll", ScrollTrigger.update)
|
|
6
|
+
* gsap.ticker.add(t => lenis.raf(t * 1000))
|
|
7
|
+
* gsap.ticker.lagSmoothing(0)
|
|
8
|
+
*
|
|
9
|
+
* Driving Lenis from GSAP's ticker (rather than its own rAF) keeps both
|
|
10
|
+
* clocks on the same frame, which is what stops ScrollTrigger from stuttering.
|
|
11
|
+
*
|
|
12
|
+
* `useScrollerProxy: true` additionally proxies `document.documentElement`,
|
|
13
|
+
* matching dzinrstudio.com's setup — only needed if you scroll a nested
|
|
14
|
+
* element instead of the window.
|
|
15
|
+
*/
|
|
16
|
+
import Lenis from "lenis";
|
|
17
|
+
import { gsap, ScrollTrigger, initGSAP } from "./gsap.js";
|
|
18
|
+
import { prefersReducedMotion } from "./util.js";
|
|
19
|
+
import type { CommonOptions, Destroy } from "./types.js";
|
|
20
|
+
|
|
21
|
+
export interface SmoothScrollOptions extends CommonOptions {
|
|
22
|
+
/** Interpolation factor — lower = floatier. @default 0.08 */
|
|
23
|
+
lerp?: number;
|
|
24
|
+
/** Smooth mouse-wheel input. @default true */
|
|
25
|
+
smoothWheel?: boolean;
|
|
26
|
+
/** Smooth touch input (can fight native scrolling). @default false */
|
|
27
|
+
smoothTouch?: boolean;
|
|
28
|
+
/** Direction to smooth. @default "vertical" */
|
|
29
|
+
orientation?: "vertical" | "horizontal";
|
|
30
|
+
/** Initial scroll position in px. @default 0 */
|
|
31
|
+
initialScroll?: number;
|
|
32
|
+
/** Proxy documentElement through ScrollTrigger (nested scrollers). @default false */
|
|
33
|
+
useScrollerProxy?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SmoothScrollHandle {
|
|
37
|
+
/** The Lenis instance, or null when smooth scrolling was skipped. */
|
|
38
|
+
lenis: Lenis | null;
|
|
39
|
+
/** Programmatic scroll that respects the smooth scroller. */
|
|
40
|
+
scrollTo: (target: number | string | HTMLElement, opts?: { duration?: number; immediate?: boolean }) => void;
|
|
41
|
+
/** True when Lenis is actually driving the page. */
|
|
42
|
+
active: boolean;
|
|
43
|
+
destroy: Destroy;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const INERT: SmoothScrollHandle = {
|
|
47
|
+
lenis: null,
|
|
48
|
+
scrollTo: () => {},
|
|
49
|
+
active: false,
|
|
50
|
+
destroy: () => {},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export function smoothScroll(options: SmoothScrollOptions = {}): SmoothScrollHandle {
|
|
54
|
+
initGSAP();
|
|
55
|
+
|
|
56
|
+
const {
|
|
57
|
+
force = false,
|
|
58
|
+
lerp = 0.08,
|
|
59
|
+
smoothWheel = true,
|
|
60
|
+
smoothTouch = false,
|
|
61
|
+
orientation = "vertical",
|
|
62
|
+
initialScroll = 0,
|
|
63
|
+
useScrollerProxy = false,
|
|
64
|
+
} = options;
|
|
65
|
+
|
|
66
|
+
if (typeof window === "undefined") return INERT;
|
|
67
|
+
|
|
68
|
+
// Honour the OS-level preference: fall back to native scrolling.
|
|
69
|
+
if (prefersReducedMotion() && !force) return INERT;
|
|
70
|
+
|
|
71
|
+
const lenis = new Lenis({
|
|
72
|
+
lerp,
|
|
73
|
+
smoothWheel,
|
|
74
|
+
syncTouch: smoothTouch,
|
|
75
|
+
orientation,
|
|
76
|
+
autoRaf: false,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (initialScroll) lenis.scrollTo(initialScroll, { immediate: true });
|
|
80
|
+
|
|
81
|
+
// 1. Let ScrollTrigger know whenever Lenis moves.
|
|
82
|
+
const onScroll = () => ScrollTrigger.update();
|
|
83
|
+
lenis.on("scroll", onScroll);
|
|
84
|
+
|
|
85
|
+
// 2. Run Lenis off GSAP's ticker so both share one frame clock.
|
|
86
|
+
const tick = (time: number) => lenis.raf(time * 1000);
|
|
87
|
+
gsap.ticker.add(tick);
|
|
88
|
+
gsap.ticker.lagSmoothing(0);
|
|
89
|
+
|
|
90
|
+
// 3. Optional: proxy the scroller (for nested/element scrollers).
|
|
91
|
+
if (useScrollerProxy) {
|
|
92
|
+
ScrollTrigger.scrollerProxy(document.documentElement, {
|
|
93
|
+
scrollTop(value?: number) {
|
|
94
|
+
if (arguments.length && typeof value === "number") {
|
|
95
|
+
lenis.scrollTo(value, { immediate: true });
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
return lenis.scroll;
|
|
99
|
+
},
|
|
100
|
+
getBoundingClientRect: () => ({
|
|
101
|
+
top: 0,
|
|
102
|
+
left: 0,
|
|
103
|
+
width: window.innerWidth,
|
|
104
|
+
height: window.innerHeight,
|
|
105
|
+
}),
|
|
106
|
+
// Lenis drives the window, so the documentElement is always visible.
|
|
107
|
+
pinType: "fixed",
|
|
108
|
+
});
|
|
109
|
+
ScrollTrigger.defaults({ scroller: document.documentElement });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const onResize = () => {
|
|
113
|
+
lenis.resize();
|
|
114
|
+
ScrollTrigger.refresh();
|
|
115
|
+
};
|
|
116
|
+
window.addEventListener("resize", onResize);
|
|
117
|
+
|
|
118
|
+
// Fonts and late images change layout; refresh once they land.
|
|
119
|
+
const refresh = () => ScrollTrigger.refresh();
|
|
120
|
+
window.addEventListener("load", refresh);
|
|
121
|
+
|
|
122
|
+
lenis.resize();
|
|
123
|
+
ScrollTrigger.refresh();
|
|
124
|
+
|
|
125
|
+
const destroy: Destroy = () => {
|
|
126
|
+
window.removeEventListener("resize", onResize);
|
|
127
|
+
window.removeEventListener("load", refresh);
|
|
128
|
+
gsap.ticker.remove(tick);
|
|
129
|
+
lenis.off("scroll", onScroll);
|
|
130
|
+
if (useScrollerProxy) {
|
|
131
|
+
ScrollTrigger.scrollerProxy(document.documentElement, {} as never);
|
|
132
|
+
ScrollTrigger.defaults({ scroller: window });
|
|
133
|
+
}
|
|
134
|
+
lenis.destroy();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
lenis,
|
|
139
|
+
active: true,
|
|
140
|
+
scrollTo: (target, opts) => lenis.scrollTo(target as never, opts),
|
|
141
|
+
destroy,
|
|
142
|
+
};
|
|
143
|
+
}
|