solid-drift 0.2.0 → 0.8.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.
Files changed (54) hide show
  1. package/README.md +933 -10
  2. package/dist/ai.d.ts +207 -0
  3. package/dist/ai.js +570 -0
  4. package/dist/animate.d.ts +2 -2
  5. package/dist/animate.js +2 -2
  6. package/dist/cartoon.d.ts +190 -0
  7. package/dist/cartoon.js +334 -0
  8. package/dist/color.d.ts +53 -0
  9. package/dist/color.js +391 -0
  10. package/dist/directive.d.ts +5 -5
  11. package/dist/directive.js +15 -7
  12. package/dist/easing.d.ts +22 -1
  13. package/dist/easing.js +49 -1
  14. package/dist/flip.d.ts +44 -0
  15. package/dist/flip.js +108 -0
  16. package/dist/horizontal.d.ts +107 -0
  17. package/dist/horizontal.js +208 -0
  18. package/dist/index.d.ts +20 -3
  19. package/dist/index.js +17 -3
  20. package/dist/inview.d.ts +4 -4
  21. package/dist/inview.js +5 -5
  22. package/dist/motion.d.ts +404 -0
  23. package/dist/motion.js +761 -0
  24. package/dist/physics.d.ts +146 -0
  25. package/dist/physics.js +352 -0
  26. package/dist/pointer.d.ts +76 -0
  27. package/dist/pointer.js +123 -0
  28. package/dist/reduced-motion.d.ts +3 -3
  29. package/dist/reduced-motion.js +5 -4
  30. package/dist/scroll.d.ts +3 -3
  31. package/dist/scroll.js +5 -5
  32. package/dist/scrollfx.d.ts +156 -0
  33. package/dist/scrollfx.js +148 -0
  34. package/dist/scrub.d.ts +51 -0
  35. package/dist/scrub.js +67 -0
  36. package/dist/spring.d.ts +39 -4
  37. package/dist/spring.js +26 -7
  38. package/dist/stagger.d.ts +4 -4
  39. package/dist/stagger.js +4 -4
  40. package/dist/text.d.ts +27 -0
  41. package/dist/text.js +84 -0
  42. package/dist/timeline.d.ts +45 -0
  43. package/dist/timeline.js +93 -0
  44. package/dist/trail.d.ts +27 -0
  45. package/dist/trail.js +75 -0
  46. package/dist/tween.d.ts +3 -3
  47. package/dist/tween.js +3 -3
  48. package/dist/typography.d.ts +241 -0
  49. package/dist/typography.js +812 -0
  50. package/dist/velocity.d.ts +44 -0
  51. package/dist/velocity.js +88 -0
  52. package/dist/web3.d.ts +213 -0
  53. package/dist/web3.js +640 -0
  54. package/package.json +1 -1
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Motion-graphics family: showreel and launch-film primitives.
3
+ *
4
+ * These are the building blocks of code-driven motion design: kinetic
5
+ * typography, scene orchestration, camera moves, time-based color
6
+ * shifts, match-cut transitions, and a beat clock for cutting on the
7
+ * music. Each primitive is small and composable; a showreel is a
8
+ * `createScenePlayer` driving a few of these together, not one mega-API.
9
+ *
10
+ * All primitives are signal-native, SSR-safe, dependency-free, and
11
+ * define sensible static behavior under `prefers-reduced-motion`.
12
+ */
13
+ import { type Accessor } from "solid-js";
14
+ import { type ColorFormat, type ColorStop } from "./color.js";
15
+ import { type Easing, type EasingName } from "./easing.js";
16
+ type MaybeElement = () => Element | null | undefined;
17
+ export interface KineticTypeFrom {
18
+ /** Vertical offset in px where each unit starts. Default 28. */
19
+ y?: number;
20
+ /** Blur in px where each unit starts. Default 10. */
21
+ blur?: number;
22
+ /** Scale where each unit starts. Default 0.85. */
23
+ scale?: number;
24
+ /** Opacity where each unit starts. Default 0. */
25
+ opacity?: number;
26
+ /** Rotation in degrees where each unit starts. Default 0. */
27
+ rotate?: number;
28
+ /**
29
+ * Per-unit jitter around the `from` values, 0 to 1. Default 0.
30
+ * At 0 every unit shares the exact `from` state; above 0 each unit
31
+ * gets a seeded random offset so the entrance feels hand-set
32
+ * instead of mechanical.
33
+ */
34
+ variance?: number;
35
+ /**
36
+ * Seed for the per-unit jitter. Same seed renders the same jitter
37
+ * on every run. Default 0.
38
+ */
39
+ seed?: number;
40
+ }
41
+ export interface KineticTypeOptions {
42
+ /** Split into "chars" or "words". Default "chars". */
43
+ unit?: "chars" | "words";
44
+ /** Milliseconds each unit takes to arrive. Default 550. */
45
+ duration?: number;
46
+ /** Milliseconds between unit starts. Default 45. */
47
+ stagger?: number;
48
+ /** Starting transform/opacity/blur for each unit. */
49
+ from?: KineticTypeFrom;
50
+ /** Easing for each unit's arrival. Default "easeOutExpo". */
51
+ easing?: Easing | EasingName;
52
+ }
53
+ export type KineticTypeStatus = "idle" | "running" | "done";
54
+ export interface KineticTypeControls {
55
+ /** Play the reveal. Resolves when the last unit arrives. */
56
+ play: () => Promise<void>;
57
+ /** Stop mid-reveal. The play() promise resolves. */
58
+ stop: () => void;
59
+ /** Stop and play again from the first unit. */
60
+ replay: () => Promise<void>;
61
+ /** Reactive status: "idle" | "running" | "done". */
62
+ status: Accessor<KineticTypeStatus>;
63
+ }
64
+ /**
65
+ * Kinetic typography: each character (or word) flies in with position,
66
+ * blur, scale, and opacity, staggered for that showreel title feel.
67
+ *
68
+ * One master clock drives every unit, so a headline with 40 characters
69
+ * costs a single rAF task, not 40 timers. Units animate through the
70
+ * same `from` state with per-unit easing; set `from.variance` above 0
71
+ * for seeded per-unit jitter around those values.
72
+ *
73
+ * SSR-safe: no-op on the server. Under reduced motion every unit jumps
74
+ * to its final state when `play()` runs, so the text is fully readable.
75
+ *
76
+ * ```tsx
77
+ * let title!: HTMLHeadingElement
78
+ * const kinetic = createKineticType(() => title, {
79
+ * unit: "chars",
80
+ * stagger: 35,
81
+ * from: { y: 40, blur: 12, scale: 0.8 },
82
+ * easing: "easeOutExpo",
83
+ * })
84
+ * onMount(() => kinetic.play())
85
+ * <h1 ref={title}>Showreel</h1>
86
+ * ```
87
+ */
88
+ export declare function createKineticType(ref: MaybeElement, options?: KineticTypeOptions): KineticTypeControls;
89
+ /** One scene in a showreel: a duration plus enter/exit hooks. */
90
+ export interface MotionScene {
91
+ /** How long the scene stays active, in milliseconds. */
92
+ duration: number;
93
+ /** Called when the scene becomes active. */
94
+ onEnter?: (index: number) => void;
95
+ /** Called when the scene ends. */
96
+ onExit?: (index: number) => void;
97
+ }
98
+ export type ScenePlayerStatus = "idle" | "running" | "paused" | "done";
99
+ export interface ScenePlayerControls {
100
+ /** Current scene index, or -1 before the first play(). */
101
+ scene: Accessor<number>;
102
+ /** Reactive status: "idle" | "running" | "paused" | "done". */
103
+ status: Accessor<ScenePlayerStatus>;
104
+ /** Play from the current scene. Resolves after the last scene. */
105
+ play: () => Promise<void>;
106
+ /** Freeze the clock; play() resumes where it left off. */
107
+ pause: () => void;
108
+ /** Halt and reset to before the first scene. */
109
+ stop: () => void;
110
+ /** Stop and play again from the first scene. */
111
+ replay: () => Promise<void>;
112
+ /** Jump to the next scene now. */
113
+ next: () => void;
114
+ /** Jump to the previous scene now. */
115
+ prev: () => void;
116
+ /** Jump to a scene now. */
117
+ goTo: (index: number) => void;
118
+ }
119
+ /**
120
+ * Scene orchestrator for showreels and launch films: an ordered list of
121
+ * scenes, each with a duration and enter/exit hooks. Think of it as the
122
+ * paused-master-timeline pattern from motion-design tools, expressed as
123
+ * signals: `scene()` tells your view which scene is live, and the hooks
124
+ * trigger each scene's choreography (a `createKineticType`, a camera
125
+ * move, a color shift).
126
+ *
127
+ * SSR-safe: scenes never advance on the server. Under reduced motion
128
+ * `play()` jumps straight to the last scene (the clean final frame)
129
+ * instead of stepping through.
130
+ *
131
+ * ```ts
132
+ * const player = createScenePlayer([
133
+ * { duration: 1200, onEnter: () => hookTitle.play() },
134
+ * { duration: 2000, onEnter: () => cameraZoom.play() },
135
+ * { duration: 1500, onEnter: () => showLogo() }, // final frame
136
+ * ])
137
+ * player.scene() // 0, 1, 2 as the reel plays
138
+ * await player.play()
139
+ * ```
140
+ */
141
+ export declare function createScenePlayer(scenes: MotionScene[]): ScenePlayerControls;
142
+ /** One camera keyframe: pan (x/y in px) and zoom (scale) at a progress. */
143
+ export interface CameraKeyframe {
144
+ /** Progress position, 0 to 1. */
145
+ at: number;
146
+ /** Horizontal pan in px. Default 0. */
147
+ x?: number;
148
+ /** Vertical pan in px. Default 0. */
149
+ y?: number;
150
+ /** Zoom factor. Default 1. */
151
+ scale?: number;
152
+ /**
153
+ * Easing for the segment that ends at this keyframe, following the
154
+ * CSS keyframe convention. Default "linear".
155
+ */
156
+ easing?: Easing | EasingName;
157
+ }
158
+ export interface CameraOptions {
159
+ /** 0-to-1 progress signal driving the camera. */
160
+ progress: Accessor<number>;
161
+ /** Fallback easing for segments without their own. Default "linear". */
162
+ easing?: Easing | EasingName;
163
+ }
164
+ /**
165
+ * Camera moves for a motion-design stage: pan and zoom driven by a
166
+ * 0-to-1 progress signal, returned as a compositor-friendly transform
167
+ * string (`translate3d(...) scale(...)`).
168
+ *
169
+ * Drive `progress` with anything: a `createScenePlayer` scene's own
170
+ * clock, an `animate()` tween, or scroll. Keyframes sort themselves by
171
+ * `at`; progress outside the range clamps to the end poses.
172
+ *
173
+ * Pure computation, no listeners, no rAF: SSR-safe by construction.
174
+ * Under reduced motion it holds the final keyframe's pose.
175
+ *
176
+ * ```tsx
177
+ * const [p, setP] = createSignal(0)
178
+ * // Slow dolly-in across the scene.
179
+ * const cam = createCamera({
180
+ * progress: p,
181
+ * keyframes... // via options below
182
+ * })
183
+ * ```
184
+ */
185
+ export declare function createCamera(keyframes: CameraKeyframe[], options: CameraOptions): Accessor<string>;
186
+ export interface ColorShiftOptions {
187
+ /** Milliseconds to travel across all stops. Default 1200. */
188
+ duration?: number;
189
+ /** Delay before starting, in milliseconds. Default 0. */
190
+ delay?: number;
191
+ /** Fallback easing for stop segments without their own. Default "linear". */
192
+ easing?: Easing | EasingName;
193
+ /** Output format. Default "hex". */
194
+ format?: ColorFormat;
195
+ }
196
+ export type ColorShiftStatus = "idle" | "running" | "done";
197
+ export interface ColorShiftControls {
198
+ /** The interpolated color as a string signal. */
199
+ color: Accessor<string>;
200
+ /** Play the shift. Resolves at the final stop. */
201
+ play: () => Promise<void>;
202
+ /** Stop mid-shift. The play() promise resolves. */
203
+ stop: () => void;
204
+ /** Stop and play again from the first stop. */
205
+ replay: () => Promise<void>;
206
+ /** Reactive status: "idle" | "running" | "done". */
207
+ status: Accessor<ColorShiftStatus>;
208
+ }
209
+ /**
210
+ * Time-based color interpolation across stops: the sibling of
211
+ * `createScrollColor` for motion graphics, where color shifts run on a
212
+ * clock instead of scroll. Colors interpolate in linear light, so
213
+ * midpoints stay vivid, and alpha channels interpolate too.
214
+ *
215
+ * Under reduced motion `play()` jumps straight to the final stop's
216
+ * color. SSR-safe: `color()` returns the final stop's color.
217
+ *
218
+ * ```ts
219
+ * const shift = createColorShift(
220
+ * [
221
+ * { at: 0, color: "#0a1220" },
222
+ * { at: 0.5, color: "#2f8fdd" },
223
+ * { at: 1, color: "#d9a441", easing: "easeInOutQuad" },
224
+ * ],
225
+ * { duration: 2000 },
226
+ * )
227
+ * shift.color() // "#0a1220" ... "#d9a441" as it plays
228
+ * await shift.play()
229
+ * ```
230
+ */
231
+ export declare function createColorShift(stops: ColorStop[], options?: ColorShiftOptions): ColorShiftControls;
232
+ /** How one scene hands off to the next. */
233
+ export type TransitionType = "cut" | "fade" | "slide" | "wipe";
234
+ /** Direction for "slide" and "wipe". */
235
+ export type TransitionDirection = "left" | "right" | "up" | "down";
236
+ export interface TransitionOptions {
237
+ /** Transition style. Default "fade". */
238
+ type?: TransitionType;
239
+ /** Direction for "slide" and "wipe". Default "left". */
240
+ direction?: TransitionDirection;
241
+ /** Duration in milliseconds. Default 500. */
242
+ duration?: number;
243
+ /** Easing. Default "easeInOutCubic". */
244
+ easing?: Easing | EasingName;
245
+ }
246
+ /** Compositor-friendly style for one side of a transition. */
247
+ export interface TransitionLayerStyle {
248
+ opacity: string;
249
+ transform: string;
250
+ clipPath: string;
251
+ }
252
+ export type TransitionStatus = "idle" | "running" | "done";
253
+ export interface TransitionControls {
254
+ /** 0-to-1 progress of the handoff. */
255
+ progress: Accessor<number>;
256
+ /** Style for the outgoing scene's layer. */
257
+ outgoing: Accessor<TransitionLayerStyle>;
258
+ /** Style for the incoming scene's layer. */
259
+ incoming: Accessor<TransitionLayerStyle>;
260
+ /** Play the handoff. Resolves when the incoming scene owns the frame. */
261
+ play: () => Promise<void>;
262
+ /** Stop mid-handoff. The play() promise resolves. */
263
+ stop: () => void;
264
+ /** Stop and play the handoff again. */
265
+ replay: () => Promise<void>;
266
+ /** Reactive status: "idle" | "running" | "done". */
267
+ status: Accessor<TransitionStatus>;
268
+ }
269
+ /**
270
+ * Match-cut style scene handoffs: `outgoing()` and `incoming()` return
271
+ * style objects for the two scene layers, driven by one 0-to-1 progress.
272
+ *
273
+ * - "cut": instant swap, no animation.
274
+ * - "fade": crossfade.
275
+ * - "slide": the outgoing scene exits one way while the incoming scene
276
+ * enters from the opposite side.
277
+ * - "wipe": the incoming scene reveals over the outgoing one with a
278
+ * clip-path wipe.
279
+ *
280
+ * Everything animates on opacity, transform, or clip-path, so handoffs
281
+ * stay on the compositor. Under reduced motion every type degrades to a
282
+ * cut: the swap happens instantly.
283
+ *
284
+ * ```tsx
285
+ * const cut = createTransition({ type: "wipe", direction: "left", duration: 600 })
286
+ * const go = async () => {
287
+ * showSceneB()
288
+ * await cut.play()
289
+ * }
290
+ * <div style={cut.outgoing()}>{sceneA}</div>
291
+ * <div style={cut.incoming()}>{sceneB}</div>
292
+ * ```
293
+ */
294
+ export declare function createTransition(options?: TransitionOptions): TransitionControls;
295
+ export interface BeatOptions {
296
+ /** Beats per minute. Default 120. */
297
+ bpm?: number;
298
+ /** Beats per bar. Default 4. */
299
+ beatsPerBar?: number;
300
+ }
301
+ export type BeatStatus = "idle" | "running";
302
+ export interface BeatControls {
303
+ /** Whole beats elapsed since start(). */
304
+ beat: Accessor<number>;
305
+ /** Whole bars elapsed since start(). */
306
+ bar: Accessor<number>;
307
+ /** Fractional position within the current beat, 0 to 1. */
308
+ phase: Accessor<number>;
309
+ /** Beats per bar, from the options. Used as the default cut interval. */
310
+ beatsPerBar: number;
311
+ /**
312
+ * Register a callback fired on every beat with the beat index.
313
+ * Returns an unsubscribe function.
314
+ */
315
+ onBeat: (cb: (beat: number) => void) => () => void;
316
+ /** Start the clock. No-op if already running. */
317
+ start: () => void;
318
+ /** Stop the clock. */
319
+ stop: () => void;
320
+ /** Reactive status: "idle" | "running". */
321
+ status: Accessor<BeatStatus>;
322
+ }
323
+ /**
324
+ * A beat clock for cutting on the music: at 120 BPM it ticks twice a
325
+ * second, and `onBeat` fires your scene cuts, kinetic type replays, or
326
+ * color shifts in time. `phase()` gives the fractional position inside
327
+ * the current beat for syncing continuous motion to the rhythm.
328
+ *
329
+ * Beats are timing, not motion, so the clock keeps ticking under
330
+ * reduced motion (your callbacks decide what that means visually).
331
+ * SSR-safe: `start()` is a no-op without requestAnimationFrame.
332
+ *
333
+ * ```ts
334
+ * const beat = createBeat({ bpm: 128 })
335
+ * const off = beat.onBeat((b) => {
336
+ * if (b % 8 === 0) player.next() // cut scenes every 2 bars
337
+ * })
338
+ * beat.start()
339
+ * ```
340
+ */
341
+ export declare function createBeat(options?: BeatOptions): BeatControls;
342
+ /** Named role of a showreel scene, for readability. */
343
+ export type ShowreelSceneKind = "title" | "camera" | "color" | "cut" | "custom";
344
+ /**
345
+ * One scene in a guided showreel: a `MotionScene` with an optional
346
+ * named kind describing what the scene does.
347
+ */
348
+ export interface ShowreelScene extends MotionScene {
349
+ /**
350
+ * Named kind for readability: "title" for kinetic-type title cards,
351
+ * "camera" for camera-move scenes, "color" for color-shift scenes,
352
+ * "cut" for transition handoffs, "custom" for anything else.
353
+ * Informational only; it does not change playback.
354
+ */
355
+ kind?: ShowreelSceneKind;
356
+ }
357
+ /**
358
+ * Guided showreel recipe: a thin typed wrapper over
359
+ * `createScenePlayer` for showreels and launch films. Scenes carry a
360
+ * named `kind` so the reel reads like a shot list, and each scene's
361
+ * `onEnter` wires one of the motion-graphics primitives
362
+ * (`createKineticType`, `createCamera`, `createColorShift`,
363
+ * `createTransition`, `createBeat`).
364
+ *
365
+ * Same controls, status values, and reduced-motion behavior as
366
+ * `createScenePlayer`: `play()` jumps to the final frame under reduced
367
+ * motion or on the server.
368
+ *
369
+ * ```ts
370
+ * const reel = createShowreel([
371
+ * { kind: "title", duration: 1200, onEnter: () => titleCard.play() },
372
+ * { kind: "camera", duration: 2000, onEnter: () => dolly.play() },
373
+ * { kind: "color", duration: 1500, onEnter: () => finale.play() },
374
+ * ])
375
+ * beatCuts = createBeatCuts(beat, reel, { every: 8 })
376
+ * await reel.play()
377
+ * ```
378
+ */
379
+ export declare function createShowreel(scenes: ShowreelScene[]): ScenePlayerControls;
380
+ export interface BeatCutOptions {
381
+ /**
382
+ * Cut every N beats. Default: the beat clock's `beatsPerBar`, so a
383
+ * cut lands on every downbeat.
384
+ */
385
+ every?: number;
386
+ }
387
+ /**
388
+ * Beat-synced scene cuts: advance the player every N beats through
389
+ * the beat clock's `onBeat`. Returns a cleanup function that
390
+ * unsubscribes the cut listener.
391
+ *
392
+ * Cuts only fire while the player is running, so pausing the reel
393
+ * pauses the cuts too.
394
+ *
395
+ * ```ts
396
+ * const beat = createBeat({ bpm: 128, beatsPerBar: 4 })
397
+ * const stopCuts = createBeatCuts(beat, player) // cut every bar
398
+ * beat.start()
399
+ * await player.play()
400
+ * stopCuts()
401
+ * ```
402
+ */
403
+ export declare function createBeatCuts(beat: BeatControls, player: Pick<ScenePlayerControls, "next" | "status">, options?: BeatCutOptions): () => void;
404
+ export {};