Skip to content

useSpring

The useSpring function provides reactive SolidJS signals for creating physically-based spring animations for numeric values.

The function returns an object with the following properties:

  • value - An accessor signal containing the current spring-animated value
  • velocity - An accessor signal containing the current velocity of the spring
  • setValue - A function to set the current value directly, bypassing physics calculations for that frame. This allows you to "teleport" the spring to a specific value. The velocity will continue to be calculated normally on the next frame.

The function internally manages the spring physics calculations and keeps its updates synchronized with the Pixi.js application's ticker, ensuring smooth and responsive animations that align with your application's frame rate.

It must be called within a SolidJS tracking scope, such as a component or another function, that is a descendant of PixiApplicationProvider, PixiCanvas, or TickerProvider.

Taking our earlier useSmoothDamp demo and applying useSpring gives us an altogether different feeling motion.

demo-app.tsx
import { Graphics, PixiCanvas } from "pixi-solid";
import { useSpring } from "pixi-solid/utils";
import type * as Pixi from "pixi.js";
import { FillGradient } from "pixi.js";
import { createSignal } from "solid-js";
const DraggingDemo = () => {
const [isDragging, setIsDragging] = createSignal(false);
const pointerPosition = { x: 100, y: 100 };
const draggingScale = 1.4;
const droppedScale = 0.8;
let scaleTarget = droppedScale;
const springyScale = useSpring({
to: () => scaleTarget,
damping: () => (isDragging() ? 50 : 100),
stiffness: () => (isDragging() ? 80 : 100),
mass: () => 20,
});
const springyPositionX = useSpring({
to: () => pointerPosition.x,
damping: () => (isDragging() ? 50 : 100),
stiffness: () => (isDragging() ? 40 : 100),
});
const springyPositionY = useSpring({
to: () => pointerPosition.y,
damping: () => (isDragging() ? 60 : 100),
stiffness: () => (isDragging() ? 30 : 100),
});
let dragTarget: Pixi.Container | undefined;
const handlePointerDown = (e: Pixi.FederatedPointerEvent) => {
dragTarget = e.target;
scaleTarget = draggingScale;
setIsDragging(true);
if (!e.target.parent) return;
// Calculate the offset from the dragTarget's local origin to the click point
e.target.parent.toLocal(e.global, undefined, pointerPosition);
};
const handlePointerUp = () => {
dragTarget = undefined;
scaleTarget = droppedScale;
setIsDragging(false);
};
const handlePointerMove = (e: Pixi.FederatedPointerEvent) => {
if (!dragTarget || !dragTarget.parent) return;
dragTarget.parent.toLocal(e.global, undefined, pointerPosition);
};
const gradient = new FillGradient({
type: "linear",
colorStops: [
{ offset: 0, color: "#b6e3ff" },
{ offset: 1, color: "#0099ff" },
],
});
return (
<Graphics
ref={(graphics) => {
graphics.roundRect(-50, -50, 100, 100, 20).fill(gradient);
}}
eventMode={"static"}
cursor={isDragging() ? "grabbing" : "grab"}
onpointerdown={handlePointerDown}
onpointerup={handlePointerUp}
onpointerupoutside={handlePointerUp}
onglobalpointermove={handlePointerMove}
angle={springyPositionX.velocity() * 0.05}
scale={springyScale.value()}
x={springyPositionX.value()}
y={springyPositionY.value()}
/>
);
};
export const DemoApp = () => (
<PixiCanvas style={{ "aspect-ratio": "2/1.5" }} antialias={true}>
<DraggingDemo />
</PixiCanvas>
);