import { useScroll, useVelocity } from 'framer-motion'; import { useEffect, useState } from 'react'; /** * Calculates the scroll position of the given element. * * @returns an array contains [`isScrolled`, `scrollY`, `velocity`] * * `isScrolled` is true if the element is scrolled into view. * * `scrollY` A motion value that represents the scroll position of the element. * * `velocity` A motion value that represents the velocity of the element. */ export const useScrolled = (): UseScrolled => { const [scrolled, setScrolled] = useState(false); const { scrollY } = useScroll(); const velocity = useVelocity(scrollY); /* https://johnchourajr.medium.com/show-hide-on-scroll-with-framer-motion-b6f937c2d662 */ function update() { // If the velocity is greater than 32000, it's considered a intentional scroll // Then we set the scrolled state based on direction of scroll const sy = scrollY.get(); const syp = scrollY.getPrevious(); if (Math.abs(velocity.getVelocity()) > 32000) { if ((sy < syp && sy < 100) || syp - sy > 20) { setScrolled(false); } else if (sy > 100 && sy > syp) { setScrolled(true); } } } /** update the onChange callback to call for `update()` **/ useEffect(() => { return scrollY.on('change', update); }); return [scrolled, scrollY, velocity]; }; export type UseScrolled = [ boolean, ReturnType['scrollY'], ReturnType ];