npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@qyu/anim-react

v1.0.4

Published

Declarative animations for react

Readme

@qyu/anim-react

React hooks for @qyu/anim-core to create and run declarative animations

Concept

  • Create animation with useAnim* hook
  • When Initial condition change - animation restarts, When target or config changes - animation updates without restarting
  • Animations by the most part accept special type of input that is created with useInput* hooks. It allows to communicate updates to animation
  • Pass created Animation to useRunAnim* hook

Animate Styles with springs

// create scheduler for browser, for native or node, use (Date, setTimeout, clearTimeout)
const scheduler_raf = fscheduler_new_frame(performance, requestAnimationFrame, cancelAnimationFrame)

const App = () => {
    const tracker = useMemo(() => signal_new_value(0), [])
    // animation will incrementally update when config changes
    const [natfreq, natfreq_set] = useState(1e-2)
    // animation will incrementally update when target changes
    const [width_target, width_target_set] = useState(500)
    const ref_element = useRef<HTMLDivElement | null>(null)

    // run animation with animation frames 
    useRunAnimInterval({
        // for fscheduler look at @qyu/anim-core
        scheduler: scheduler_raf,
        // will treat child animations individually as much as posiible
        // that means when one of children changes it's initial value it will restart without affecting unrelated animations
        // if false (by default) - runner will treat animation as monolith
        spread: true,

        src: useAnimStyleMapSpring({
            // target element
            ref: useRefObject(ref_element),

            // default config for animations
            config: useInputDynamicSet({
                natfreq,
                dampratio: 0.1,
            }),

            // useInputDynamicSet means 
            properties: useInputDynamicSet({
                height: transvalue_new_cssunit({ from: 0, target: 400, unit: "px" }),
                // when widht_target updates - this animation will also update
                // you can override default config
                width: transvalue_new_cssunit({ from: 0, target: { target: width_target, natfreq: 1e-3 }, unit: "px" }),
                // color animation
                // property names are not transformed = need to preserve dashes
                // use tracker param to track value of something
                // no animation on green param
                "background-color": transvalue_new_csscolor([[0, 200], 150, [0, 255, { tracker }], [0.2, 0.8]]),

                transform: transvalue_new_csstransform({
                    // no animation on this one
                    translateX: "100px",

                    // specific config for property
                    scaleX: transvalue_new_cssnumber({
                        from: 1,

                        target: {
                            target: 1.6,
                            natfreq: 5e-3,

                            precision: {
                                velocity: 1e-4,
                                displacement: 1e-5
                            }
                        },
                    })
                })
            })
        }),
    })

    return <>
        <button onClick={() => { natfreq_set(natfreq_old => natfreq_old * 2) } }>
            Increase natfreq {natfreq}
        </button>

        <button onClick={() => { width_target_set(width_target_old => width_target_old + 500) } }>
            Increase width_target {width_target}
        </button>

        <div ref={ref_element} />
    </>
}

Basic Linear animation

Animation will continuosly update on path conditions change (for linear animation it's config), but restart on initial conditions change. This works for all types of animations

const scheduler_raf = fscheduler_new_frame(performance, requestAnimationFrame, cancelAnimationFrame)

const App = () => {
    const [target, target_set] = useState(100)
    const [velocity, velocity_set] = useState(1e-1)

    // run animation with animation frames 
    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimLine({
            init: useInputConstant({
                state: 0
            }),

            config: useInputDynamicSet({
                target,
                velocity,

                effect: state => {
                    console.log("Animation Tick: ", state)
                }
            })
        }),
    })

    return <>
        <button onClick={() => { target_set(target_old => target_old + 100) } }>
            Increase target {target}
        </button>

        <button onClick={() => { velocity_set(velocity_old => velocity_old * 2) } }>
            Increase velocity {velocity}
        </button>
    </>
}

useInput* hooks

Animations take special kind of input created with useInput hooks

// will never update no matter what
useInputConstant(10)
// will update when dependencies change
// dependencies can be ignored, then it will update when provided value changes
// as it updates will return new value and consequentially restart the animation
useInputStatic(10, [])
// accepts signal as parameter, when value in signal changes sends update to dependent animations
useInputDynamic(useSignalValue(10, [deps]))
// the same as previous one, just merged into one
useInputDynamicSet(10, [deps])

usePath* and useInit* hooks

useAnim* hook creates whole animation, but useInit* and usePath* hooks can be used to only create point or path definition Almost all of animation variants have both useAnim, usePath and useInit variants

// does not request whole anim definition in links, see later
useAnimChain([
    useAnimLine({
        init: useInputConstant({
            state: 0
        }),

        config: useInputDynamicSet({
            target: 100,
            velocity: 1e-2,

            effect: state => {
                console.log("Animation Tick: ", state)
            }
        })
    }),

    // only defining path
    usePathLine(useInputDynamicSet({
        target: 100,
        velocity: 1e-2,

        effect: state => {
            console.log("Animation Tick: ", state)
        }
    }))
] as const)

