@praxisjs/motion 0.2.3 → 1.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/__tests__/decorators.test.d.ts +2 -0
  3. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  4. package/dist/__tests__/decorators.test.js +179 -0
  5. package/dist/__tests__/decorators.test.js.map +1 -0
  6. package/dist/__tests__/easings.test.js +82 -55
  7. package/dist/__tests__/easings.test.js.map +1 -1
  8. package/dist/__tests__/spring.test.js +80 -34
  9. package/dist/__tests__/spring.test.js.map +1 -1
  10. package/dist/__tests__/transition.test.js +111 -43
  11. package/dist/__tests__/transition.test.js.map +1 -1
  12. package/dist/__tests__/tween.test.js +107 -94
  13. package/dist/__tests__/tween.test.js.map +1 -1
  14. package/dist/decorators.d.ts +3 -1
  15. package/dist/decorators.d.ts.map +1 -1
  16. package/dist/decorators.js +39 -16
  17. package/dist/decorators.js.map +1 -1
  18. package/dist/easings.d.ts.map +1 -1
  19. package/dist/easings.js +7 -1
  20. package/dist/easings.js.map +1 -1
  21. package/dist/index.d.ts +2 -10
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -6
  24. package/dist/index.js.map +1 -1
  25. package/dist/spring.d.ts.map +1 -1
  26. package/dist/spring.js +3 -0
  27. package/dist/spring.js.map +1 -1
  28. package/dist/transition.d.ts.map +1 -1
  29. package/dist/transition.js +34 -18
  30. package/dist/transition.js.map +1 -1
  31. package/dist/tween.d.ts.map +1 -1
  32. package/dist/tween.js +2 -1
  33. package/dist/tween.js.map +1 -1
  34. package/package.json +3 -2
  35. package/src/__tests__/decorators.test.ts +196 -0
  36. package/src/__tests__/easings.test.ts +86 -57
  37. package/src/__tests__/spring.test.ts +90 -34
  38. package/src/__tests__/transition.test.ts +122 -45
  39. package/src/__tests__/tween.test.ts +116 -97
  40. package/src/decorators.ts +42 -15
  41. package/src/easings.ts +6 -1
  42. package/src/index.ts +2 -15
  43. package/src/spring.ts +4 -0
  44. package/src/transition.ts +28 -18
  45. package/src/tween.ts +2 -1
  46. package/dist/__tests__/use-motion.test.d.ts +0 -2
  47. package/dist/__tests__/use-motion.test.d.ts.map +0 -1
  48. package/dist/__tests__/use-motion.test.js +0 -139
  49. package/dist/__tests__/use-motion.test.js.map +0 -1
  50. package/dist/use-motion.d.ts +0 -20
  51. package/dist/use-motion.d.ts.map +0 -1
  52. package/dist/use-motion.js +0 -58
  53. package/dist/use-motion.js.map +0 -1
  54. package/src/__tests__/use-motion.test.ts +0 -172
  55. package/src/use-motion.ts +0 -89
