Reputation: 177
I'm using Framer Motion's useViewportScroll
hook to track the user's scroll on my page, but it isn't updating. I came across this article, which explains how Framer Motion uses document.body.clientHeight
and window.innerHeight
to calculate the viewport scroll.
The article explains how some CSS can break this, specifically if document.body.clientHeight - window.innerHeight <= 0
.
I can't seem to figure out how this wouldn't be true. Even with no CSS at all in my react app, that expression evaluates to true. You can see it in this example on CodeSandbox.
Thanks!
Upvotes: 6
Views: 11912
Reputation: 151
Note that now onChange()
is deprecated and replaced with useMotionValueEvent()
.
An update to Lars' answer is below using useMotionValueEvent()
import React from "react";
import { useMotionValueEvent, useScroll } from "framer-motion"
const FramerPostionHook = () => {
const ref = React.useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end end"],
});
const [hookedYPostion, setHookedYPosition] = React.useState(0);
useMotionValueEvent(scrollYProgress, "change", (latest) => {
setHookedYPosition(latest);
})
return (<>
scrollYProgress.current: {scrollYProgress.current}<br/>
scrollYProgress.hookedYPostion: {hookedYPostion}<br/>
</>)
}
Upvotes: 13
Reputation: 3573
I think the reason you're not seeing updates is not because of any css.
Framer seems to calculate it's state standalone from react. So when Framer updates its values internally. react won't necessarily trigger a re-render with the updated values. (probably this is done for performance).
You can hook into the state changes by subscribing to an onChange
handler that Framer is exposing and set the state you're interested in.
For example, the onChange
on scrollYProgress
will call setState
, triggering react to re-render.
import React from "react";
import { useViewportScroll } from "framer-motion"
const FramerPostionHook = () => {
const { scrollYProgress } = useViewportScroll();
const [hookedYPostion, setHookedYPosition] = React.useState(0);
React.useEffect(()=>{
// hook into the onChange, store the current value as state.
scrollYProgress.onChange(v=> setHookedYPosition(v));
},[scrollYProgress]); //make sure to re-subscriobe when scrollYProgress changes
return (<>
scrollYProgress.current: {scrollYProgress.current}<br/>
scrollYProgress.hookedYPostion: {hookedYPostion}<br/>
</>)
}
Upvotes: 11