Creating Animation

Line

Linear animation

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimLine({
            init: useInputConstant({
                state: 0
            }),

            config: useInputDynamicSet({
                target: target,
                velocity: 1e-1,

                effect: state => {
                    console.log("Animation 1: ", state)
                }
            })
        })
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Spring

Spring-like animation

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimSpring({
            init: useInputConstant({
                state: 0,
                velocity: -100,
            }),

            config: useInputDynamicSet({
                target: target,
                // bigger faster
                natfreq: 1e-3,
                // dampratio < 1 - will overshoot, >= 1 will not overshoot, dampratio <= 0 - inifinite animation
                dampratio: 0.1,

                // will forcefully finish animation when it's too slow and close to target
                precision: {
                    velocity: 1e-3,
                    displacement: 1e-4,
                },

                effect: state => {
                    console.log("Animation 1: ", state)
                }
            })
        })
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Playback

Speed up or slow down animation

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimPlayback({
            config: useInputDynamicSet({
                multiplire: 2.5
            }),

            src: useAnimSpring({
                init: useInputConstant({
                    state: 0,
                    velocity: -100,
                }),

                config: useInputDynamicSet({
                    target: target,
                    natfreq: 1e-3,
                    dampratio: 0.1,

                    effect: state => {
                        console.log("Animation 1: ", state)
                    }
                })
            })
        })
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Sequence

Emit animations in sequence, if animation completed once, when target updated it will emit in parallel

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimSequence([
            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1: ", state)
                    }
                })
            }),

            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target * 2,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 2: ", state)
                    }
                })
            })
        ] as const)
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Sequence Strict

Init animations in sequence, but only emit one at a time event if it has finished before

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimSequenceStrict([
            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1: ", state)
                    }
                })
            }),

            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target * 2,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 2: ", state)
                    }
                })
            })
        ] as const)
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Merge

Init animations in parallel

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimMerge([
            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1: ", state)
                    }
                })
            }),

            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target * 2,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 2: ", state)
                    }
                })
            })
        ] as const)
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Chain

Emit animations in sequence, but with shared point

const App = () => {
    const [target, target_set] = useState(100)

    // first element should be animation definition
    // chain links share point so no need to define init futher
    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimChain([
            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1: ", state)
                    }
                })
            }),

            usePathLine(useInputDynamicSet({
                target: target * 2,
                velocity: 1e-1,

                effect: state => {
                    console.log("Animation 2: ", state)
                }
            }))
        ] as const)
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

ChainMap

Like chain but with threads

const App = () => {
    const [target, target_set] = useState(100)

    // First element of each thread should be animation definition
    // Thread share point, not need to define init futher
    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimChainMap([
            {
                thread_a: useAnimLine({
                    init: useInputConstant({
                        state: 0
                    }),

                    config: useInputDynamicSet({
                        target: target,
                        velocity: 1e-1,

                        effect: state => {
                            console.log("Animation 1a: ", state)
                        }
                    })
                })
            },

            {
                thread_a: usePathLine({
                    init: useInputConstant({
                        state: 0
                    }),

                    config: useInputDynamicSet({
                        target: target * 2,
                        velocity: 1e-1,

                        effect: state => {
                            console.log("Animation 2a: ", state)
                        }
                    })
                }),

                thread_b: useAnimLine({
                    init: useInputConstant({
                        state: 0
                    }),

                    config: useInputDynamicSet({
                        target: target,
                        velocity: 1e-1,

                        effect: state => {
                            console.log("Animation 1b: ", state)
                        }
                    })
                })
            }
        ] as const)
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Loop

Repeat an animation

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimLoop({
            src: useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1a: ", state)
                    }
                })
            }),

            init: useInputConstant({
                // will make it first time and then repeat 3 times
                repeat: 3
            })
        })
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Merge Init and Path together

    const App = () => {
        const [target, target_set] = useState(100)

        useRunAnimInterval({
            scheduler: scheduler_raf,

            src: useAnim(
                useInitLine(useInputConstant({
                    state: 0
                })),

                usePathLine(useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1a: ", state)
                    }
                })
            ),
        })

        return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
            Increase target {target}
        </button>
    }

Pipe

Adapt animation for different kind of point

