@framesquared/fx 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 John Carbone
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.
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @framesquared/fx – Animation
3
+ *
4
+ * Wraps the Web Animations API (Element.animate()). Provides a
5
+ * config-driven interface with play/pause/cancel/finish/reverse
6
+ * controls and a finished promise.
7
+ */
8
+ interface AnimationConfig {
9
+ target: Element;
10
+ keyframes: Keyframe[];
11
+ duration?: number;
12
+ easing?: string;
13
+ delay?: number;
14
+ iterations?: number;
15
+ direction?: PlaybackDirection;
16
+ fill?: FillMode;
17
+ }
18
+ declare class Animation {
19
+ private target;
20
+ private keyframes;
21
+ private options;
22
+ private waAnimation;
23
+ /** Resolves when the animation completes. */
24
+ finished: Promise<void>;
25
+ private _resolveFinished;
26
+ constructor(config: AnimationConfig);
27
+ play(): this;
28
+ pause(): this;
29
+ cancel(): this;
30
+ finish(): this;
31
+ reverse(): this;
32
+ getPlayState(): string;
33
+ get currentTime(): number | null;
34
+ get playbackRate(): number;
35
+ set playbackRate(rate: number);
36
+ /** Get the underlying WAAPI Animation object. */
37
+ getNative(): globalThis.Animation | null;
38
+ }
39
+
40
+ /**
41
+ * @framesquared/fx – Anim
42
+ *
43
+ * Convenience factory for predefined animations. Each method
44
+ * returns an Animation instance. Also provides queue() for
45
+ * sequential and parallel() for simultaneous execution.
46
+ */
47
+
48
+ interface AnimOptions {
49
+ duration?: number;
50
+ easing?: string;
51
+ delay?: number;
52
+ fill?: FillMode;
53
+ }
54
+ interface SlideOptions extends AnimOptions {
55
+ }
56
+ interface HighlightOptions extends AnimOptions {
57
+ color?: string;
58
+ }
59
+ interface ScaleOptions extends AnimOptions {
60
+ from?: number;
61
+ to?: number;
62
+ }
63
+ interface RotateOptions extends AnimOptions {
64
+ degrees?: number;
65
+ }
66
+ declare const Anim: {
67
+ fadeIn(el: Element, opts?: AnimOptions): Animation;
68
+ fadeOut(el: Element, opts?: AnimOptions): Animation;
69
+ slideIn(el: Element, direction?: "left" | "right" | "top" | "bottom", opts?: SlideOptions): Animation;
70
+ slideOut(el: Element, direction?: "left" | "right" | "top" | "bottom", opts?: SlideOptions): Animation;
71
+ highlight(el: Element, opts?: HighlightOptions): Animation;
72
+ scale(el: Element, opts?: ScaleOptions): Animation;
73
+ rotate(el: Element, opts?: RotateOptions): Animation;
74
+ shake(el: Element, opts?: AnimOptions): Animation;
75
+ pulse(el: Element, opts?: AnimOptions): Animation;
76
+ flip(el: Element, opts?: AnimOptions): Animation;
77
+ queue(animations: Animation[]): Promise<void>;
78
+ parallel(animations: Animation[]): Promise<void>;
79
+ };
80
+
81
+ /**
82
+ * @framesquared/fx – Easing
83
+ *
84
+ * Named CSS easing strings. Standard names map to CSS keywords;
85
+ * extended names map to cubic-bezier() values. Bounce easings
86
+ * use linear() approximation since cubic-bezier can't represent them.
87
+ */
88
+ declare const Easing: {
89
+ readonly linear: "linear";
90
+ readonly ease: "ease";
91
+ readonly easeIn: "ease-in";
92
+ readonly easeOut: "ease-out";
93
+ readonly easeInOut: "ease-in-out";
94
+ readonly easeInQuad: "cubic-bezier(0.55, 0.085, 0.68, 0.53)";
95
+ readonly easeOutQuad: "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
96
+ readonly easeInOutQuad: "cubic-bezier(0.455, 0.03, 0.515, 0.955)";
97
+ readonly easeInCubic: "cubic-bezier(0.55, 0.055, 0.675, 0.19)";
98
+ readonly easeOutCubic: "cubic-bezier(0.215, 0.61, 0.355, 1)";
99
+ readonly easeInOutCubic: "cubic-bezier(0.645, 0.045, 0.355, 1)";
100
+ readonly easeInBack: "cubic-bezier(0.6, -0.28, 0.735, 0.045)";
101
+ readonly easeOutBack: "cubic-bezier(0.175, 0.885, 0.32, 1.275)";
102
+ readonly easeInOutBack: "cubic-bezier(0.68, -0.55, 0.265, 1.55)";
103
+ readonly easeInBounce: "cubic-bezier(0.6, 0.04, 0.98, 0.335)";
104
+ readonly easeOutBounce: "cubic-bezier(0.015, 0.66, 0.39, 0.995)";
105
+ readonly easeInOutBounce: "cubic-bezier(0.83, 0.04, 0.17, 1)";
106
+ };
107
+ type EasingName = keyof typeof Easing;
108
+
109
+ /**
110
+ * @framesquared/fx – Transition
111
+ *
112
+ * CSS transition management: set/clear transitions on elements,
113
+ * and register transitionend callbacks with cleanup.
114
+ */
115
+ declare const Transition: {
116
+ /**
117
+ * Set a CSS transition on an element.
118
+ * @param el Target element
119
+ * @param property CSS property name(s), comma-separated
120
+ * @param duration Duration in ms
121
+ * @param easing CSS easing string
122
+ */
123
+ set(el: HTMLElement, property: string, duration: number, easing?: string): void;
124
+ /** Remove all transitions from an element. */
125
+ clear(el: HTMLElement): void;
126
+ /**
127
+ * Register a callback for transitionend. Returns a cleanup
128
+ * function that removes the listener.
129
+ */
130
+ onEnd(el: HTMLElement, callback: (e: Event) => void): () => void;
131
+ };
132
+
133
+ export { Anim, type AnimOptions, Animation, type AnimationConfig, Easing, type EasingName, Transition };
package/dist/index.js ADDED
@@ -0,0 +1,305 @@
1
+ // src/Animation.ts
2
+ var Animation = class {
3
+ target;
4
+ keyframes;
5
+ options;
6
+ waAnimation = null;
7
+ /** Resolves when the animation completes. */
8
+ finished;
9
+ _resolveFinished;
10
+ constructor(config) {
11
+ this.target = config.target;
12
+ this.keyframes = config.keyframes;
13
+ this.options = {
14
+ duration: config.duration ?? 300,
15
+ easing: config.easing ?? "ease",
16
+ delay: config.delay ?? 0,
17
+ iterations: config.iterations ?? 1,
18
+ direction: config.direction ?? "normal",
19
+ fill: config.fill ?? "none"
20
+ };
21
+ this.finished = new Promise((resolve) => {
22
+ this._resolveFinished = resolve;
23
+ });
24
+ }
25
+ play() {
26
+ this.waAnimation = this.target.animate(this.keyframes, this.options);
27
+ this.waAnimation.finished.then(() => this._resolveFinished()).catch(() => {
28
+ });
29
+ return this;
30
+ }
31
+ pause() {
32
+ this.waAnimation?.pause();
33
+ return this;
34
+ }
35
+ cancel() {
36
+ this.waAnimation?.cancel();
37
+ return this;
38
+ }
39
+ finish() {
40
+ this.waAnimation?.finish();
41
+ return this;
42
+ }
43
+ reverse() {
44
+ this.waAnimation?.reverse();
45
+ return this;
46
+ }
47
+ getPlayState() {
48
+ return this.waAnimation?.playState ?? "idle";
49
+ }
50
+ get currentTime() {
51
+ return this.waAnimation?.currentTime ?? null;
52
+ }
53
+ get playbackRate() {
54
+ return this.waAnimation?.playbackRate ?? 1;
55
+ }
56
+ set playbackRate(rate) {
57
+ if (this.waAnimation) this.waAnimation.playbackRate = rate;
58
+ }
59
+ /** Get the underlying WAAPI Animation object. */
60
+ getNative() {
61
+ return this.waAnimation;
62
+ }
63
+ };
64
+
65
+ // src/Anim.ts
66
+ var Anim = {
67
+ // -----------------------------------------------------------------------
68
+ // Fade
69
+ // -----------------------------------------------------------------------
70
+ fadeIn(el, opts = {}) {
71
+ return new Animation({
72
+ target: el,
73
+ keyframes: [{ opacity: "0" }, { opacity: "1" }],
74
+ duration: opts.duration ?? 300,
75
+ easing: opts.easing ?? "ease",
76
+ delay: opts.delay ?? 0,
77
+ fill: opts.fill ?? "both"
78
+ });
79
+ },
80
+ fadeOut(el, opts = {}) {
81
+ return new Animation({
82
+ target: el,
83
+ keyframes: [{ opacity: "1" }, { opacity: "0" }],
84
+ duration: opts.duration ?? 300,
85
+ easing: opts.easing ?? "ease",
86
+ delay: opts.delay ?? 0,
87
+ fill: opts.fill ?? "both"
88
+ });
89
+ },
90
+ // -----------------------------------------------------------------------
91
+ // Slide
92
+ // -----------------------------------------------------------------------
93
+ slideIn(el, direction = "left", opts = {}) {
94
+ const transforms = {
95
+ left: "translateX(-100%)",
96
+ right: "translateX(100%)",
97
+ top: "translateY(-100%)",
98
+ bottom: "translateY(100%)"
99
+ };
100
+ return new Animation({
101
+ target: el,
102
+ keyframes: [
103
+ { transform: transforms[direction], opacity: "0" },
104
+ { transform: "translateX(0) translateY(0)", opacity: "1" }
105
+ ],
106
+ duration: opts.duration ?? 400,
107
+ easing: opts.easing ?? "ease-out",
108
+ fill: opts.fill ?? "both"
109
+ });
110
+ },
111
+ slideOut(el, direction = "right", opts = {}) {
112
+ const transforms = {
113
+ left: "translateX(-100%)",
114
+ right: "translateX(100%)",
115
+ top: "translateY(-100%)",
116
+ bottom: "translateY(100%)"
117
+ };
118
+ return new Animation({
119
+ target: el,
120
+ keyframes: [
121
+ { transform: "translateX(0) translateY(0)", opacity: "1" },
122
+ { transform: transforms[direction], opacity: "0" }
123
+ ],
124
+ duration: opts.duration ?? 400,
125
+ easing: opts.easing ?? "ease-in",
126
+ fill: opts.fill ?? "both"
127
+ });
128
+ },
129
+ // -----------------------------------------------------------------------
130
+ // Highlight
131
+ // -----------------------------------------------------------------------
132
+ highlight(el, opts = {}) {
133
+ const color = opts.color ?? "yellow";
134
+ return new Animation({
135
+ target: el,
136
+ keyframes: [
137
+ { backgroundColor: color },
138
+ { backgroundColor: "transparent" }
139
+ ],
140
+ duration: opts.duration ?? 1e3,
141
+ easing: opts.easing ?? "ease-in-out",
142
+ fill: opts.fill ?? "both"
143
+ });
144
+ },
145
+ // -----------------------------------------------------------------------
146
+ // Scale
147
+ // -----------------------------------------------------------------------
148
+ scale(el, opts = {}) {
149
+ const from = opts.from ?? 1;
150
+ const to = opts.to ?? 1.5;
151
+ return new Animation({
152
+ target: el,
153
+ keyframes: [
154
+ { transform: `scale(${from})` },
155
+ { transform: `scale(${to})` }
156
+ ],
157
+ duration: opts.duration ?? 300,
158
+ easing: opts.easing ?? "ease",
159
+ fill: opts.fill ?? "both"
160
+ });
161
+ },
162
+ // -----------------------------------------------------------------------
163
+ // Rotate
164
+ // -----------------------------------------------------------------------
165
+ rotate(el, opts = {}) {
166
+ const degrees = opts.degrees ?? 360;
167
+ return new Animation({
168
+ target: el,
169
+ keyframes: [
170
+ { transform: "rotate(0deg)" },
171
+ { transform: `rotate(${degrees}deg)` }
172
+ ],
173
+ duration: opts.duration ?? 500,
174
+ easing: opts.easing ?? "ease",
175
+ fill: opts.fill ?? "both"
176
+ });
177
+ },
178
+ // -----------------------------------------------------------------------
179
+ // Shake
180
+ // -----------------------------------------------------------------------
181
+ shake(el, opts = {}) {
182
+ return new Animation({
183
+ target: el,
184
+ keyframes: [
185
+ { transform: "translateX(0)" },
186
+ { transform: "translateX(-10px)" },
187
+ { transform: "translateX(10px)" },
188
+ { transform: "translateX(-6px)" },
189
+ { transform: "translateX(6px)" },
190
+ { transform: "translateX(-2px)" },
191
+ { transform: "translateX(0)" }
192
+ ],
193
+ duration: opts.duration ?? 500,
194
+ easing: opts.easing ?? "ease-in-out"
195
+ });
196
+ },
197
+ // -----------------------------------------------------------------------
198
+ // Pulse
199
+ // -----------------------------------------------------------------------
200
+ pulse(el, opts = {}) {
201
+ return new Animation({
202
+ target: el,
203
+ keyframes: [
204
+ { transform: "scale(1)" },
205
+ { transform: "scale(1.1)" },
206
+ { transform: "scale(1)" }
207
+ ],
208
+ duration: opts.duration ?? 600,
209
+ easing: opts.easing ?? "ease-in-out"
210
+ });
211
+ },
212
+ // -----------------------------------------------------------------------
213
+ // Flip
214
+ // -----------------------------------------------------------------------
215
+ flip(el, opts = {}) {
216
+ return new Animation({
217
+ target: el,
218
+ keyframes: [
219
+ { transform: "rotateY(0deg)" },
220
+ { transform: "rotateY(180deg)" }
221
+ ],
222
+ duration: opts.duration ?? 600,
223
+ easing: opts.easing ?? "ease-in-out",
224
+ fill: opts.fill ?? "both"
225
+ });
226
+ },
227
+ // -----------------------------------------------------------------------
228
+ // Queue — run animations sequentially
229
+ // -----------------------------------------------------------------------
230
+ async queue(animations) {
231
+ for (const anim of animations) {
232
+ anim.play();
233
+ await anim.finished;
234
+ }
235
+ },
236
+ // -----------------------------------------------------------------------
237
+ // Parallel — run animations simultaneously
238
+ // -----------------------------------------------------------------------
239
+ async parallel(animations) {
240
+ for (const anim of animations) {
241
+ anim.play();
242
+ }
243
+ await Promise.all(animations.map((a) => a.finished));
244
+ }
245
+ };
246
+
247
+ // src/Easing.ts
248
+ var Easing = {
249
+ // CSS keywords
250
+ linear: "linear",
251
+ ease: "ease",
252
+ easeIn: "ease-in",
253
+ easeOut: "ease-out",
254
+ easeInOut: "ease-in-out",
255
+ // Quadratic
256
+ easeInQuad: "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
257
+ easeOutQuad: "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
258
+ easeInOutQuad: "cubic-bezier(0.455, 0.03, 0.515, 0.955)",
259
+ // Cubic
260
+ easeInCubic: "cubic-bezier(0.55, 0.055, 0.675, 0.19)",
261
+ easeOutCubic: "cubic-bezier(0.215, 0.61, 0.355, 1)",
262
+ easeInOutCubic: "cubic-bezier(0.645, 0.045, 0.355, 1)",
263
+ // Back (overshoot)
264
+ easeInBack: "cubic-bezier(0.6, -0.28, 0.735, 0.045)",
265
+ easeOutBack: "cubic-bezier(0.175, 0.885, 0.32, 1.275)",
266
+ easeInOutBack: "cubic-bezier(0.68, -0.55, 0.265, 1.55)",
267
+ // Bounce (approximated — true bounce can't be expressed as cubic-bezier)
268
+ easeInBounce: "cubic-bezier(0.6, 0.04, 0.98, 0.335)",
269
+ easeOutBounce: "cubic-bezier(0.015, 0.66, 0.39, 0.995)",
270
+ easeInOutBounce: "cubic-bezier(0.83, 0.04, 0.17, 1)"
271
+ };
272
+
273
+ // src/Transition.ts
274
+ var Transition = {
275
+ /**
276
+ * Set a CSS transition on an element.
277
+ * @param el Target element
278
+ * @param property CSS property name(s), comma-separated
279
+ * @param duration Duration in ms
280
+ * @param easing CSS easing string
281
+ */
282
+ set(el, property, duration, easing = "ease") {
283
+ const props = property.split(",").map((p) => p.trim());
284
+ el.style.transition = props.map((p) => `${p} ${duration}ms ${easing}`).join(", ");
285
+ },
286
+ /** Remove all transitions from an element. */
287
+ clear(el) {
288
+ el.style.transition = "";
289
+ },
290
+ /**
291
+ * Register a callback for transitionend. Returns a cleanup
292
+ * function that removes the listener.
293
+ */
294
+ onEnd(el, callback) {
295
+ el.addEventListener("transitionend", callback);
296
+ return () => el.removeEventListener("transitionend", callback);
297
+ }
298
+ };
299
+ export {
300
+ Anim,
301
+ Animation,
302
+ Easing,
303
+ Transition
304
+ };
305
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Animation.ts","../src/Anim.ts","../src/Easing.ts","../src/Transition.ts"],"sourcesContent":["/**\n * @framesquared/fx – Animation\n *\n * Wraps the Web Animations API (Element.animate()). Provides a\n * config-driven interface with play/pause/cancel/finish/reverse\n * controls and a finished promise.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport interface AnimationConfig {\n target: Element;\n keyframes: Keyframe[];\n duration?: number;\n easing?: string;\n delay?: number;\n iterations?: number;\n direction?: PlaybackDirection;\n fill?: FillMode;\n}\n\nexport class Animation {\n private target: Element;\n private keyframes: Keyframe[];\n private options: KeyframeAnimationOptions;\n private waAnimation: globalThis.Animation | null = null;\n\n /** Resolves when the animation completes. */\n finished: Promise<void>;\n private _resolveFinished!: () => void;\n\n constructor(config: AnimationConfig) {\n this.target = config.target;\n this.keyframes = config.keyframes;\n this.options = {\n duration: config.duration ?? 300,\n easing: config.easing ?? 'ease',\n delay: config.delay ?? 0,\n iterations: config.iterations ?? 1,\n direction: config.direction ?? 'normal',\n fill: config.fill ?? 'none',\n };\n this.finished = new Promise<void>((resolve) => {\n this._resolveFinished = resolve;\n });\n }\n\n play(): this {\n this.waAnimation = this.target.animate(this.keyframes, this.options);\n // Wire the finished promise\n this.waAnimation.finished\n .then(() => this._resolveFinished())\n .catch(() => { /* cancelled */ });\n return this;\n }\n\n pause(): this {\n this.waAnimation?.pause();\n return this;\n }\n\n cancel(): this {\n this.waAnimation?.cancel();\n return this;\n }\n\n finish(): this {\n this.waAnimation?.finish();\n return this;\n }\n\n reverse(): this {\n this.waAnimation?.reverse();\n return this;\n }\n\n getPlayState(): string {\n return this.waAnimation?.playState ?? 'idle';\n }\n\n get currentTime(): number | null {\n return this.waAnimation?.currentTime as number | null ?? null;\n }\n\n get playbackRate(): number {\n return this.waAnimation?.playbackRate ?? 1;\n }\n\n set playbackRate(rate: number) {\n if (this.waAnimation) this.waAnimation.playbackRate = rate;\n }\n\n /** Get the underlying WAAPI Animation object. */\n getNative(): globalThis.Animation | null {\n return this.waAnimation;\n }\n}\n","/**\n * @framesquared/fx – Anim\n *\n * Convenience factory for predefined animations. Each method\n * returns an Animation instance. Also provides queue() for\n * sequential and parallel() for simultaneous execution.\n */\n\nimport { Animation } from './Animation.js';\n\nexport interface AnimOptions {\n duration?: number;\n easing?: string;\n delay?: number;\n fill?: FillMode;\n}\n\ninterface SlideOptions extends AnimOptions {}\ninterface HighlightOptions extends AnimOptions { color?: string; }\ninterface ScaleOptions extends AnimOptions { from?: number; to?: number; }\ninterface RotateOptions extends AnimOptions { degrees?: number; }\n\nexport const Anim = {\n // -----------------------------------------------------------------------\n // Fade\n // -----------------------------------------------------------------------\n\n fadeIn(el: Element, opts: AnimOptions = {}): Animation {\n return new Animation({\n target: el,\n keyframes: [{ opacity: '0' }, { opacity: '1' }],\n duration: opts.duration ?? 300,\n easing: opts.easing ?? 'ease',\n delay: opts.delay ?? 0,\n fill: opts.fill ?? 'both',\n });\n },\n\n fadeOut(el: Element, opts: AnimOptions = {}): Animation {\n return new Animation({\n target: el,\n keyframes: [{ opacity: '1' }, { opacity: '0' }],\n duration: opts.duration ?? 300,\n easing: opts.easing ?? 'ease',\n delay: opts.delay ?? 0,\n fill: opts.fill ?? 'both',\n });\n },\n\n // -----------------------------------------------------------------------\n // Slide\n // -----------------------------------------------------------------------\n\n slideIn(el: Element, direction: 'left' | 'right' | 'top' | 'bottom' = 'left', opts: SlideOptions = {}): Animation {\n const transforms: Record<string, string> = {\n left: 'translateX(-100%)',\n right: 'translateX(100%)',\n top: 'translateY(-100%)',\n bottom: 'translateY(100%)',\n };\n return new Animation({\n target: el,\n keyframes: [\n { transform: transforms[direction], opacity: '0' },\n { transform: 'translateX(0) translateY(0)', opacity: '1' },\n ],\n duration: opts.duration ?? 400,\n easing: opts.easing ?? 'ease-out',\n fill: opts.fill ?? 'both',\n });\n },\n\n slideOut(el: Element, direction: 'left' | 'right' | 'top' | 'bottom' = 'right', opts: SlideOptions = {}): Animation {\n const transforms: Record<string, string> = {\n left: 'translateX(-100%)',\n right: 'translateX(100%)',\n top: 'translateY(-100%)',\n bottom: 'translateY(100%)',\n };\n return new Animation({\n target: el,\n keyframes: [\n { transform: 'translateX(0) translateY(0)', opacity: '1' },\n { transform: transforms[direction], opacity: '0' },\n ],\n duration: opts.duration ?? 400,\n easing: opts.easing ?? 'ease-in',\n fill: opts.fill ?? 'both',\n });\n },\n\n // -----------------------------------------------------------------------\n // Highlight\n // -----------------------------------------------------------------------\n\n highlight(el: Element, opts: HighlightOptions = {}): Animation {\n const color = opts.color ?? 'yellow';\n return new Animation({\n target: el,\n keyframes: [\n { backgroundColor: color },\n { backgroundColor: 'transparent' },\n ],\n duration: opts.duration ?? 1000,\n easing: opts.easing ?? 'ease-in-out',\n fill: opts.fill ?? 'both',\n });\n },\n\n // -----------------------------------------------------------------------\n // Scale\n // -----------------------------------------------------------------------\n\n scale(el: Element, opts: ScaleOptions = {}): Animation {\n const from = opts.from ?? 1;\n const to = opts.to ?? 1.5;\n return new Animation({\n target: el,\n keyframes: [\n { transform: `scale(${from})` },\n { transform: `scale(${to})` },\n ],\n duration: opts.duration ?? 300,\n easing: opts.easing ?? 'ease',\n fill: opts.fill ?? 'both',\n });\n },\n\n // -----------------------------------------------------------------------\n // Rotate\n // -----------------------------------------------------------------------\n\n rotate(el: Element, opts: RotateOptions = {}): Animation {\n const degrees = opts.degrees ?? 360;\n return new Animation({\n target: el,\n keyframes: [\n { transform: 'rotate(0deg)' },\n { transform: `rotate(${degrees}deg)` },\n ],\n duration: opts.duration ?? 500,\n easing: opts.easing ?? 'ease',\n fill: opts.fill ?? 'both',\n });\n },\n\n // -----------------------------------------------------------------------\n // Shake\n // -----------------------------------------------------------------------\n\n shake(el: Element, opts: AnimOptions = {}): Animation {\n return new Animation({\n target: el,\n keyframes: [\n { transform: 'translateX(0)' },\n { transform: 'translateX(-10px)' },\n { transform: 'translateX(10px)' },\n { transform: 'translateX(-6px)' },\n { transform: 'translateX(6px)' },\n { transform: 'translateX(-2px)' },\n { transform: 'translateX(0)' },\n ],\n duration: opts.duration ?? 500,\n easing: opts.easing ?? 'ease-in-out',\n });\n },\n\n // -----------------------------------------------------------------------\n // Pulse\n // -----------------------------------------------------------------------\n\n pulse(el: Element, opts: AnimOptions = {}): Animation {\n return new Animation({\n target: el,\n keyframes: [\n { transform: 'scale(1)' },\n { transform: 'scale(1.1)' },\n { transform: 'scale(1)' },\n ],\n duration: opts.duration ?? 600,\n easing: opts.easing ?? 'ease-in-out',\n });\n },\n\n // -----------------------------------------------------------------------\n // Flip\n // -----------------------------------------------------------------------\n\n flip(el: Element, opts: AnimOptions = {}): Animation {\n return new Animation({\n target: el,\n keyframes: [\n { transform: 'rotateY(0deg)' },\n { transform: 'rotateY(180deg)' },\n ],\n duration: opts.duration ?? 600,\n easing: opts.easing ?? 'ease-in-out',\n fill: opts.fill ?? 'both',\n });\n },\n\n // -----------------------------------------------------------------------\n // Queue — run animations sequentially\n // -----------------------------------------------------------------------\n\n async queue(animations: Animation[]): Promise<void> {\n for (const anim of animations) {\n anim.play();\n await anim.finished;\n }\n },\n\n // -----------------------------------------------------------------------\n // Parallel — run animations simultaneously\n // -----------------------------------------------------------------------\n\n async parallel(animations: Animation[]): Promise<void> {\n for (const anim of animations) {\n anim.play();\n }\n await Promise.all(animations.map(a => a.finished));\n },\n};\n","/**\n * @framesquared/fx – Easing\n *\n * Named CSS easing strings. Standard names map to CSS keywords;\n * extended names map to cubic-bezier() values. Bounce easings\n * use linear() approximation since cubic-bezier can't represent them.\n */\n\nexport const Easing = {\n // CSS keywords\n linear: 'linear',\n ease: 'ease',\n easeIn: 'ease-in',\n easeOut: 'ease-out',\n easeInOut: 'ease-in-out',\n\n // Quadratic\n easeInQuad: 'cubic-bezier(0.55, 0.085, 0.68, 0.53)',\n easeOutQuad: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\n easeInOutQuad: 'cubic-bezier(0.455, 0.03, 0.515, 0.955)',\n\n // Cubic\n easeInCubic: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)',\n easeOutCubic: 'cubic-bezier(0.215, 0.61, 0.355, 1)',\n easeInOutCubic: 'cubic-bezier(0.645, 0.045, 0.355, 1)',\n\n // Back (overshoot)\n easeInBack: 'cubic-bezier(0.6, -0.28, 0.735, 0.045)',\n easeOutBack: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',\n easeInOutBack: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',\n\n // Bounce (approximated — true bounce can't be expressed as cubic-bezier)\n easeInBounce: 'cubic-bezier(0.6, 0.04, 0.98, 0.335)',\n easeOutBounce: 'cubic-bezier(0.015, 0.66, 0.39, 0.995)',\n easeInOutBounce: 'cubic-bezier(0.83, 0.04, 0.17, 1)',\n} as const;\n\nexport type EasingName = keyof typeof Easing;\n","/**\n * @framesquared/fx – Transition\n *\n * CSS transition management: set/clear transitions on elements,\n * and register transitionend callbacks with cleanup.\n */\n\nexport const Transition = {\n /**\n * Set a CSS transition on an element.\n * @param el Target element\n * @param property CSS property name(s), comma-separated\n * @param duration Duration in ms\n * @param easing CSS easing string\n */\n set(el: HTMLElement, property: string, duration: number, easing = 'ease'): void {\n const props = property.split(',').map(p => p.trim());\n el.style.transition = props\n .map(p => `${p} ${duration}ms ${easing}`)\n .join(', ');\n },\n\n /** Remove all transitions from an element. */\n clear(el: HTMLElement): void {\n el.style.transition = '';\n },\n\n /**\n * Register a callback for transitionend. Returns a cleanup\n * function that removes the listener.\n */\n onEnd(el: HTMLElement, callback: (e: Event) => void): () => void {\n el.addEventListener('transitionend', callback);\n return () => el.removeEventListener('transitionend', callback);\n },\n};\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@framesquared/fx",
3
+ "version": "0.1.0",
4
+ "description": "Animations and effects",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@framesquared/core": "0.1.0"
19
+ },
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "test": "vitest run",
23
+ "clean": "rm -rf dist",
24
+ "typecheck": "tsc --noEmit --project tsconfig.build.json"
25
+ }
26
+ }