package/src/easings.ts CHANGED
@@ -23,5 +23,10 @@ export const easings = {
23
23
  export type Easing = keyof typeof easings | ((t: number) => number);
24
24
 
25
25
  export function resolveEasing(easing: Easing): (t: number) => number {
26
- return typeof easing === "function" ? easing : easings[easing];
26
+ if (typeof easing === "function") return easing;
27
+ const fn = easings[easing];
28
+ if (typeof fn !== "function") {
29
+ throw new Error(`[resolveEasing] Unknown easing: "${easing}". Valid names are: ${Object.keys(easings).join(", ")}.`);
30
+ }
31
+ return fn;
27
32
  }
package/src/index.ts CHANGED
@@ -1,16 +1,3 @@
1
- export { easings, resolveEasing } from "./easings";
2
- export type { Easing } from "./easings";
3
-
4
- export { tween } from "./tween";
5
- export type { Tween, TweenOptions } from "./tween";
6
-
7
- export { spring } from "./spring";
1
+ export { Tween, Spring } from "./decorators";
2
+ export type { TweenOptions } from "./tween";
8
3
  export type { SpringOptions } from "./spring";
9
-
10
- export { useMotion } from "./use-motion";
11
- export type { MotionKeyframes } from "./use-motion";
12
-
13
- export { createTransition } from "./transition";
14
- export type { TransitionOptions } from "./transition";
15
-
16
- export { Animate } from "./decorators";
package/src/spring.ts CHANGED
@@ -15,6 +15,10 @@ export function spring(initial: number, options: SpringOptions = {}) {
15
15
  precision = 0.001,
16
16
  } = options;
17
17
 
18
+ if (stiffness <= 0) {
19
+ throw new Error("stiffness must be greater than 0");
20
+ }
21
+
18
22
  const _value = signal(initial);
19
23
  const _target = signal(initial);
20
24
  let velocity = 0;
package/src/transition.ts CHANGED
@@ -10,32 +10,42 @@ export function createTransition(options: TransitionOptions = {}) {
10
10
 
11
11
  return {
12
12
  enter(el: HTMLElement): Promise<void> {
13
- onEnter?.(el);
14
- el.classList.add(`${name}-enter-from`);
15
- requestAnimationFrame(() => {
16
- el.classList.remove(`${name}-enter-from`);
17
- el.classList.add(`${name}-enter-to`);
18
- });
19
- return new Promise((res) =>
13
+ return new Promise((res, rej) => {
14
+ try {
15
+ onEnter?.(el);
16
+ } catch (err) {
17
+ rej(err instanceof Error ? err : new Error(String(err)));
18
+ return;
19
+ }
20
+ el.classList.add(`${name}-enter-from`);
21
+ requestAnimationFrame(() => {
22
+ el.classList.remove(`${name}-enter-from`);
23
+ el.classList.add(`${name}-enter-to`);
24
+ });
20
25
  setTimeout(() => {
21
26
  el.classList.remove(`${name}-enter-to`);
22
27
  res();
23
- }, duration),
24
- );
28
+ }, duration);
29
+ });
25
30
  },
26
31
  leave(el: HTMLElement): Promise<void> {
27
- onLeave?.(el);
28
- el.classList.add(`${name}-leave-from`);
29
- requestAnimationFrame(() => {
30
- el.classList.remove(`${name}-leave-from`);
31
- el.classList.add(`${name}-leave-to`);
32
- });
33
- return new Promise((res) =>
32
+ return new Promise((res, rej) => {
33
+ try {
34
+ onLeave?.(el);
35
+ } catch (err) {
36
+ rej(err instanceof Error ? err : new Error(String(err)));
37
+ return;
38
+ }
39
+ el.classList.add(`${name}-leave-from`);
40
+ requestAnimationFrame(() => {
41
+ el.classList.remove(`${name}-leave-from`);
42
+ el.classList.add(`${name}-leave-to`);
43
+ });
34
44
  setTimeout(() => {
35
45
  el.classList.remove(`${name}-leave-to`);
36
46
  res();
37
- }, duration),
38
- );
47
+ }, duration);
48
+ });
39
49
  },
40
50
  };
41
51
  }
