use-spring-transition
Utilize react-transition-group
functionality into the hook with spring animation support
Usage
Demo
Show menu
import cx from 'clsx';
import { useSpringTransition } from 'use-spring-transition';
import styles from './Menu.module.css';
const Menu = () => {
const [opened, setOpened] = useState(false);
const transition = useSpringTransition(
opened,
{ mass: 1, stiffness: 80, damping: 20 },
{
from: 0,
to: 500,
},
);
if (transition.stage === 'closed') {
return null;
}
return (
<div
className={styles.menu}
style={{ height: transition.springValue }}
>
Menu
</div>
);
};
.menu {
width: 500px;
}
Switch image
import cx from 'clsx';
import { useSpringTransition } from 'use-spring-transition';
import styles from './Viewer.module.css';
const Viewer = ({
images,
}: {
images: {
id: string;
url: sring;
alt: string;
}[];
}) => {
const [currentIndex, setCurrentIndex] =
useState(0);
const transitions = useSpringTransition(
images[currentIndex],
{ mass: 1, stiffness: 180, damping: 30 },
{ from: 100, to: { open: 0, close: -100 } },
);
return (
<div className={styles.viewer}>
{transitions.map(
(transition) =>
transition.value && (
<img
className={cx(
styles.image,
styles[transition.stage],
)}
style={{
transform: `translate3d(${transition.springValue}px, 0, 0)`,
}}
key={transition.value.id}
src={transition.value.url}
alt={transition.value.alt}
/>
),
)}
</div>
);
};
.viewer {
height: 100%;
width: 100%;
position: relative;
}
.image {
height: 100%;
width: 100%;
}
.image.close,
.image.closing {
position: absolute;
top: 0;
left: 0;
}
API
SpringOptions
interface SpringOptions {
mass?: number;
stiffness?: number;
damping?: number;
initialVelocity?: number;
threshold?: number;
}
Defaults:
mass: 1;
stiffness: 100;
damping: 10;
initialVelocity: 0;
threshold: 0.01;
Description:
mass, stiffness, damping, initialVelocity
- spring animation configurationthreshold
- special property for ending a spring animation when spring value difference on each frame lower than threshold
10 times in a row*.
*spring animation can generate a small difference on each frame before reach to
value. For example, we want to animate opacity from 0 to 1. Spring animation can generate 0.991233, 0.992533, 0.993334... in a near minute before reach 1 (or never reach at all). To avoid this long useless animation we can finish animation when spring value is near the to
value. That's why we use threshold
property.
Stage
type Stage =
| 'open'
| 'opening'
| 'opened'
| 'close'
| 'closing'
| 'closed';
Values
interface Values {
from: number;
to:
| number
| {
open: number;
close: number;
};
}
Defaults:
{ from: 0, to: 1 }
Options
type Options =
| SpringOptions
| number
| {
close: SpringOptions | number;
open: SpringOptions | number;
};
Defaults:
{
}
HookOptions
interface HookOptions<T> {
getKey?: (item: T) => any;
keepClosed?: boolean;
}
Defaults:
getKey: (item) => item;
keepClosed: false;
Transition
interface Transition<T> {
value: T;
stage: Stage;
springValue: number;
}
useSpringTransition
export function useSpringTransition(
opened: boolean,
options?: Options,
values?: Values,
): Omit<Transition<boolean>, 'value'>;
export function useSpringTransition<T>(
value: T,
options?: Options,
values?: Values,
hookOptions?: HookOptions<T>,
): Transition<T>[];
export function useSpringTransition<T>(
value: T[],
options?: Options,
values?: Values,
hookOptions?: HookOptions<T>,
): Transition<T>[];