framer-motion
Version:
An open source and production-ready motion library for React on the web
64 lines (56 loc) • 2.15 kB
text/typescript
import { useRef, useMemo, useContext } from "react"
import { animate, PlaybackControls, SpringOptions } from "popmotion"
import { MotionValue } from "../value"
import { isMotionValue } from "./utils/is-motion-value"
import { useMotionValue } from "./use-motion-value"
import { useOnChange } from "./use-on-change"
import { MotionConfigContext } from "../motion/context/MotionConfigContext"
/**
* Creates a `MotionValue` that, when `set`, will use a spring animation to animate to its new state.
*
* It can either work as a stand-alone `MotionValue` by initialising it with a value, or as a subscriber
* to another `MotionValue`.
*
* @remarks
*
* ```jsx
* const x = useSpring(0, { stiffness: 300 })
* const y = useSpring(x, { damping: 10 })
* ```
*
* @param inputValue - `MotionValue` or number. If provided a `MotionValue`, when the input `MotionValue` changes, the created `MotionValue` will spring towards that value.
* @param springConfig - Configuration options for the spring.
* @returns `MotionValue`
*
* @public
*/
export function useSpring(
source: MotionValue | number,
config: SpringOptions = {}
) {
const { isStatic } = useContext(MotionConfigContext)
const activeSpringAnimation = useRef<PlaybackControls | null>(null)
const value = useMotionValue(isMotionValue(source) ? source.get() : source)
useMemo(() => {
return value.attach((v, set) => {
/**
* A more hollistic approach to this might be to use isStatic to fix VisualElement animations
* at that level, but this will work for now
*/
if (isStatic) return set(v)
if (activeSpringAnimation.current) {
activeSpringAnimation.current.stop()
}
activeSpringAnimation.current = animate({
from: value.get(),
to: v,
velocity: value.getVelocity(),
...config,
onUpdate: set,
})
return value.get()
})
}, Object.values(config))
useOnChange(source, (v) => value.set(parseFloat(v)))
return value
}