useSpring
loomui/use-springFreeUtility
A spring over named numbers, driven onto a callback rather than through state, that carries velocity across every retarget.
Install it
npx shadcn@latest add https://loomui.design/r/use-spring.jsonSource
1"use client"23import * as React from "react"45export interface SpringOptions {6 /**7 * Seconds to settle, roughly. A spring has no fixed end, but this is the8 * number you would have reached for with a duration.9 */10 duration?: number11 /**12 * Overshoot. `0` arrives and stops, above `0` passes the target and comes13 * back, below `0` eases in slow. Keep it under `0.3` for anything that is14 * not meant to be playful.15 */16 bounce?: number17 /** Below this, in target units, the spring is called finished. */18 epsilon?: number19}2021type Values = Record<string, number>2223/** Fixed step, so the motion is the same on a 60Hz screen and a 120Hz one. */24const STEP = 1 / 24025/** A tab left in the background hands back one enormous frame. Ignore it. */26const MAX_FRAME = 0.0642728/**29 * Apple's parameterisation: say how long and how bouncy, rather than picking30 * mass, stiffness and damping and discovering what they add up to.31 */32function coefficients({ duration = 0.4, bounce = 0 }: SpringOptions) {33 const omega = (2 * Math.PI) / duration34 const damping =35 bounce >= 036 ? (4 * Math.PI * (1 - bounce)) / duration37 : (4 * Math.PI) / (duration * (1 + bounce))3839 return { stiffness: omega * omega, damping }40}4142/**43 * A spring over a set of named numbers, driven straight onto a callback rather44 * than through state. A frame that re-renders is a frame that can drop.45 *46 * The point of a spring over a curve is interruption. A CSS animation retargeted47 * halfway through restarts from a standstill, which is the moment it stops48 * feeling physical. This carries velocity across every retarget, so a value49 * already travelling keeps travelling.50 */51export function useSpring(52 onFrame: (values: Values) => void,53 options: SpringOptions = {}54) {55 const { epsilon = 0.0005 } = options56 const { stiffness, damping } = coefficients(options)5758 const current = React.useRef<Values>({})59 const velocity = React.useRef<Values>({})60 const target = React.useRef<Values>({})61 const frame = React.useRef(0)62 const clock = React.useRef(0)6364 // Read through refs so the loop never closes over a stale callback and never65 // has to be torn down and rebuilt when one changes.66 const frameRef = React.useRef(onFrame)67 const springRef = React.useRef({ stiffness, damping, epsilon })68 React.useEffect(() => {69 frameRef.current = onFrame70 springRef.current = { stiffness, damping, epsilon }71 })7273 const stop = React.useCallback(() => {74// … truncated
Files it writes
More from loomui
All 91 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useInViewportuse-in-viewport | loomui | Utilities | Free | no deps | |
| Utilsutils | loomui | Utilities | Free | 2 deps |
