@xanui/core 1.3.3 → 1.3.5
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/Transition/index.cjs +0 -16
- package/Transition/index.cjs.map +1 -1
- package/Transition/index.js +1 -17
- package/Transition/index.js.map +1 -1
- package/animate/index.cjs +30 -32
- package/animate/index.cjs.map +1 -1
- package/animate/index.js +30 -32
- package/animate/index.js.map +1 -1
- package/package.json +1 -1
package/Transition/index.cjs
CHANGED
|
@@ -59,22 +59,6 @@ function TransitionBase(_a) {
|
|
|
59
59
|
trans.exit(isnot ? false : true);
|
|
60
60
|
}
|
|
61
61
|
}, [open]);
|
|
62
|
-
React.useEffect(() => {
|
|
63
|
-
if (!ref.current)
|
|
64
|
-
return;
|
|
65
|
-
const ele = ref.current;
|
|
66
|
-
const observer = new ResizeObserver(() => {
|
|
67
|
-
const _rect = ele.getBoundingClientRect();
|
|
68
|
-
const current = rect.current;
|
|
69
|
-
if (!current || (_rect.width !== current.width || _rect.height !== current.height)) {
|
|
70
|
-
rect.current = _rect;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
observer.observe(ele);
|
|
74
|
-
return () => {
|
|
75
|
-
observer.disconnect();
|
|
76
|
-
};
|
|
77
|
-
}, []);
|
|
78
62
|
if (exitOnUnmount && trans.status === "exited")
|
|
79
63
|
return;
|
|
80
64
|
const childs = React.Children.toArray(children);
|
package/Transition/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/Transition/index.tsx"],"sourcesContent":["\"use client\";\nimport React, { cloneElement, Children, useRef, isValidElement, useLayoutEffect, useEffect } from 'react';\nimport * as variants from './variants'\nimport { Easing } from '../animate';\nimport useTransition from '../hooks/useTransition';\n\nexport type TransitionVariantTypes = keyof typeof variants\nexport type TransitionProps = {\n children: React.ReactElement;\n open: boolean;\n variant: TransitionVariantTypes\n easing?: keyof typeof Easing\n duration?: number;\n delay?: number;\n initialTransition?: boolean;\n\n exitOnUnmount?: boolean;\n\n onEnter?: () => void\n onEntered?: () => void\n onExit?: () => void\n onExited?: () => void\n onUpdate?: (value: Record<string, number>, progress: number) => void;\n onDone?: () => void;\n}\n\n\nfunction TransitionBase({ children, ...options }: TransitionProps) {\n let {\n open,\n variant = \"fade\",\n duration = 450,\n delay,\n easing,\n exitOnUnmount = false,\n initialTransition = true,\n onEnter,\n onEntered,\n onExit,\n onExited,\n onUpdate,\n onDone,\n\n } = options\n\n easing ??= \"default\"\n\n const variantCb = variants[variant]\n const ref = useRef<HTMLElement>(null)\n const init = useRef(false)\n const rect = useRef<DOMRect>(null)\n\n const trans = useTransition({\n delay,\n duration,\n easing: Easing[easing],\n onEnter,\n onEntered,\n onExit,\n onExited,\n onDone,\n from: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.from\n },\n to: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.to\n },\n onUpdate: (val, prograss) => {\n if (!ref.current || !rect.current) return\n const vc = variantCb(ref.current, rect.current)\n onUpdate?.(val, prograss)\n return vc.onUpdate(val)\n },\n })\n\n\n useLayoutEffect(() => {\n const isnot = !init.current && !initialTransition\n init.current = true\n if (open) {\n trans.enter(isnot ? false : true)\n } else {\n trans.exit(isnot ? false : true)\n }\n }, [open])\n\n
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/Transition/index.tsx"],"sourcesContent":["\"use client\";\nimport React, { cloneElement, Children, useRef, isValidElement, useLayoutEffect, useEffect } from 'react';\nimport * as variants from './variants'\nimport { Easing } from '../animate';\nimport useTransition from '../hooks/useTransition';\n\nexport type TransitionVariantTypes = keyof typeof variants\nexport type TransitionProps = {\n children: React.ReactElement;\n open: boolean;\n variant: TransitionVariantTypes\n easing?: keyof typeof Easing\n duration?: number;\n delay?: number;\n initialTransition?: boolean;\n\n exitOnUnmount?: boolean;\n\n onEnter?: () => void\n onEntered?: () => void\n onExit?: () => void\n onExited?: () => void\n onUpdate?: (value: Record<string, number>, progress: number) => void;\n onDone?: () => void;\n}\n\n\nfunction TransitionBase({ children, ...options }: TransitionProps) {\n let {\n open,\n variant = \"fade\",\n duration = 450,\n delay,\n easing,\n exitOnUnmount = false,\n initialTransition = true,\n onEnter,\n onEntered,\n onExit,\n onExited,\n onUpdate,\n onDone,\n\n } = options\n\n easing ??= \"default\"\n\n const variantCb = variants[variant]\n const ref = useRef<HTMLElement>(null)\n const init = useRef(false)\n const rect = useRef<DOMRect>(null)\n\n const trans = useTransition({\n delay,\n duration,\n easing: Easing[easing],\n onEnter,\n onEntered,\n onExit,\n onExited,\n onDone,\n from: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.from\n },\n to: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.to\n },\n onUpdate: (val, prograss) => {\n if (!ref.current || !rect.current) return\n const vc = variantCb(ref.current, rect.current)\n onUpdate?.(val, prograss)\n return vc.onUpdate(val)\n },\n })\n\n\n useLayoutEffect(() => {\n const isnot = !init.current && !initialTransition\n init.current = true\n if (open) {\n trans.enter(isnot ? false : true)\n } else {\n trans.exit(isnot ? false : true)\n }\n }, [open])\n\n\n if (exitOnUnmount && trans.status === \"exited\") return\n\n const childs = Children.toArray(children)\n if (childs.length !== 1) {\n throw new Error(\"Transition expects exactly one child.\");\n }\n const child = childs[0]\n if (!isValidElement(child)) {\n throw new Error(\"Transition expects a valid React element.\");\n }\n\n return cloneElement(child, {\n ref: (node: HTMLElement) => {\n ref.current = node;\n\n const childRef = (child as any).ref;\n if (typeof childRef === \"function\") {\n childRef(node);\n } else if (childRef) {\n childRef.current = node;\n }\n }\n } as any);\n}\n\n\nconst Transition = (props: any) => {\n return (\n <TransitionBase\n key={props.variant}\n {...props}\n />\n )\n}\nexport default Transition"],"names":[],"mappings":";;;;;;;;;;AA2BA;AAAwB;AACpB;;AAmBA;AACA;AACA;AACA;;;;AAKI;;;;;;;;AAOI;;;AAGA;;;;;AAIA;;;AAGA;;;AAGJ;;;AAEI;;AAEA;;AAEP;;;AAKG;;AAEI;;;AAEA;;AAER;AAGA;;;AAGA;AACI;;AAEJ;AACA;AACI;;;AAIA;AACI;AAEA;AACA;;;;AAGI;;;AAGJ;AACZ;AAGA;;AAOA;;"}
|
package/Transition/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { __rest } from 'tslib';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
|
-
import { useRef, useLayoutEffect,
|
|
4
|
+
import { useRef, useLayoutEffect, Children, isValidElement, cloneElement } from 'react';
|
|
5
5
|
import * as variants from './variants.js';
|
|
6
6
|
import Easing from '../animate/easing.js';
|
|
7
7
|
import useTransition from '../hooks/useTransition.js';
|
|
@@ -57,22 +57,6 @@ function TransitionBase(_a) {
|
|
|
57
57
|
trans.exit(isnot ? false : true);
|
|
58
58
|
}
|
|
59
59
|
}, [open]);
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (!ref.current)
|
|
62
|
-
return;
|
|
63
|
-
const ele = ref.current;
|
|
64
|
-
const observer = new ResizeObserver(() => {
|
|
65
|
-
const _rect = ele.getBoundingClientRect();
|
|
66
|
-
const current = rect.current;
|
|
67
|
-
if (!current || (_rect.width !== current.width || _rect.height !== current.height)) {
|
|
68
|
-
rect.current = _rect;
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
observer.observe(ele);
|
|
72
|
-
return () => {
|
|
73
|
-
observer.disconnect();
|
|
74
|
-
};
|
|
75
|
-
}, []);
|
|
76
60
|
if (exitOnUnmount && trans.status === "exited")
|
|
77
61
|
return;
|
|
78
62
|
const childs = Children.toArray(children);
|
package/Transition/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/Transition/index.tsx"],"sourcesContent":["\"use client\";\nimport React, { cloneElement, Children, useRef, isValidElement, useLayoutEffect, useEffect } from 'react';\nimport * as variants from './variants'\nimport { Easing } from '../animate';\nimport useTransition from '../hooks/useTransition';\n\nexport type TransitionVariantTypes = keyof typeof variants\nexport type TransitionProps = {\n children: React.ReactElement;\n open: boolean;\n variant: TransitionVariantTypes\n easing?: keyof typeof Easing\n duration?: number;\n delay?: number;\n initialTransition?: boolean;\n\n exitOnUnmount?: boolean;\n\n onEnter?: () => void\n onEntered?: () => void\n onExit?: () => void\n onExited?: () => void\n onUpdate?: (value: Record<string, number>, progress: number) => void;\n onDone?: () => void;\n}\n\n\nfunction TransitionBase({ children, ...options }: TransitionProps) {\n let {\n open,\n variant = \"fade\",\n duration = 450,\n delay,\n easing,\n exitOnUnmount = false,\n initialTransition = true,\n onEnter,\n onEntered,\n onExit,\n onExited,\n onUpdate,\n onDone,\n\n } = options\n\n easing ??= \"default\"\n\n const variantCb = variants[variant]\n const ref = useRef<HTMLElement>(null)\n const init = useRef(false)\n const rect = useRef<DOMRect>(null)\n\n const trans = useTransition({\n delay,\n duration,\n easing: Easing[easing],\n onEnter,\n onEntered,\n onExit,\n onExited,\n onDone,\n from: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.from\n },\n to: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.to\n },\n onUpdate: (val, prograss) => {\n if (!ref.current || !rect.current) return\n const vc = variantCb(ref.current, rect.current)\n onUpdate?.(val, prograss)\n return vc.onUpdate(val)\n },\n })\n\n\n useLayoutEffect(() => {\n const isnot = !init.current && !initialTransition\n init.current = true\n if (open) {\n trans.enter(isnot ? false : true)\n } else {\n trans.exit(isnot ? false : true)\n }\n }, [open])\n\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/Transition/index.tsx"],"sourcesContent":["\"use client\";\nimport React, { cloneElement, Children, useRef, isValidElement, useLayoutEffect, useEffect } from 'react';\nimport * as variants from './variants'\nimport { Easing } from '../animate';\nimport useTransition from '../hooks/useTransition';\n\nexport type TransitionVariantTypes = keyof typeof variants\nexport type TransitionProps = {\n children: React.ReactElement;\n open: boolean;\n variant: TransitionVariantTypes\n easing?: keyof typeof Easing\n duration?: number;\n delay?: number;\n initialTransition?: boolean;\n\n exitOnUnmount?: boolean;\n\n onEnter?: () => void\n onEntered?: () => void\n onExit?: () => void\n onExited?: () => void\n onUpdate?: (value: Record<string, number>, progress: number) => void;\n onDone?: () => void;\n}\n\n\nfunction TransitionBase({ children, ...options }: TransitionProps) {\n let {\n open,\n variant = \"fade\",\n duration = 450,\n delay,\n easing,\n exitOnUnmount = false,\n initialTransition = true,\n onEnter,\n onEntered,\n onExit,\n onExited,\n onUpdate,\n onDone,\n\n } = options\n\n easing ??= \"default\"\n\n const variantCb = variants[variant]\n const ref = useRef<HTMLElement>(null)\n const init = useRef(false)\n const rect = useRef<DOMRect>(null)\n\n const trans = useTransition({\n delay,\n duration,\n easing: Easing[easing],\n onEnter,\n onEntered,\n onExit,\n onExited,\n onDone,\n from: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.from\n },\n to: () => {\n if (!rect.current) {\n rect.current = ref.current?.getBoundingClientRect() as DOMRect\n }\n const v = variantCb(ref.current as HTMLElement, rect.current)\n return v.to\n },\n onUpdate: (val, prograss) => {\n if (!ref.current || !rect.current) return\n const vc = variantCb(ref.current, rect.current)\n onUpdate?.(val, prograss)\n return vc.onUpdate(val)\n },\n })\n\n\n useLayoutEffect(() => {\n const isnot = !init.current && !initialTransition\n init.current = true\n if (open) {\n trans.enter(isnot ? false : true)\n } else {\n trans.exit(isnot ? false : true)\n }\n }, [open])\n\n\n if (exitOnUnmount && trans.status === \"exited\") return\n\n const childs = Children.toArray(children)\n if (childs.length !== 1) {\n throw new Error(\"Transition expects exactly one child.\");\n }\n const child = childs[0]\n if (!isValidElement(child)) {\n throw new Error(\"Transition expects a valid React element.\");\n }\n\n return cloneElement(child, {\n ref: (node: HTMLElement) => {\n ref.current = node;\n\n const childRef = (child as any).ref;\n if (typeof childRef === \"function\") {\n childRef(node);\n } else if (childRef) {\n childRef.current = node;\n }\n }\n } as any);\n}\n\n\nconst Transition = (props: any) => {\n return (\n <TransitionBase\n key={props.variant}\n {...props}\n />\n )\n}\nexport default Transition"],"names":[],"mappings":";;;;;;;;AA2BA;AAAwB;AACpB;;AAmBA;AACA;AACA;AACA;;;;AAKI;;;;;;;;AAOI;;;AAGA;;;;;AAIA;;;AAGA;;;AAGJ;;;AAEI;;AAEA;;AAEP;;;AAKG;;AAEI;;;AAEA;;AAER;AAGA;;;AAGA;AACI;;AAEJ;AACA;AACI;;;AAIA;AACI;AAEA;AACA;;;;AAGI;;;AAGJ;AACZ;AAGA;;AAOA;;"}
|
package/animate/index.cjs
CHANGED
|
@@ -9,27 +9,35 @@ const animate = ({ from, to, duration = 400, delay = 0, easing: easing$1 = easin
|
|
|
9
9
|
let rafId;
|
|
10
10
|
let cycle = 0;
|
|
11
11
|
let forward = true;
|
|
12
|
-
// Track triggered breakpoints
|
|
13
12
|
const triggered = {};
|
|
13
|
+
const lastValues = {};
|
|
14
14
|
const resolve = (val) => typeof val === "function" ? val() : val;
|
|
15
15
|
const getEased = (key, t) => {
|
|
16
|
+
let e;
|
|
16
17
|
if (typeof easing$1 === "function")
|
|
17
|
-
|
|
18
|
-
if (easing$1[key])
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
e = easing$1(t);
|
|
19
|
+
else if (easing$1[key])
|
|
20
|
+
e = easing$1[key](t);
|
|
21
|
+
else
|
|
22
|
+
e = t;
|
|
23
|
+
return Math.max(0, Math.min(1, e)); // clamp
|
|
21
24
|
};
|
|
22
25
|
const startAnimation = () => {
|
|
23
26
|
const fromVal = resolve(from);
|
|
24
27
|
const toVal = resolve(to);
|
|
25
28
|
const keys = Object.keys(fromVal);
|
|
26
29
|
if (breakpoints) {
|
|
27
|
-
for (const key of keys)
|
|
30
|
+
for (const key of keys) {
|
|
28
31
|
triggered[key] = new Set();
|
|
32
|
+
lastValues[key] = forward ? fromVal[key] : toVal[key];
|
|
33
|
+
}
|
|
29
34
|
}
|
|
35
|
+
// first frame exact start
|
|
36
|
+
onUpdate(Object.assign({}, fromVal), 0);
|
|
30
37
|
const start = performance.now();
|
|
31
38
|
const frame = (now) => {
|
|
32
|
-
const
|
|
39
|
+
const elapsed = now - start;
|
|
40
|
+
const progress = duration === 0 ? 1 : Math.min(elapsed / duration, 1);
|
|
33
41
|
const current = {};
|
|
34
42
|
for (const key of keys) {
|
|
35
43
|
const f = forward ? fromVal[key] : toVal[key];
|
|
@@ -37,50 +45,40 @@ const animate = ({ from, to, duration = 400, delay = 0, easing: easing$1 = easin
|
|
|
37
45
|
const eased = getEased(key, progress);
|
|
38
46
|
const val = f + (t - f) * eased;
|
|
39
47
|
current[key] = val;
|
|
40
|
-
// breakpoints
|
|
48
|
+
// ✅ breakpoints: only trigger if inside from..to
|
|
41
49
|
const bps = breakpoints === null || breakpoints === void 0 ? void 0 : breakpoints[key];
|
|
42
50
|
if (bps) {
|
|
51
|
+
const last = lastValues[key];
|
|
43
52
|
for (let i = 0; i < bps.length; i++) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
if (triggered[key].has(i))
|
|
54
|
+
continue;
|
|
55
|
+
const bp = bps[i].value;
|
|
56
|
+
// skip if breakpoint outside from..to
|
|
57
|
+
if (!((f < t && bp >= f && bp <= t) || (f > t && bp <= f && bp >= t)))
|
|
58
|
+
continue;
|
|
59
|
+
// trigger only if crossed this frame
|
|
60
|
+
if ((f < t && last < bp && val >= bp) || (f > t && last > bp && val <= bp)) {
|
|
61
|
+
triggered[key].add(i);
|
|
49
62
|
bps[i].callback();
|
|
50
63
|
}
|
|
51
64
|
}
|
|
65
|
+
lastValues[key] = val;
|
|
52
66
|
}
|
|
53
67
|
}
|
|
54
|
-
onUpdate(current, progress);
|
|
55
68
|
if (progress < 1) {
|
|
69
|
+
onUpdate(current, progress);
|
|
56
70
|
rafId = requestAnimationFrame(frame);
|
|
57
71
|
}
|
|
58
72
|
else {
|
|
59
73
|
const finalState = forward ? toVal : fromVal;
|
|
60
|
-
onUpdate(finalState, 1);
|
|
61
|
-
// fire remaining breakpoints
|
|
62
|
-
if (breakpoints) {
|
|
63
|
-
for (const key of keys) {
|
|
64
|
-
const bps = breakpoints[key];
|
|
65
|
-
if (!bps)
|
|
66
|
-
continue;
|
|
67
|
-
const triggeredSet = triggered[key];
|
|
68
|
-
for (let i = 0; i < bps.length; i++) {
|
|
69
|
-
if (!triggeredSet.has(i)) {
|
|
70
|
-
triggeredSet.add(i);
|
|
71
|
-
bps[i].callback();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
74
|
+
onUpdate(Object.assign({}, finalState), 1);
|
|
76
75
|
cycle++;
|
|
77
76
|
if (cycle <= repeat) {
|
|
78
77
|
if (repeatBack)
|
|
79
78
|
forward = !forward;
|
|
80
|
-
startAnimation();
|
|
79
|
+
startAnimation();
|
|
81
80
|
}
|
|
82
81
|
else {
|
|
83
|
-
const finalState = forward ? toVal : fromVal;
|
|
84
82
|
onDone === null || onDone === void 0 ? void 0 : onDone(finalState);
|
|
85
83
|
}
|
|
86
84
|
}
|
package/animate/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/animate/index.ts"],"sourcesContent":["\"use client\"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/animate/index.ts"],"sourcesContent":["\"use client\";\nimport Easing from \"./easing\";\n\nexport { Easing };\n\nexport type AnimateOptions<T extends Record<string, number>> = {\n from: T | (() => T);\n to: T | (() => T);\n duration?: number;\n delay?: number;\n easing?: ((t: number) => number) | Partial<Record<keyof T, (t: number) => number>>;\n onUpdate: (value: T, progress: number) => void;\n onDone?: (value: T) => void;\n breakpoints?: Partial<Record<keyof T, Array<{ value: number; callback: () => void }>>>;\n repeat?: number;\n repeatBack?: boolean;\n};\n\nconst animate = <T extends Record<string, number>>({\n from,\n to,\n duration = 400,\n delay = 0,\n easing = Easing.default,\n onUpdate,\n onDone,\n breakpoints,\n repeat = 0,\n repeatBack = false,\n}: AnimateOptions<T>) => {\n let rafId: number;\n let cycle = 0;\n let forward = true;\n\n const triggered: Partial<Record<keyof T, Set<number>>> = {};\n const lastValues: Partial<Record<keyof T, number>> = {};\n\n const resolve = (val: T | (() => T)): T =>\n typeof val === \"function\" ? (val as () => T)() : val;\n\n const getEased = (key: keyof T, t: number) => {\n let e: number;\n if (typeof easing === \"function\") e = easing(t);\n else if (easing[key]) e = easing[key]!(t);\n else e = t;\n return Math.max(0, Math.min(1, e)); // clamp\n };\n\n const startAnimation = () => {\n const fromVal = resolve(from);\n const toVal = resolve(to);\n\n const keys = Object.keys(fromVal) as (keyof T)[];\n\n if (breakpoints) {\n for (const key of keys) {\n triggered[key] = new Set();\n lastValues[key] = forward ? fromVal[key] : toVal[key];\n }\n }\n\n // first frame exact start\n onUpdate({ ...fromVal }, 0);\n\n const start = performance.now();\n\n const frame = (now: number) => {\n const elapsed = now - start;\n const progress = duration === 0 ? 1 : Math.min(elapsed / duration, 1);\n\n const current = {} as T;\n\n for (const key of keys) {\n const f = forward ? fromVal[key] : toVal[key];\n const t = forward ? toVal[key] : fromVal[key];\n\n const eased = getEased(key, progress);\n const val = f + (t - f) * eased;\n\n (current as any)[key] = val;\n\n // ✅ breakpoints: only trigger if inside from..to\n const bps = breakpoints?.[key];\n if (bps) {\n const last = lastValues[key]!;\n for (let i = 0; i < bps.length; i++) {\n if (triggered[key]!.has(i)) continue;\n\n const bp = bps[i].value;\n\n // skip if breakpoint outside from..to\n if (!((f < t && bp >= f && bp <= t) || (f > t && bp <= f && bp >= t))) continue;\n\n // trigger only if crossed this frame\n if ((f < t && last < bp && val >= bp) || (f > t && last > bp && val <= bp)) {\n triggered[key]!.add(i);\n bps[i].callback();\n }\n }\n\n lastValues[key] = val;\n }\n }\n\n if (progress < 1) {\n onUpdate(current, progress);\n rafId = requestAnimationFrame(frame);\n } else {\n const finalState = forward ? toVal : fromVal;\n onUpdate({ ...finalState }, 1);\n\n cycle++;\n if (cycle <= repeat) {\n if (repeatBack) forward = !forward;\n startAnimation();\n } else {\n onDone?.(finalState);\n }\n }\n };\n\n rafId = requestAnimationFrame(frame);\n };\n\n if (delay > 0) {\n const timeout = setTimeout(startAnimation, delay);\n return () => {\n clearTimeout(timeout);\n cancelAnimationFrame(rafId);\n };\n } else {\n startAnimation();\n return () => cancelAnimationFrame(rafId);\n }\n};\n\nexport default animate;"],"names":[],"mappings":";;;;;;;AAkBA;AAYG;;;;;;AAUA;AACG;;AACkC;;;;;AAGlC;AACH;;AAGG;AACA;;;AAKG;AACG;AACA;;;;AAKN;AAEA;AAEA;AACG;;;AAKA;AACG;AACA;;;AAKC;;;;AAKE;AACA;;;;;AAMG;;;AAGA;;AAEG;;;AAIN;;;AAIN;AACG;AACA;;;;AAGA;AAEA;AACA;AACG;;AACA;;;AAEA;;;AAGT;AAEA;AACH;AAEA;;AAEG;;;AAGA;;;AAEA;AACA;;AAEN;;;"}
|
package/animate/index.js
CHANGED
|
@@ -5,27 +5,35 @@ const animate = ({ from, to, duration = 400, delay = 0, easing = Easing.default,
|
|
|
5
5
|
let rafId;
|
|
6
6
|
let cycle = 0;
|
|
7
7
|
let forward = true;
|
|
8
|
-
// Track triggered breakpoints
|
|
9
8
|
const triggered = {};
|
|
9
|
+
const lastValues = {};
|
|
10
10
|
const resolve = (val) => typeof val === "function" ? val() : val;
|
|
11
11
|
const getEased = (key, t) => {
|
|
12
|
+
let e;
|
|
12
13
|
if (typeof easing === "function")
|
|
13
|
-
|
|
14
|
-
if (easing[key])
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
e = easing(t);
|
|
15
|
+
else if (easing[key])
|
|
16
|
+
e = easing[key](t);
|
|
17
|
+
else
|
|
18
|
+
e = t;
|
|
19
|
+
return Math.max(0, Math.min(1, e)); // clamp
|
|
17
20
|
};
|
|
18
21
|
const startAnimation = () => {
|
|
19
22
|
const fromVal = resolve(from);
|
|
20
23
|
const toVal = resolve(to);
|
|
21
24
|
const keys = Object.keys(fromVal);
|
|
22
25
|
if (breakpoints) {
|
|
23
|
-
for (const key of keys)
|
|
26
|
+
for (const key of keys) {
|
|
24
27
|
triggered[key] = new Set();
|
|
28
|
+
lastValues[key] = forward ? fromVal[key] : toVal[key];
|
|
29
|
+
}
|
|
25
30
|
}
|
|
31
|
+
// first frame exact start
|
|
32
|
+
onUpdate(Object.assign({}, fromVal), 0);
|
|
26
33
|
const start = performance.now();
|
|
27
34
|
const frame = (now) => {
|
|
28
|
-
const
|
|
35
|
+
const elapsed = now - start;
|
|
36
|
+
const progress = duration === 0 ? 1 : Math.min(elapsed / duration, 1);
|
|
29
37
|
const current = {};
|
|
30
38
|
for (const key of keys) {
|
|
31
39
|
const f = forward ? fromVal[key] : toVal[key];
|
|
@@ -33,50 +41,40 @@ const animate = ({ from, to, duration = 400, delay = 0, easing = Easing.default,
|
|
|
33
41
|
const eased = getEased(key, progress);
|
|
34
42
|
const val = f + (t - f) * eased;
|
|
35
43
|
current[key] = val;
|
|
36
|
-
// breakpoints
|
|
44
|
+
// ✅ breakpoints: only trigger if inside from..to
|
|
37
45
|
const bps = breakpoints === null || breakpoints === void 0 ? void 0 : breakpoints[key];
|
|
38
46
|
if (bps) {
|
|
47
|
+
const last = lastValues[key];
|
|
39
48
|
for (let i = 0; i < bps.length; i++) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
if (triggered[key].has(i))
|
|
50
|
+
continue;
|
|
51
|
+
const bp = bps[i].value;
|
|
52
|
+
// skip if breakpoint outside from..to
|
|
53
|
+
if (!((f < t && bp >= f && bp <= t) || (f > t && bp <= f && bp >= t)))
|
|
54
|
+
continue;
|
|
55
|
+
// trigger only if crossed this frame
|
|
56
|
+
if ((f < t && last < bp && val >= bp) || (f > t && last > bp && val <= bp)) {
|
|
57
|
+
triggered[key].add(i);
|
|
45
58
|
bps[i].callback();
|
|
46
59
|
}
|
|
47
60
|
}
|
|
61
|
+
lastValues[key] = val;
|
|
48
62
|
}
|
|
49
63
|
}
|
|
50
|
-
onUpdate(current, progress);
|
|
51
64
|
if (progress < 1) {
|
|
65
|
+
onUpdate(current, progress);
|
|
52
66
|
rafId = requestAnimationFrame(frame);
|
|
53
67
|
}
|
|
54
68
|
else {
|
|
55
69
|
const finalState = forward ? toVal : fromVal;
|
|
56
|
-
onUpdate(finalState, 1);
|
|
57
|
-
// fire remaining breakpoints
|
|
58
|
-
if (breakpoints) {
|
|
59
|
-
for (const key of keys) {
|
|
60
|
-
const bps = breakpoints[key];
|
|
61
|
-
if (!bps)
|
|
62
|
-
continue;
|
|
63
|
-
const triggeredSet = triggered[key];
|
|
64
|
-
for (let i = 0; i < bps.length; i++) {
|
|
65
|
-
if (!triggeredSet.has(i)) {
|
|
66
|
-
triggeredSet.add(i);
|
|
67
|
-
bps[i].callback();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
70
|
+
onUpdate(Object.assign({}, finalState), 1);
|
|
72
71
|
cycle++;
|
|
73
72
|
if (cycle <= repeat) {
|
|
74
73
|
if (repeatBack)
|
|
75
74
|
forward = !forward;
|
|
76
|
-
startAnimation();
|
|
75
|
+
startAnimation();
|
|
77
76
|
}
|
|
78
77
|
else {
|
|
79
|
-
const finalState = forward ? toVal : fromVal;
|
|
80
78
|
onDone === null || onDone === void 0 ? void 0 : onDone(finalState);
|
|
81
79
|
}
|
|
82
80
|
}
|
package/animate/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/animate/index.ts"],"sourcesContent":["\"use client\"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/animate/index.ts"],"sourcesContent":["\"use client\";\nimport Easing from \"./easing\";\n\nexport { Easing };\n\nexport type AnimateOptions<T extends Record<string, number>> = {\n from: T | (() => T);\n to: T | (() => T);\n duration?: number;\n delay?: number;\n easing?: ((t: number) => number) | Partial<Record<keyof T, (t: number) => number>>;\n onUpdate: (value: T, progress: number) => void;\n onDone?: (value: T) => void;\n breakpoints?: Partial<Record<keyof T, Array<{ value: number; callback: () => void }>>>;\n repeat?: number;\n repeatBack?: boolean;\n};\n\nconst animate = <T extends Record<string, number>>({\n from,\n to,\n duration = 400,\n delay = 0,\n easing = Easing.default,\n onUpdate,\n onDone,\n breakpoints,\n repeat = 0,\n repeatBack = false,\n}: AnimateOptions<T>) => {\n let rafId: number;\n let cycle = 0;\n let forward = true;\n\n const triggered: Partial<Record<keyof T, Set<number>>> = {};\n const lastValues: Partial<Record<keyof T, number>> = {};\n\n const resolve = (val: T | (() => T)): T =>\n typeof val === \"function\" ? (val as () => T)() : val;\n\n const getEased = (key: keyof T, t: number) => {\n let e: number;\n if (typeof easing === \"function\") e = easing(t);\n else if (easing[key]) e = easing[key]!(t);\n else e = t;\n return Math.max(0, Math.min(1, e)); // clamp\n };\n\n const startAnimation = () => {\n const fromVal = resolve(from);\n const toVal = resolve(to);\n\n const keys = Object.keys(fromVal) as (keyof T)[];\n\n if (breakpoints) {\n for (const key of keys) {\n triggered[key] = new Set();\n lastValues[key] = forward ? fromVal[key] : toVal[key];\n }\n }\n\n // first frame exact start\n onUpdate({ ...fromVal }, 0);\n\n const start = performance.now();\n\n const frame = (now: number) => {\n const elapsed = now - start;\n const progress = duration === 0 ? 1 : Math.min(elapsed / duration, 1);\n\n const current = {} as T;\n\n for (const key of keys) {\n const f = forward ? fromVal[key] : toVal[key];\n const t = forward ? toVal[key] : fromVal[key];\n\n const eased = getEased(key, progress);\n const val = f + (t - f) * eased;\n\n (current as any)[key] = val;\n\n // ✅ breakpoints: only trigger if inside from..to\n const bps = breakpoints?.[key];\n if (bps) {\n const last = lastValues[key]!;\n for (let i = 0; i < bps.length; i++) {\n if (triggered[key]!.has(i)) continue;\n\n const bp = bps[i].value;\n\n // skip if breakpoint outside from..to\n if (!((f < t && bp >= f && bp <= t) || (f > t && bp <= f && bp >= t))) continue;\n\n // trigger only if crossed this frame\n if ((f < t && last < bp && val >= bp) || (f > t && last > bp && val <= bp)) {\n triggered[key]!.add(i);\n bps[i].callback();\n }\n }\n\n lastValues[key] = val;\n }\n }\n\n if (progress < 1) {\n onUpdate(current, progress);\n rafId = requestAnimationFrame(frame);\n } else {\n const finalState = forward ? toVal : fromVal;\n onUpdate({ ...finalState }, 1);\n\n cycle++;\n if (cycle <= repeat) {\n if (repeatBack) forward = !forward;\n startAnimation();\n } else {\n onDone?.(finalState);\n }\n }\n };\n\n rafId = requestAnimationFrame(frame);\n };\n\n if (delay > 0) {\n const timeout = setTimeout(startAnimation, delay);\n return () => {\n clearTimeout(timeout);\n cancelAnimationFrame(rafId);\n };\n } else {\n startAnimation();\n return () => cancelAnimationFrame(rafId);\n }\n};\n\nexport default animate;"],"names":[],"mappings":";;;AAkBA;AAYG;;;;;;AAUA;AACG;;AACkC;;;;;AAGlC;AACH;;AAGG;AACA;;;AAKG;AACG;AACA;;;;AAKN;AAEA;AAEA;AACG;;;AAKA;AACG;AACA;;;AAKC;;;;AAKE;AACA;;;;;AAMG;;;AAGA;;AAEG;;;AAIN;;;AAIN;AACG;AACA;;;;AAGA;AAEA;AACA;AACG;;AACA;;;AAEA;;;AAGT;AAEA;AACH;AAEA;;AAEG;;;AAGA;;;AAEA;AACA;;AAEN;;"}
|