const App = () => {
    const [target, target_set] = useState(100)

    // first element should be animation definition
    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimChain([
            useAnimPipe({
                src: useAnimLine({
                    init: useInputConstant({
                        state: 0
                    }),

                    config: useInputDynamicSet({
                        target: target,
                        velocity: 1e-1,

                        effect: state => {
                            console.log("Animation 1: ", state)
                        }
                    })
                }),

                pipei: (point_input: AnimSpring_Point): AnimLine_Point => ({
                    state: point_input.state
                }),

                pipeo: (point_input: AnimLine_Point): AnimSpring_Point => ({
                    state: point_input.state,
                    velocity: 1e-1
                }),
            }),

            usePathSpring(useInputDynamicSet({
                natfreq: 1e-2,
                dampratio: 0.1,
                target: target * 2,

                effect: state => {
                    console.log("Animation 2: ", state)
                }
            }))
        ] as const)
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

Cluster

Makes animation monolith, when one updates initial conditions - restarts the whole thing

const App = () => {
    const [target, target_set] = useState(100)

    useRunAnimInterval({
        scheduler: scheduler_raf,

        src: useAnimCluster(useAnimMerge([
            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 1: ", state)
                    }
                })
            }),

            useAnimLine({
                init: useInputConstant({
                    state: 0
                }),

                config: useInputDynamicSet({
                    target: target * 2,
                    velocity: 1e-1,

                    effect: state => {
                        console.log("Animation 2: ", state)
                    }
                })
            })
        ] as const))
    })

    return <button onClick={ () => { target_set(target_old => target_old + 100) } }>
        Increase target {target}
    </button>
}

StyleMapSpring

Animate ref's styles with springs

const App = () => {
    const [natfreq, natfreq_set] = useState(1e-2)
    const [width_target, width_target_set] = useState(500)
    const ref_element = useRef<HTMLDivElement | null>(null)

    // run animation with animation frames 
    useRunAnimInterval({
        scheduler: scheduler_raf,
        // will treat child animations individually as much as posiible
        // that means when one of children changes it's initial value it will restart without affecting unrelated animations
        // if false (by default) - runner will treat animation as monolith
        spread: true,

        src: useAnimStyleMapSpring({
            // target element
            ref: useRefObject(ref_element),

            // default config for animations
            config: useInputDynamicSet({
                natfreq,
                dampratio: 0.1,
            }),

            // useInputDynamicSet means 
            properties: useInputDynamicSet({
                height: transvalue_new_cssunit({ from: 0, target: 400, unit: "px" }),
                // when widht_target updates - this animation will also update
                width: transvalue_new_cssunit({ from: 0, target: width_target, unit: "px" }),
                // color animation
                // property names are not transformed = need to preserve dashes
                "background-color": transvalue_new_csscolor([[0, 200], [0, 150], [0, 255], [120, 255]]),

                transform: transvalue_new_csstransform({
                    // specific config for property
                    scaleX: transvalue_new_cssnumber({
                        from: 1,

                        target: {
                            target: 1.6,
                            natfreq: 5e-3,

                            precision: {
                                velocity: 1e-4,
                                displacement: 1e-5
                            }
                        },
                    })
                })
            })
        }),
    })

    return <>
        <button onClick={() => { natfreq_set(natfreq_old => natfreq_old * 2) } }>
            Increase natfreq {natfreq}
        </button>

        <button onClick={() => { width_target_set(width_target_old => width_target_old + 500) } }>
            Increase width_target {width_target}
        </button>

        <div ref={ref_element} />
    </>
}

StyleMapLine

Animate styles of target with line

const App = () => {
    const [velocity, velocity_set] = useState(1e-2)
    const [width_target, width_target_set] = useState(500)
    const ref_element = useRef<HTMLDivElement | null>(null)

    // run animation with animation frames 
    useRunAnimInterval({
        scheduler: scheduler_raf,
        // will treat child animations individually as much as posiible
        // that means when one of children changes it's initial value it will restart without affecting unrelated animations
        // if false (by default) - runner will treat animation as monolith
        spread: true,

        src: useAnimStyleMapLine({
            // target element
            ref: useRefObject(ref_element),

            // default config for animations
            config: useInputDynamicSet({
                velocity
            }),

            // useInputDynamicSet means 
            properties: useInputDynamicSet({
                height: transvalue_new_cssunit({ from: 0, target: 400, unit: "px" }),
                // when widht_target updates - this animation will also update
                width: transvalue_new_cssunit({ from: 0, target: width_target, unit: "px" }),
                // color animation
                // property names are not transformed = need to preserve dashes
                "background-color": transvalue_new_csscolor([[0, 200], [0, 150], [0, 255], [120, 255]]),

                transform: transvalue_new_csstransform({
                    // specific config for property
                    scaleX: transvalue_new_cssnumber({
                        from: 1,

                        target: {
                            target: 1.6,
                            velocity: 1e-4
                        },
                    })
                })
            })
        }),
    })

    return <>
        <button onClick={() => { velocity_set(velocity_old => velocity_old * 2) } }>
            Increase velocity {velocity}
        </button>

        <button onClick={() => { width_target_set(width_target_old => width_target_old + 500) } }>
            Increase width_target {width_target}
        </button>

        <div ref={ref_element} />
    </>
}