pixi-solid 0.0.29 → 0.0.30
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/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/smooth-damp.d.ts +36 -0
- package/dist/types/utils/spring.d.ts +26 -0
- package/dist/utils/index.js +5 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/object-fit.js.map +1 -1
- package/dist/utils/smooth-damp.js +54 -0
- package/dist/utils/smooth-damp.js.map +1 -0
- package/dist/utils/spring.js +32 -0
- package/dist/utils/spring.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Accessor } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* Smoothly dampens a value towards a target over time.
|
|
4
|
+
* This is similar to Unity's Mathf.SmoothDamp function.
|
|
5
|
+
*
|
|
6
|
+
* @param current - The current value.
|
|
7
|
+
* @param target - The target value.
|
|
8
|
+
* @param velocity - A reference to the current velocity. This should be persistent between calls.
|
|
9
|
+
* @param smoothTimeMs - The approximate time it will take to reach the target in milliseconds. Smaller values will reach the target faster.
|
|
10
|
+
* @param maxSpeed - Optionally, the maximum speed the value can move in units per second. Defaults to Infinity.
|
|
11
|
+
* @param deltaTime - The time since the last call in seconds. Defaults to 1/60 (assuming 60 FPS).
|
|
12
|
+
* @param precision - Optionally, the threshold for snapping the value to the target. If the absolute difference between the current and target value is less than this, it snaps to the target. Defaults to 0.01.
|
|
13
|
+
* @returns The new current value.
|
|
14
|
+
*/
|
|
15
|
+
export declare const smoothDamp: (current: number, target: number, velocity: {
|
|
16
|
+
value: number;
|
|
17
|
+
}, smoothTimeMs?: number, maxSpeed?: number, deltaTime?: number, precision?: number) => number;
|
|
18
|
+
/**
|
|
19
|
+
* A SolidJS hook that provides a smoothly damped signal towards a target value.
|
|
20
|
+
* Internally manages velocity and continuous updates synced to the Pixi ticker.
|
|
21
|
+
*
|
|
22
|
+
* @param to - An accessor for the target value to damp towards.
|
|
23
|
+
* @param smoothTimeMs - The approximate time it will take to reach the target in milliseconds. Smaller values will reach the target faster. Defaults to 300ms.
|
|
24
|
+
* @param maxSpeed - Optionally, the maximum speed the value can move in units per second. Defaults to Infinity.
|
|
25
|
+
* @returns A signal containing the current damped value.
|
|
26
|
+
*/
|
|
27
|
+
export type UseSmoothDampProps = {
|
|
28
|
+
to: () => number;
|
|
29
|
+
smoothTimeMs?: () => number;
|
|
30
|
+
maxSpeed?: () => number;
|
|
31
|
+
};
|
|
32
|
+
export type SmoothDamp = {
|
|
33
|
+
value: Accessor<number>;
|
|
34
|
+
velocity: Accessor<number>;
|
|
35
|
+
};
|
|
36
|
+
export declare const useSmoothDamp: (props: UseSmoothDampProps) => SmoothDamp;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Accessor } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {Object} UseSpringProps
|
|
4
|
+
* @property {Accessor<number>} to - An accessor for the target value for the spring.
|
|
5
|
+
* @property {Accessor<number>} [stiffness=10] - Effective range from 0 - 100. Controls the spring's resistance to displacement.
|
|
6
|
+
* @property {Accessor<number>} [damping=30] - Effective range from 0 - 100. Controls the amount of friction or resistance to motion.
|
|
7
|
+
* @property {Accessor<number>} [mass=20] - Effective range from 0 - 100. Controls the inertia of the spring.
|
|
8
|
+
*/
|
|
9
|
+
export type UseSpringProps = {
|
|
10
|
+
to: () => number;
|
|
11
|
+
stiffness?: () => number;
|
|
12
|
+
damping?: () => number;
|
|
13
|
+
mass?: () => number;
|
|
14
|
+
};
|
|
15
|
+
export type Spring = {
|
|
16
|
+
value: Accessor<number>;
|
|
17
|
+
velocity: Accessor<number>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A SolidJS hook that provides a spring-animated signal towards a target value.
|
|
21
|
+
* Internally manages the spring physics and continuous updates synced to the Pixi ticker.
|
|
22
|
+
*
|
|
23
|
+
* @param {UseSpringProps} props - The properties for the spring animation.
|
|
24
|
+
* @returns {Accessor<number>} A signal containing the current spring-animated value.
|
|
25
|
+
*/
|
|
26
|
+
export declare const useSpring: (props: UseSpringProps) => Spring;
|
package/dist/utils/index.js
CHANGED
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-fit.js","sources":["../../src/utils/object-fit.ts"],"sourcesContent":["import type * as Pixi from \"pixi.js\";\n\nexport type ObjectFitMode = \"cover\" | \"contain\" | \"fill\" | \"scale-down\";\n\n/**\n * Scale an object to fit within the given bounds according to the specified fit mode.\n * @param object The object to be scaled.\n * @param bounds The bounds it should fit within.\n * @param fitMode The object fit mode to apply.\n */\nexport const objectFit = (\n object: Pixi.Container,\n bounds: { width: number; height: number },\n fitMode: ObjectFitMode,\n): void => {\n const originalWidth = object.width / object.scale.x;\n const originalHeight = object.height / object.scale.y;\n\n if (originalWidth === 0 || originalHeight === 0 || bounds.width === 0 || bounds.height === 0)\n return;\n\n const widthRatio = bounds.width / originalWidth;\n const heightRatio = bounds.height / originalHeight;\n\n let scaleX = 1;\n let scaleY = 1;\n\n switch (fitMode) {\n case \"cover\": {\n const coverScale = Math.max(widthRatio, heightRatio);\n scaleX = coverScale;\n scaleY = coverScale;\n break;\n }\n case \"contain\": {\n const containScale = Math.min(widthRatio, heightRatio);\n scaleX = containScale;\n scaleY = containScale;\n break;\n }\n case \"fill\": {\n scaleX = widthRatio;\n scaleY = heightRatio;\n break;\n }\n case \"scale-down\": {\n // If the object is smaller than the container, it's 'none' (no scaling up).\n // Otherwise, it's 'contain'.\n if (originalWidth <= bounds.width && originalHeight <= bounds.height) {\n scaleX = 1;\n scaleY = 1;\n } else {\n const scaleDown = Math.min(widthRatio, heightRatio);\n scaleX = scaleDown;\n scaleY = scaleDown;\n }\n break;\n }\n default:\n // Default to no scaling if an unknown fitMode is provided\n break;\n }\n\n object.scale.set(scaleX, scaleY);\n\n // Center the object\n object.x = (bounds.width - object.width) / 2;\n object.y = (bounds.height - object.height) / 2;\n};\n"],"names":[],"mappings":"AAUO,MAAM,YAAY,CACvB,QACA,QACA,YACS;AACT,QAAM,gBAAgB,OAAO,QAAQ,OAAO,MAAM;AAClD,QAAM,iBAAiB,OAAO,SAAS,OAAO,MAAM;AAEpD,MAAI,kBAAkB,KAAK,mBAAmB,KAAK,OAAO,UAAU,KAAK,OAAO,WAAW;AACzF;AAEF,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,cAAc,OAAO,SAAS;AAEpC,MAAI,SAAS;AACb,MAAI,SAAS;
|
|
1
|
+
{"version":3,"file":"object-fit.js","sources":["../../src/utils/object-fit.ts"],"sourcesContent":["import type * as Pixi from \"pixi.js\";\n\nexport type ObjectFitMode = \"cover\" | \"contain\" | \"fill\" | \"scale-down\";\n\n/**\n * Scale an object to fit within the given bounds according to the specified fit mode.\n * @param object The object to be scaled.\n * @param bounds The bounds it should fit within.\n * @param fitMode The object fit mode to apply.\n */\nexport const objectFit = (\n object: Pixi.Container,\n bounds: { width: number; height: number },\n fitMode: ObjectFitMode,\n): void => {\n const originalWidth = object.width / object.scale.x;\n const originalHeight = object.height / object.scale.y;\n\n if (originalWidth === 0 || originalHeight === 0 || bounds.width === 0 || bounds.height === 0)\n return;\n\n const widthRatio = bounds.width / originalWidth;\n const heightRatio = bounds.height / originalHeight;\n\n let scaleX = 1;\n let scaleY = 1;\n\n // biome-ignore lint/nursery/noUnnecessaryConditions: <explanation>\n switch (fitMode) {\n case \"cover\": {\n const coverScale = Math.max(widthRatio, heightRatio);\n scaleX = coverScale;\n scaleY = coverScale;\n break;\n }\n case \"contain\": {\n const containScale = Math.min(widthRatio, heightRatio);\n scaleX = containScale;\n scaleY = containScale;\n break;\n }\n case \"fill\": {\n scaleX = widthRatio;\n scaleY = heightRatio;\n break;\n }\n case \"scale-down\": {\n // If the object is smaller than the container, it's 'none' (no scaling up).\n // Otherwise, it's 'contain'.\n if (originalWidth <= bounds.width && originalHeight <= bounds.height) {\n scaleX = 1;\n scaleY = 1;\n } else {\n const scaleDown = Math.min(widthRatio, heightRatio);\n scaleX = scaleDown;\n scaleY = scaleDown;\n }\n break;\n }\n default:\n // Default to no scaling if an unknown fitMode is provided\n break;\n }\n\n object.scale.set(scaleX, scaleY);\n\n // Center the object\n object.x = (bounds.width - object.width) / 2;\n object.y = (bounds.height - object.height) / 2;\n};\n"],"names":[],"mappings":"AAUO,MAAM,YAAY,CACvB,QACA,QACA,YACS;AACT,QAAM,gBAAgB,OAAO,QAAQ,OAAO,MAAM;AAClD,QAAM,iBAAiB,OAAO,SAAS,OAAO,MAAM;AAEpD,MAAI,kBAAkB,KAAK,mBAAmB,KAAK,OAAO,UAAU,KAAK,OAAO,WAAW;AACzF;AAEF,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,cAAc,OAAO,SAAS;AAEpC,MAAI,SAAS;AACb,MAAI,SAAS;AAGb,UAAQ,SAAA;AAAA,IACN,KAAK,SAAS;AACZ,YAAM,aAAa,KAAK,IAAI,YAAY,WAAW;AACnD,eAAS;AACT,eAAS;AACT;AAAA,IACF;AAAA,IACA,KAAK,WAAW;AACd,YAAM,eAAe,KAAK,IAAI,YAAY,WAAW;AACrD,eAAS;AACT,eAAS;AACT;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,eAAS;AACT,eAAS;AACT;AAAA,IACF;AAAA,IACA,KAAK,cAAc;AAGjB,UAAI,iBAAiB,OAAO,SAAS,kBAAkB,OAAO,QAAQ;AACpE,iBAAS;AACT,iBAAS;AAAA,MACX,OAAO;AACL,cAAM,YAAY,KAAK,IAAI,YAAY,WAAW;AAClD,iBAAS;AACT,iBAAS;AAAA,MACX;AACA;AAAA,IACF;AAAA,EAGE;AAGJ,SAAO,MAAM,IAAI,QAAQ,MAAM;AAG/B,SAAO,KAAK,OAAO,QAAQ,OAAO,SAAS;AAC3C,SAAO,KAAK,OAAO,SAAS,OAAO,UAAU;AAC/C;"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createSignal } from "solid-js";
|
|
2
|
+
import { createMutable } from "solid-js/store";
|
|
3
|
+
import { onTick } from "../pixi-application.js";
|
|
4
|
+
const smoothDamp = (current, target, velocity, smoothTimeMs = 300, maxSpeed = Infinity, deltaTime = 1 / 60, precision = 0.01) => {
|
|
5
|
+
if (Math.abs(current - target) < precision && Math.abs(velocity.value) < precision) {
|
|
6
|
+
velocity.value = 0;
|
|
7
|
+
return target;
|
|
8
|
+
}
|
|
9
|
+
const smoothTime = smoothTimeMs / 1e3;
|
|
10
|
+
const omega = 2 / smoothTime;
|
|
11
|
+
const x = omega * deltaTime;
|
|
12
|
+
const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
|
|
13
|
+
const change = current - target;
|
|
14
|
+
const temp = (velocity.value + omega * change) * deltaTime;
|
|
15
|
+
velocity.value = (velocity.value - omega * temp) * exp;
|
|
16
|
+
let result = target + (change + temp) * exp;
|
|
17
|
+
if (maxSpeed !== Infinity) {
|
|
18
|
+
const maxChange = maxSpeed * deltaTime;
|
|
19
|
+
result = current + Math.max(-maxChange, Math.min(maxChange, result - current));
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
};
|
|
23
|
+
const useSmoothDamp = (props) => {
|
|
24
|
+
const [current, setCurrent] = createSignal(props.to());
|
|
25
|
+
const velocity = createMutable({ value: 0 });
|
|
26
|
+
const update = (deltaTimeMS) => {
|
|
27
|
+
if (current() === props.to()) {
|
|
28
|
+
velocity.value = 0;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const deltaTime = deltaTimeMS / 1e3;
|
|
32
|
+
const newCurrent = smoothDamp(
|
|
33
|
+
current(),
|
|
34
|
+
props.to(),
|
|
35
|
+
velocity,
|
|
36
|
+
props.smoothTimeMs?.(),
|
|
37
|
+
props.maxSpeed?.(),
|
|
38
|
+
deltaTime
|
|
39
|
+
);
|
|
40
|
+
setCurrent(newCurrent);
|
|
41
|
+
};
|
|
42
|
+
onTick((ticker) => {
|
|
43
|
+
update(ticker.deltaMS);
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
value: current,
|
|
47
|
+
velocity: () => velocity.value
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export {
|
|
51
|
+
smoothDamp,
|
|
52
|
+
useSmoothDamp
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=smooth-damp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smooth-damp.js","sources":["../../src/utils/smooth-damp.ts"],"sourcesContent":["import type { Accessor } from \"solid-js\";\nimport { createSignal } from \"solid-js\";\nimport { createMutable } from \"solid-js/store\";\nimport { onTick } from \"../pixi-application\";\n\n/**\n * Smoothly dampens a value towards a target over time.\n * This is similar to Unity's Mathf.SmoothDamp function.\n *\n * @param current - The current value.\n * @param target - The target value.\n * @param velocity - A reference to the current velocity. This should be persistent between calls.\n * @param smoothTimeMs - The approximate time it will take to reach the target in milliseconds. Smaller values will reach the target faster.\n * @param maxSpeed - Optionally, the maximum speed the value can move in units per second. Defaults to Infinity.\n * @param deltaTime - The time since the last call in seconds. Defaults to 1/60 (assuming 60 FPS).\n * @param precision - Optionally, the threshold for snapping the value to the target. If the absolute difference between the current and target value is less than this, it snaps to the target. Defaults to 0.01.\n * @returns The new current value.\n */\nexport const smoothDamp = (\n current: number,\n target: number,\n velocity: { value: number },\n smoothTimeMs: number = 300,\n maxSpeed: number = Infinity,\n deltaTime: number = 1 / 60,\n precision: number = 0.01, // Added precision parameter with a default\n): number => {\n // Check precision and velocity before doing calculations\n if (Math.abs(current - target) < precision && Math.abs(velocity.value) < precision) {\n velocity.value = 0; // Reset velocity\n return target; // Snap to target and return early\n }\n\n const smoothTime = smoothTimeMs / 1000; // Convert smoothTimeMs to seconds\n const omega = 2 / smoothTime;\n const x = omega * deltaTime;\n const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);\n const change = current - target;\n const temp = (velocity.value + omega * change) * deltaTime;\n velocity.value = (velocity.value - omega * temp) * exp;\n\n let result = target + (change + temp) * exp;\n\n // Clamp to max speed\n if (maxSpeed !== Infinity) {\n const maxChange = maxSpeed * deltaTime;\n result = current + Math.max(-maxChange, Math.min(maxChange, result - current));\n }\n\n return result;\n};\n\n/**\n * A SolidJS hook that provides a smoothly damped signal towards a target value.\n * Internally manages velocity and continuous updates synced to the Pixi ticker.\n *\n * @param to - An accessor for the target value to damp towards.\n * @param smoothTimeMs - The approximate time it will take to reach the target in milliseconds. Smaller values will reach the target faster. Defaults to 300ms.\n * @param maxSpeed - Optionally, the maximum speed the value can move in units per second. Defaults to Infinity.\n * @returns A signal containing the current damped value.\n */\n\nexport type UseSmoothDampProps = {\n to: () => number;\n smoothTimeMs?: () => number;\n maxSpeed?: () => number;\n};\n\nexport type SmoothDamp = {\n value: Accessor<number>;\n velocity: Accessor<number>;\n};\n\nexport const useSmoothDamp = (props: UseSmoothDampProps): SmoothDamp => {\n const [current, setCurrent] = createSignal(props.to());\n const velocity = createMutable({ value: 0 });\n\n const update = (deltaTimeMS: number) => {\n if (current() === props.to()) {\n velocity.value = 0;\n return;\n }\n\n const deltaTime = deltaTimeMS / 1000; // Convert milliseconds to seconds\n const newCurrent = smoothDamp(\n current(),\n props.to(),\n velocity,\n props.smoothTimeMs?.(),\n props.maxSpeed?.(),\n deltaTime,\n );\n setCurrent(newCurrent);\n };\n\n onTick((ticker) => {\n update(ticker.deltaMS);\n });\n\n return {\n value: current,\n velocity: () => velocity.value,\n };\n};\n"],"names":[],"mappings":";;;AAkBO,MAAM,aAAa,CACxB,SACA,QACA,UACA,eAAuB,KACvB,WAAmB,UACnB,YAAoB,IAAI,IACxB,YAAoB,SACT;AAEX,MAAI,KAAK,IAAI,UAAU,MAAM,IAAI,aAAa,KAAK,IAAI,SAAS,KAAK,IAAI,WAAW;AAClF,aAAS,QAAQ;AACjB,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,eAAe;AAClC,QAAM,QAAQ,IAAI;AAClB,QAAM,IAAI,QAAQ;AAClB,QAAM,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,IAAI,QAAQ,IAAI,IAAI;AACxD,QAAM,SAAS,UAAU;AACzB,QAAM,QAAQ,SAAS,QAAQ,QAAQ,UAAU;AACjD,WAAS,SAAS,SAAS,QAAQ,QAAQ,QAAQ;AAEnD,MAAI,SAAS,UAAU,SAAS,QAAQ;AAGxC,MAAI,aAAa,UAAU;AACzB,UAAM,YAAY,WAAW;AAC7B,aAAS,UAAU,KAAK,IAAI,CAAC,WAAW,KAAK,IAAI,WAAW,SAAS,OAAO,CAAC;AAAA,EAC/E;AAEA,SAAO;AACT;AAuBO,MAAM,gBAAgB,CAAC,UAA0C;AACtE,QAAM,CAAC,SAAS,UAAU,IAAI,aAAa,MAAM,IAAI;AACrD,QAAM,WAAW,cAAc,EAAE,OAAO,GAAG;AAE3C,QAAM,SAAS,CAAC,gBAAwB;AACtC,QAAI,QAAA,MAAc,MAAM,MAAM;AAC5B,eAAS,QAAQ;AACjB;AAAA,IACF;AAEA,UAAM,YAAY,cAAc;AAChC,UAAM,aAAa;AAAA,MACjB,QAAA;AAAA,MACA,MAAM,GAAA;AAAA,MACN;AAAA,MACA,MAAM,eAAA;AAAA,MACN,MAAM,WAAA;AAAA,MACN;AAAA,IAAA;AAEF,eAAW,UAAU;AAAA,EACvB;AAEA,SAAO,CAAC,WAAW;AACjB,WAAO,OAAO,OAAO;AAAA,EACvB,CAAC;AAED,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU,MAAM,SAAS;AAAA,EAAA;AAE7B;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createSignal } from "solid-js";
|
|
2
|
+
import { onTick } from "../pixi-application.js";
|
|
3
|
+
const useSpring = (props) => {
|
|
4
|
+
const [value, setValue] = createSignal(props.to());
|
|
5
|
+
const [velocity, setVelocity] = createSignal(0);
|
|
6
|
+
const update = (deltaTimeMS) => {
|
|
7
|
+
const targetInput = props.to();
|
|
8
|
+
if (Math.abs(velocity()) < 0.1 && Math.abs(value() - targetInput) < 0.01) {
|
|
9
|
+
setVelocity(0);
|
|
10
|
+
setValue(targetInput);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const deltaTime = deltaTimeMS / 1e3;
|
|
14
|
+
const stiffness = percentToValueBetweenRange(props.stiffness?.() ?? 10, -1, -300);
|
|
15
|
+
const damping = percentToValueBetweenRange(props.damping?.() ?? 30, -0.4, -20);
|
|
16
|
+
const mass = percentToValueBetweenRange(props.mass?.() ?? 20, 0.1, 10);
|
|
17
|
+
const springX = stiffness * (value() - targetInput);
|
|
18
|
+
const damperX = damping * velocity();
|
|
19
|
+
const amplitude = (springX + damperX) / mass;
|
|
20
|
+
setVelocity((prev) => prev + amplitude * deltaTime);
|
|
21
|
+
setValue(value() + velocity() * deltaTime);
|
|
22
|
+
};
|
|
23
|
+
onTick((ticker) => {
|
|
24
|
+
update(ticker.deltaMS);
|
|
25
|
+
});
|
|
26
|
+
return { value, velocity };
|
|
27
|
+
};
|
|
28
|
+
const percentToValueBetweenRange = (percent, min, max) => percent * (max - min) / 100 + min;
|
|
29
|
+
export {
|
|
30
|
+
useSpring
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=spring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spring.js","sources":["../../src/utils/spring.ts"],"sourcesContent":["import type { Accessor } from \"solid-js\";\nimport { createSignal } from \"solid-js\";\nimport { onTick } from \"../pixi-application\";\n\n/**\n * @typedef {Object} UseSpringProps\n * @property {Accessor<number>} to - An accessor for the target value for the spring.\n * @property {Accessor<number>} [stiffness=10] - Effective range from 0 - 100. Controls the spring's resistance to displacement.\n * @property {Accessor<number>} [damping=30] - Effective range from 0 - 100. Controls the amount of friction or resistance to motion.\n * @property {Accessor<number>} [mass=20] - Effective range from 0 - 100. Controls the inertia of the spring.\n */\nexport type UseSpringProps = {\n to: () => number;\n stiffness?: () => number;\n damping?: () => number;\n mass?: () => number;\n};\n\nexport type Spring = {\n value: Accessor<number>;\n velocity: Accessor<number>;\n};\n\n/**\n * A SolidJS hook that provides a spring-animated signal towards a target value.\n * Internally manages the spring physics and continuous updates synced to the Pixi ticker.\n *\n * @param {UseSpringProps} props - The properties for the spring animation.\n * @returns {Accessor<number>} A signal containing the current spring-animated value.\n */\nexport const useSpring = (props: UseSpringProps): Spring => {\n const [value, setValue] = createSignal(props.to());\n const [velocity, setVelocity] = createSignal(0);\n\n const update = (deltaTimeMS: number) => {\n const targetInput = props.to();\n\n // Settling condition: if output is very close to input and velocity is negligible,\n // snap to input and stop calculations.\n if (Math.abs(velocity()) < 0.1 && Math.abs(value() - targetInput) < 0.01) {\n setVelocity(0);\n setValue(targetInput);\n return;\n }\n\n const deltaTime = deltaTimeMS / 1000; // Convert milliseconds to seconds\n\n // Get current spring properties, using defaults if not provided\n const stiffness = percentToValueBetweenRange(props.stiffness?.() ?? 10, -1, -300);\n const damping = percentToValueBetweenRange(props.damping?.() ?? 30, -0.4, -20);\n const mass = percentToValueBetweenRange(props.mass?.() ?? 20, 0.1, 10);\n\n const springX = stiffness * (value() - targetInput);\n const damperX = damping * velocity();\n const amplitude = (springX + damperX) / mass;\n\n setVelocity((prev) => prev + amplitude * deltaTime);\n setValue(value() + velocity() * deltaTime);\n };\n\n onTick((ticker) => {\n update(ticker.deltaMS);\n });\n\n return { value, velocity };\n};\n\n// Helper to convert 0-100 percent range to internal calculation range\nconst percentToValueBetweenRange = (percent: number, min: number, max: number) =>\n (percent * (max - min)) / 100 + min;\n"],"names":[],"mappings":";;AA8BO,MAAM,YAAY,CAAC,UAAkC;AAC1D,QAAM,CAAC,OAAO,QAAQ,IAAI,aAAa,MAAM,IAAI;AACjD,QAAM,CAAC,UAAU,WAAW,IAAI,aAAa,CAAC;AAE9C,QAAM,SAAS,CAAC,gBAAwB;AACtC,UAAM,cAAc,MAAM,GAAA;AAI1B,QAAI,KAAK,IAAI,SAAA,CAAU,IAAI,OAAO,KAAK,IAAI,MAAA,IAAU,WAAW,IAAI,MAAM;AACxE,kBAAY,CAAC;AACb,eAAS,WAAW;AACpB;AAAA,IACF;AAEA,UAAM,YAAY,cAAc;AAGhC,UAAM,YAAY,2BAA2B,MAAM,iBAAiB,IAAI,IAAI,IAAI;AAChF,UAAM,UAAU,2BAA2B,MAAM,eAAe,IAAI,MAAM,GAAG;AAC7E,UAAM,OAAO,2BAA2B,MAAM,YAAY,IAAI,KAAK,EAAE;AAErE,UAAM,UAAU,aAAa,MAAA,IAAU;AACvC,UAAM,UAAU,UAAU,SAAA;AAC1B,UAAM,aAAa,UAAU,WAAW;AAExC,gBAAY,CAAC,SAAS,OAAO,YAAY,SAAS;AAClD,aAAS,MAAA,IAAU,SAAA,IAAa,SAAS;AAAA,EAC3C;AAEA,SAAO,CAAC,WAAW;AACjB,WAAO,OAAO,OAAO;AAAA,EACvB,CAAC;AAED,SAAO,EAAE,OAAO,SAAA;AAClB;AAGA,MAAM,6BAA6B,CAAC,SAAiB,KAAa,QAC/D,WAAW,MAAM,OAAQ,MAAM;"}
|