will
will

Reputation: 177

Framer Motion not updating scrollYProgress

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

Answers (2)

Isaackoz
Isaackoz

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

Lars
Lars

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/>
  </>)
}

Sandbox example

Upvotes: 11

Related Questions