Reputation: 61
I want to add scroll progress using framer-motion
. I want to do this using width
property and I tried the following:
export const ScrollProgress = ({ children }: { children: ReactNode }) => {
const { scrollYProgress } = useScroll();
return (
<div>
<motion.div
style={{
transformOrigin: 'left',
backgroundColor: 'blue',
position: 'sticky',
top: '80px',
width: `${scrollYProgress.get() * 100}%`,
height: '20px',
zIndex: 10,
}}
/>
{children}
</div>
);
};
It doesnt work :( Then I tried the following:
export const ScrollProgress = ({ children }: { children: ReactNode }) => {
const { scrollYProgress } = useScroll();
const [progress, setProgress] = useState<number>(scrollYProgress.get());
useMotionValueEvent(scrollYProgress, 'change', (latest) => {
setProgress(latest);
});
return (
<div>
<motion.div
style={{
// scaleX: scrollYProgress,
transformOrigin: 'left',
backgroundColor: 'blue',
position: 'sticky',
top: '80px',
width: `${progress * 100}%`,
height: '20px',
zIndex: 10,
}}
/>
{children}
</div>
);
};
and it works. How can I made it without introducing additional state like progress
?
Upvotes: 0
Views: 379
Reputation: 2057
Use the useTranform
hook to map the scroll progress to a percentage like so:
// scrollYProgess returns a value between 0 and 1. We want the width
// to be 0% when scrollYProgress is 0 and 100% when scrollYProgress is 1.
const width = useTransform(scrollYProgress, [0, 1], ["0%", "100%"]);
The useTransform
hook can be used to map one motion value into a new motion value. Since scrollYProgress
is a motion value, you can map it to the width percentage you want. The reason width: '${scrollYProgress.get() * 100}%'
wasn't working is because using .get()
on a motion value runs outside of reacts render.
Upvotes: 1