package/src/tween.ts CHANGED
@@ -23,7 +23,8 @@ export function tween(
23
23
  to: number,
24
24
  options: TweenOptions = {},
25
25
  ): Tween {
26
- const { duration = 300, easing = "easeOut", delay = 0 } = options;
26
+ const { duration: rawDuration = 300, easing = "easeOut", delay = 0 } = options;
27
+ const duration = Math.max(1, rawDuration);
27
28
  const easeFn = resolveEasing(easing);
28
29
 
29
30
  const _value = signal(from);
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=use-motion.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-motion.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/use-motion.test.ts"],"names":[],"mappings":""}
@@ -1,139 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
- import { useMotion } from "../use-motion";
4
- function makeRef(el = null) {
5
- return { current: el };
6
- }
7
- describe("useMotion", () => {
8
- beforeEach(() => {
9
- vi.useFakeTimers();
10
- });
11
- afterEach(() => {
12
- vi.clearAllTimers();
13
- vi.useRealTimers();
14
- });
15
- it("returns animate, enter, and exit functions", () => {
16
- const ref = makeRef(document.createElement("div"));
17
- const m = useMotion(ref);
18
- expect(typeof m.animate).toBe("function");
19
- expect(typeof m.enter).toBe("function");
20
- expect(typeof m.exit).toBe("function");
21
- });
22
- it("returns a cancel function when ref.current is null", () => {
23
- const ref = makeRef(null);
24
- const m = useMotion(ref);
25
- const cancel = m.animate({ opacity: [0, 1] });
26
- expect(typeof cancel).toBe("function");
27
- // calling cancel on null ref should not throw
28
- expect(() => { cancel(); }).not.toThrow();
29
- });
30
- it("sets opacity style during animation", async () => {
31
- const el = document.createElement("div");
32
- const ref = makeRef(el);
33
- const m = useMotion(ref);
34
- const rafCallbacks = [];
35
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
36
- rafCallbacks.push(cb);
37
- return 1;
38
- });
39
- m.animate({ opacity: [0, 1], duration: 100 });
40
- // Simulate first frame at t=50 (half way through)
41
- rafCallbacks[0]?.(50);
42
- expect(el.style.opacity).not.toBe("");
43
- });
44
- it("sets transform style for x, y, scale, rotate", () => {
45
- const el = document.createElement("div");
46
- const ref = makeRef(el);
47
- const m = useMotion(ref);
48
- const rafCallbacks = [];
49
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
50
- rafCallbacks.push(cb);
51
- return 1;
52
- });
53
- m.animate({ x: [0, 100], y: [0, 50], scale: [1, 2], rotate: [0, 90], duration: 200 });
54
- rafCallbacks[0]?.(100);
55
- expect(el.style.transform).toContain("translateX");
56
- expect(el.style.transform).toContain("translateY");
57
- expect(el.style.transform).toContain("scale");
58
- expect(el.style.transform).toContain("rotate");
59
- });
60
- it("calls onComplete when animation finishes", () => {
61
- const el = document.createElement("div");
62
- const ref = makeRef(el);
63
- const m = useMotion(ref);
64
- const onComplete = vi.fn();
65
- const rafCallbacks = [];
66
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
67
- rafCallbacks.push(cb);
68
- return rafCallbacks.length;
69
- });
70
- m.animate({ opacity: [0, 1], duration: 100, onComplete });
71
- // First frame sets startTime = 0
72
- rafCallbacks[0](0);
73
- // Second frame at ts=100: elapsed=100, t=1, onComplete fires
74
- rafCallbacks[1](100);
75
- expect(onComplete).toHaveBeenCalledTimes(1);
76
- });
77
- it("cancel() stops the animation", () => {
78
- const el = document.createElement("div");
79
- const ref = makeRef(el);
80
- const m = useMotion(ref);
81
- const cancelRaf = vi.fn();
82
- const rafCallbacks = [];
83
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
84
- rafCallbacks.push(cb);
85
- return 42;
86
- });
87
- vi.spyOn(window, "cancelAnimationFrame").mockImplementation(cancelRaf);
88
- const cancel = m.animate({ opacity: [0, 1], duration: 300 });
89
- cancel();
90
- // After cancel, further frames should be no-ops
91
- rafCallbacks[0]?.(100);
92
- expect(cancelRaf).toHaveBeenCalledWith(42);
93
- });
94
- it("exit() reverses opacity and x/y values", () => {
95
- const el = document.createElement("div");
96
- const ref = makeRef(el);
97
- const m = useMotion(ref);
98
- const rafCallbacks = [];
99
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
100
- rafCallbacks.push(cb);
101
- return 1;
102
- });
103
- // exit reverses opacity [0,1] → animates [1,0] and x [0,100] → [100,0]
104
- m.exit({ opacity: [0, 1], x: [0, 100], duration: 100 });
105
- rafCallbacks[0]?.(0);
106
- // At t=0 (start), opacity should be 1 (the reversed start)
107
- expect(el.style.opacity).toBe("1");
108
- });
109
- it("enter() is an alias for animate()", () => {
110
- const el = document.createElement("div");
111
- const ref = makeRef(el);
112
- const m = useMotion(ref);
113
- const onComplete = vi.fn();
114
- const rafCallbacks = [];
115
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
116
- rafCallbacks.push(cb);
117
- return rafCallbacks.length;
118
- });
119
- m.enter({ opacity: [0, 1], duration: 50, onComplete });
120
- rafCallbacks[0](0); // sets startTime=0
121
- rafCallbacks[1](50); // elapsed=50, t=1
122
- expect(onComplete).toHaveBeenCalledTimes(1);
123
- });
124
- it("respects delay option — no progress before delay elapses", () => {
125
- const el = document.createElement("div");
126
- const ref = makeRef(el);
127
- const m = useMotion(ref);
128
- const rafCallbacks = [];
129
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
130
- rafCallbacks.push(cb);
131
- return 1;
132
- });
133
- m.animate({ opacity: [0, 1], delay: 200, duration: 100 });
134
- // At ts=50, delay not yet passed; opacity should be at start value (0)
135
- rafCallbacks[0]?.(50);
136
- expect(el.style.opacity).toBe("0");
137
- });
138
- });
139
- //# sourceMappingURL=use-motion.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-motion.test.js","sourceRoot":"","sources":["../../src/__tests__/use-motion.test.ts"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- import { type Easing } from "./easings";
2
- export interface MotionKeyframes {
3
- opacity?: [number, number];
4
- x?: [number, number];
5
- y?: [number, number];
6
- scale?: [number, number];
7
- rotate?: [number, number];
8
- duration?: number;
9
- easing?: Easing;
10
- delay?: number;
11
- onComplete?: () => void;
12
- }
13
- export declare function useMotion(ref: {
14
- current: HTMLElement | null;
15
- }): {
16
- animate: (keyframes: MotionKeyframes) => () => void;
17
- enter: (kf: MotionKeyframes) => () => void;
18
- exit: (kf: MotionKeyframes) => () => void;
19
- };
20
- //# sourceMappingURL=use-motion.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-motion.d.ts","sourceRoot":"","sources":["../src/use-motion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE;yBAChC,eAAe,KAAG,MAAM,IAAI;gBAgE1C,eAAe,WAhEuB,IAAI;eAiE3C,eAAe,WAjEwB,IAAI;EAyEzD"}
@@ -1,58 +0,0 @@
1
- import { resolveEasing } from "./easings";
2
- export function useMotion(ref) {
3
- function animate(keyframes) {
4
- const el = ref.current;
5
- if (!el)
6
- return () => undefined;
7
- const safeEl = el;
8
- const { opacity, x, y, scale, rotate, duration = 300, easing = "easeOut", delay = 0, onComplete, } = keyframes;
9
- const easeFn = resolveEasing(easing);
10
- let raf;
11
- let startTime;
12
- let cancelled = false;
13
- function frame(ts) {
14
- if (cancelled)
15
- return;
16
- startTime ??= ts + delay;
17
- const elapsed = Math.max(0, ts - startTime);
18
- const t = Math.min(elapsed / duration, 1);
19
- const e = easeFn(t);
20
- const transforms = [];
21
- if (x)
22
- transforms.push(`translateX(${String(x[0] + (x[1] - x[0]) * e)}px)`);
23
- if (y)
24
- transforms.push(`translateY(${String(y[0] + (y[1] - y[0]) * e)}px)`);
25
- if (scale)
26
- transforms.push(`scale(${String(scale[0] + (scale[1] - scale[0]) * e)})`);
27
- if (rotate)
28
- transforms.push(`rotate(${String(rotate[0] + (rotate[1] - rotate[0]) * e)}deg)`);
29
- if (transforms.length)
30
- safeEl.style.transform = transforms.join(" ");
31
- if (opacity)
32
- safeEl.style.opacity = String(opacity[0] + (opacity[1] - opacity[0]) * e);
33
- if (t < 1) {
34
- raf = requestAnimationFrame(frame);
35
- }
36
- else {
37
- onComplete?.();
38
- }
39
- }
40
- raf = requestAnimationFrame(frame);
41
- return () => {
42
- cancelled = true;
43
- if (raf)
44
- cancelAnimationFrame(raf);
45
- };
46
- }
47
- return {
48
- animate,
49
- enter: (kf) => animate(kf),
50
- exit: (kf) => animate({
51
- ...kf,
52
- opacity: kf.opacity ? [kf.opacity[1], kf.opacity[0]] : undefined,
53
- x: kf.x ? [kf.x[1], kf.x[0]] : undefined,
54
- y: kf.y ? [kf.y[1], kf.y[0]] : undefined,
55
- }),
56
- };
57
- }
58
- //# sourceMappingURL=use-motion.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-motion.js","sourceRoot":"","sources":["../src/use-motion.ts"],"names":[],"mappings":"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"}
@@ -1,172 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
-
4
- import { useMotion } from "../use-motion";
5
-
6
- function makeRef(el: HTMLElement | null = null) {
7
- return { current: el };
8
- }
9
-
10
- describe("useMotion", () => {
11
- beforeEach(() => {
12
- vi.useFakeTimers();
13
- });
14
-
15
- afterEach(() => {
16
- vi.clearAllTimers();
17
- vi.useRealTimers();
18
- });
19
-
20
- it("returns animate, enter, and exit functions", () => {
21
- const ref = makeRef(document.createElement("div"));
22
- const m = useMotion(ref);
23
- expect(typeof m.animate).toBe("function");
24
- expect(typeof m.enter).toBe("function");
25
- expect(typeof m.exit).toBe("function");
26
- });
27
-
28
- it("returns a cancel function when ref.current is null", () => {
29
- const ref = makeRef(null);
30
- const m = useMotion(ref);
31
- const cancel = m.animate({ opacity: [0, 1] });
32
- expect(typeof cancel).toBe("function");
33
- // calling cancel on null ref should not throw
34
- expect(() => { cancel(); }).not.toThrow();
35
- });
36
-
37
- it("sets opacity style during animation", async () => {
38
- const el = document.createElement("div");
39
- const ref = makeRef(el);
40
- const m = useMotion(ref);
41
-
42
- const rafCallbacks: FrameRequestCallback[] = [];
43
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
44
- rafCallbacks.push(cb);
45
- return 1;
46
- });
47
-
48
- m.animate({ opacity: [0, 1], duration: 100 });
49
-
50
- // Simulate first frame at t=50 (half way through)
51
- rafCallbacks[0]?.(50);
52
- expect(el.style.opacity).not.toBe("");
53
- });
54
-
55
- it("sets transform style for x, y, scale, rotate", () => {
56
- const el = document.createElement("div");
57
- const ref = makeRef(el);
58
- const m = useMotion(ref);
59
-
60
- const rafCallbacks: FrameRequestCallback[] = [];
61
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
62
- rafCallbacks.push(cb);
63
- return 1;
64
- });
65
-
66
- m.animate({ x: [0, 100], y: [0, 50], scale: [1, 2], rotate: [0, 90], duration: 200 });
67
-
68
- rafCallbacks[0]?.(100);
69
- expect(el.style.transform).toContain("translateX");
70
- expect(el.style.transform).toContain("translateY");
71
- expect(el.style.transform).toContain("scale");
72
- expect(el.style.transform).toContain("rotate");
73
- });
74
-
75
- it("calls onComplete when animation finishes", () => {
76
- const el = document.createElement("div");
77
- const ref = makeRef(el);
78
- const m = useMotion(ref);
79
- const onComplete = vi.fn();
80
-
81
- const rafCallbacks: FrameRequestCallback[] = [];
82
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
83
- rafCallbacks.push(cb);
84
- return rafCallbacks.length;
85
- });
86
-
87
- m.animate({ opacity: [0, 1], duration: 100, onComplete });
88
-
89
- // First frame sets startTime = 0
90
- rafCallbacks[0](0);
91
- // Second frame at ts=100: elapsed=100, t=1, onComplete fires
92
- rafCallbacks[1](100);
93
- expect(onComplete).toHaveBeenCalledTimes(1);
94
- });
95
-
96
- it("cancel() stops the animation", () => {
97
- const el = document.createElement("div");
98
- const ref = makeRef(el);
99
- const m = useMotion(ref);
100
-
101
- const cancelRaf = vi.fn();
102
- const rafCallbacks: FrameRequestCallback[] = [];
103
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
104
- rafCallbacks.push(cb);
105
- return 42;
106
- });
107
- vi.spyOn(window, "cancelAnimationFrame").mockImplementation(cancelRaf);
108
-
109
- const cancel = m.animate({ opacity: [0, 1], duration: 300 });
110
- cancel();
111
-
112
- // After cancel, further frames should be no-ops
113
- rafCallbacks[0]?.(100);
114
- expect(cancelRaf).toHaveBeenCalledWith(42);
115
- });
116
-
117
- it("exit() reverses opacity and x/y values", () => {
118
- const el = document.createElement("div");
119
- const ref = makeRef(el);
120
- const m = useMotion(ref);
121
-
122
- const rafCallbacks: FrameRequestCallback[] = [];
123
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
124
- rafCallbacks.push(cb);
125
- return 1;
126
- });
127
-
128
- // exit reverses opacity [0,1] → animates [1,0] and x [0,100] → [100,0]
129
- m.exit({ opacity: [0, 1], x: [0, 100], duration: 100 });
130
-
131
- rafCallbacks[0]?.(0);
132
- // At t=0 (start), opacity should be 1 (the reversed start)
133
- expect(el.style.opacity).toBe("1");
134
- });
135
-
136
- it("enter() is an alias for animate()", () => {
137
- const el = document.createElement("div");
138
- const ref = makeRef(el);
139
- const m = useMotion(ref);
140
- const onComplete = vi.fn();
141
-
142
- const rafCallbacks: FrameRequestCallback[] = [];
143
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
144
- rafCallbacks.push(cb);
145
- return rafCallbacks.length;
146
- });
147
-
148
- m.enter({ opacity: [0, 1], duration: 50, onComplete });
149
-
150
- rafCallbacks[0](0); // sets startTime=0
151
- rafCallbacks[1](50); // elapsed=50, t=1
152
- expect(onComplete).toHaveBeenCalledTimes(1);
153
- });
154
-
155
- it("respects delay option — no progress before delay elapses", () => {
156
- const el = document.createElement("div");
157
- const ref = makeRef(el);
158
- const m = useMotion(ref);
159
-
160
- const rafCallbacks: FrameRequestCallback[] = [];
161
- vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => {
162
- rafCallbacks.push(cb);
163
- return 1;
164
- });
165
-
166
- m.animate({ opacity: [0, 1], delay: 200, duration: 100 });
167
-
168
- // At ts=50, delay not yet passed; opacity should be at start value (0)
169
- rafCallbacks[0]?.(50);
170
- expect(el.style.opacity).toBe("0");
171
- });
172
- });
package/src/use-motion.ts DELETED
@@ -1,89 +0,0 @@
1
- import { resolveEasing, type Easing } from "./easings";
2
-
3
- export interface MotionKeyframes {
4
- opacity?: [number, number];
5
- x?: [number, number];
6
- y?: [number, number];
7
- scale?: [number, number];
8
- rotate?: [number, number];
9
- duration?: number;
10
- easing?: Easing;
11
- delay?: number;
12
- onComplete?: () => void;
13
- }
14
-
15
- export function useMotion(ref: { current: HTMLElement | null }) {
16
- function animate(keyframes: MotionKeyframes): () => void {
17
- const el: HTMLElement | null = ref.current;
18
- if (!el) return () => undefined;
19
- const safeEl = el;
20
-
21
- const {
22
- opacity,
23
- x,
24
- y,
25
- scale,
26
- rotate,
27
- duration = 300,
28
- easing = "easeOut",
29
- delay = 0,
30
- onComplete,
31
- } = keyframes;
32
- const easeFn = resolveEasing(easing);
33
- let raf: number | undefined;
34
- let startTime: number | undefined;
35
- let cancelled = false;
36
-
37
- function frame(ts: number) {
38
- if (cancelled) return;
39
- startTime ??= ts + delay;
40
- const elapsed = Math.max(0, ts - startTime);
41
- const t = Math.min(elapsed / duration, 1);
42
- const e = easeFn(t);
43
-
44
- const transforms: string[] = [];
45
- if (x)
46
- transforms.push(`translateX(${String(x[0] + (x[1] - x[0]) * e)}px)`);
47
- if (y)
48
- transforms.push(`translateY(${String(y[0] + (y[1] - y[0]) * e)}px)`);
49
- if (scale)
50
- transforms.push(
51
- `scale(${String(scale[0] + (scale[1] - scale[0]) * e)})`,
52
- );
53
- if (rotate)
54
- transforms.push(
55
- `rotate(${String(rotate[0] + (rotate[1] - rotate[0]) * e)}deg)`,
56
- );
57
-
58
- if (transforms.length) safeEl.style.transform = transforms.join(" ");
59
- if (opacity)
60
- safeEl.style.opacity = String(
61
- opacity[0] + (opacity[1] - opacity[0]) * e,
62
- );
63
-
64
- if (t < 1) {
65
- raf = requestAnimationFrame(frame);
66
- } else {
67
- onComplete?.();
68
- }
69
- }
70
-
71
- raf = requestAnimationFrame(frame);
72
- return () => {
73
- cancelled = true;
74
- if (raf) cancelAnimationFrame(raf);
75
- };
76
- }
77
-
78
- return {
79
- animate,
80
- enter: (kf: MotionKeyframes) => animate(kf),
81
- exit: (kf: MotionKeyframes) =>
82
- animate({
83
- ...kf,
84
- opacity: kf.opacity ? [kf.opacity[1], kf.opacity[0]] : undefined,
85
- x: kf.x ? [kf.x[1], kf.x[0]] : undefined,
86
- y: kf.y ? [kf.y[1], kf.y[0]] : undefined,
87
- }),
88
- };
89
- }