react-hinge
v1.0.1
Published
A tiny library to run GPU powered animations in React
Downloads
11
Maintainers
Readme
Features
- Animate components both declaratively, or imperatively
- Run animations on the GPU layer leaving the main thread free
- Simplest API, no custom tags or wrapping of the components
- Supports all the Web Animations API features
1.2kb
minified and gzipped
Installation
npm install react-hinge
Get Started
The core of React Hinge is its use
method. Supercharge any dom element or react component that accepts ref
prop with animation capabilities.
When those values change, React Hinge will automatically render an animation to the latest values. The animation will work great by default, but it can be configured with KeyframeAnimationOptions
import { useHinge } from 'react-hinge'
const hinge = useHinge();
<button ref={hinge.use({
transform: hover ? 'scale(1.05)' : 'scale(1)'
})} />
The second argument will be the transition options. Check out configuration to define default options.
Animation
Hinge.animate(...)
Hinge.animate
is similar to Hinge.use
method, it gives you more control over the initial state of the animation.
import { useHinge } from 'react-hinge'
const hinge = useHinge();
{visible && (
<MyComponent ref={hinge.animate({
// Optional
initial: {
transform: 'scale(0.95)',
opacity: 0
},
// Automatically animates to the latest values on change
animate: {
transform: 'scale(1)',
opacity: 1
},
// Not required, default options will be provided
options: {
// delay: 100,
duration: 150,
easing: 'ease-out'
}
})} />
)}
Keyframes
Values can also be set as a series of keyframes. This will animate through each value in sequence.
import { useHinge } from 'react-hinge'
const hinge = useHinge();
<button ref={hinge.use({
transform: ['scale(1.05)', 'scale(1)', 'scale(1.05)', 'scale(1)'],
})} />
If keyframes provided to Hinge.animate
together with initial values, React Hinge will immediately apply the initial values and kick off the animation right after.
<button ref={hinge.animate({
initial: {
opacity: 1,
},
animate: {
transform: ['scale(1.05)', 'scale(1)', 'scale(1.05)', 'scale(1)'],
}
})} />
Loop animations
React Hinge uses Web Animations API effect timing options for transition options and so to loop your animations you need to use the same config you'd use for Web Animations API:
import { useHinge } from 'react-hinge'
const hinge = useHinge();
<button ref={hinge.use({
transform: ['scale(1.05)', 'scale(1)', 'scale(1.05)', 'scale(1)'],
}, {
duration: 5000,
iterations: Infinity,
direction: "alternate",
easing: "linear",
})} />
Manual control
React Hinge lets you register elements one by one or in bulk and animate them on command anytime you need it.
To get started you will need to register references to your elements.
Hinge.to(...)
import { useHinge } from 'react-hinge'
const hinge = useHinge();
// Register a reference to your element first
<div ref={hinge.register('banner')} />
<button
onClick={() => {
hinge.to('banner', {
opacity: 1,
})
}}
>
Submit
</button>
Hinge.fromTo
If you need more control over the initial state of the animation, you can use hinge.fromTo
instead.
import { useHinge } from 'react-hinge'
const hinge = useHinge();
// Register reference to your element first
<div ref={hinge.register('banner')} />
<button
onClick={() => {
hinge.fromTo('banner', {
display: 'block',
opacity: 0,
}, {
opacity: 1,
})
}}
>
Submit
</button>
Both Hinge.fromTo
and Hinge.to
support keyframes API, just like Hinge.animate
and Hinge.use
import { useHinge } from 'react-hinge'
const hinge = useHinge();
// Register reference to your element first
<div ref={hinge.register('banner')} />
<button
onClick={() => {
hinge.to('banner', {
opacity: [0, 1],
}, {
// Optionally pass transition options
// Otherwise default options will be used
duration: 650,
})
}}
>
Submit
</button>
Animate multiple elements
Similarly to document.querySelectorAll('button')
, the same id passed to Hinge.register(...)
can be used to grab multiple elements.
import { useHinge } from 'react-hinge'
const hinge = useHinge();
// It's safe to register references to multiple elements under the same id
<div ref={hinge.register('banner')} />
<div ref={hinge.register('banner')} />
<div ref={hinge.register('banner')} />
<button
onClick={() => {
// Will animate all of them at the same time
hinge.to('banner', {
opacity: [0, 1],
})
}}
>
Submit
</button>
Stagger animations are not supported yet. Submit a feature request if you'd like it to be prioritized.
Callbacks
Both Hinge.to
and Hinge.fromTo
return an array of Animation
instances they create for each of the nodes animated. You can use them to assign your callbacks:
import { useHinge } from 'react-hinge'
import { useQuery } from 'react-query'
const hinge = useHinge();
useQuery('@example', () => {}, {
async onSuccess() {
const [animation] = hinge.to('toast', {
opacity: [0,1],
transform: ['translateY(100px)', 'translateY(0px)']
})
await animation.finished;
// await sleep(3000);
// router.to('/')
// animation.addEventListener('cancel', () => {...})
// animation.addEventListener('finish', () => {...})
// animation.addEventListener('remove', () => {...})
}
})
Configuration
React Hinge lets you specify default transition options for every instance or define your defaults for all of them at once.
Per instance configuration
Default transition options are picked only once during the initial render phase. They can't be updated on the fly.
import { useHinge } from 'react-hinge'
const hinge = useHinge({
defaultTransitionOptions: {
duration: 150,
easing: "ease-out",
}
});
<button ref={hinge.use({
backgroundColor: hover ? 'rgba(0,0,0,0.15)' : 'rgba(0,0,0,0.25)',
})} />
Default configuration for all instances
To replace the default configuration for React Hinge you'd need to create a separate file that exports your own useHinge instance, as follows:
import { useHinge as myHingeHook, HingeConfig } from 'react-hinge'
// Default config that comes out the box
myHingeHook.defaultConfig = {
defaultTransitionOptions: {
duration: 150,
easing: "ease-out",
},
} satisfies HingeConfig;
export const useHinge = myHingeHook;
Later in your app:
import { useHinge } from '@/my-hinge'
const hinge = useHinge();
<button ref={hinge.use({
backgroundColor: hover ? 'rgba(0,0,0,0.15)' : 'rgba(0,0,0,0.25)',
})} />
Utilities
Hinge.set(...)
Hinge.set
can be used any time you need to apply styles to an element registered.
import { useHinge } from 'react-hinge'
import { useDrag } from 'use-gesture/react'
const hinge = useHinge();
const bind = useDrag((down, movement: [mx, my]) => {
hinge.set('banner', { x: down ? mx : 0, y: down ? my : 0 })
});
<div {...bind()} ref={hinge.register('banner')} />
Hinge.getElements(...)
Use this element if you need to get access to references registered.
[!IMPORTANT]
Hinge.getElements()
returns a JavaScript Set (it's not an array!) that contains HTML elements registered.
import { useEffect } from 'react'
import { useHinge } from 'react-hinge'
import { useDrag } from 'use-gesture/react'
const hinge = useHinge();
useEffect(() => {
const elements = hinge.getElements('banner') // Set<HTMLElement>, size=1
for (const element of elements) {
// ...
}
}, [])
<div ref={hinge.register('banner')} />
Benchmarks
React Hinge vs JS animation library vs CSS animations (Work in progress)
Acknowledgments
This project is inspired by and builds upon the ideas and work of several other projects in the React + Javascript animation ecosystems:
- GSAP for introducing the
to
,fromTo
andset
functions - Framer Motion for introducing simple abstraction in the form of
initial
andanimate
props - React Spring for introducing hook-based animation api in React
- Web Animations API for the animation engine of the library
Development
In one terminal, run the following command to build the library and watch for changes:
npm install
npm run dev
In another terminal, run the following command to start the development server for the site:
(Work in progress)
cd site
npm install
npm run dev