All files / src/value use-spring.ts

95.45% Statements 21/22
75% Branches 6/8
100% Functions 4/4
100% Lines 20/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 6430x 30x   30x 30x 30x 30x                                         30x   40x   20x 20x 20x   20x 6x         3x   3x 1x     3x               3x       20x   20x    
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 "../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,
    Econfig: 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
             */
            Iif (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
}