Animate on show with React & React Spring
Recently I've been wanting to learn how to use react-spring and also trying to learn more about typescript. Two days ago I was assigned a task on a project to animate components as the user scrolls into them, you know, fly-in/fade-in/whatever-the-client-wants-this-time, anyways, I thought to myself, this is a great opportunity to check some of the stuff off my list.
I'm going to break bit by bit how I built this how and how to use it:
This is the wrapper component that will be returned to the end user.
const wrapper: FunctionComponent<WrapperProps> = ({
children,
style, // use spring return object.
visible // ref for the element to be watched.
}) => (
<div ref={visible}>
<animated.div style={style}>{children}</animated.div>
</div>
)
Here we define the hook, I removed some types here for readability.
function useAnimateOnShow(from, to, config) {
const ref = useRef<HTMLElement>()
const [visible, setVisible] = useState(false)
const props = useSpring(visible ? to : from)
useEffect(() => {
// if no window or no Intersection Observer early return showing the elements.
if (typeof window === 'undefined') return
if (
!('IntersectionObserver' in window) ||
!('IntersectionObserverEntry' in window) ||
!('isIntersecting' in window.IntersectionObserverEntry.prototype)
) {
setVisible(true)
return function cleanup() {
console.warn('No intersection observer in window.')
}
}
// IO Callback
const handleVisible = (
entries: IntersectionObserverEntry[],
observer: IntersectionObserver
) => {
if (entries.length > 0) {
const entry = entries[0]
// if config is infinite then keep watching otherwise unobserve.
if (config?.infinite) {
setVisible(entry.isIntersecting)
} else {
if (entry.intersectionRatio > 0) {
setVisible(true)
observer.unobserve(entry.target)
}
}
// Custom extra method just because.
if (config?.onShow) {
config.onShow(entry.isIntersecting)
}
}
}
// IO Options
const options = {
root: config?.root || null,
rootMargin: config?.rootMargin || '0px',
threshold: config?.threshold || 1.0
}
const observer = new IntersectionObserver(handleVisible, options)
if (ref && ref.current) {
observer.observe(ref.current)
}
return function cleanup() {}
}, [ref, config])
return [wrapper, ref, props]
}
export default useAnimateOnShow
That's it, single file to get our React components animating in like this.
Got a suggestion or improvement? You can check the repo here
I'll continue to add more and more examples.