Merger
Merger

Reputation: 61

framer-motion get() method doesnt work with % width

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

Answers (1)

Rico
Rico

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.

Here is a working example

Upvotes: 1

Related Questions