Categories
React

Framer Motion — Drag and Scroll Progress

Spread the love

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

Drag

We can add drag and drop into our React app with Framer Motion.

For example, we can write:

import React from "react";
import { motion } from "framer-motion";

export default function App() {
  return (
    <>
      <motion.div
        drag
        dragConstraints={{
          top: -50,
          left: -50,
          right: 50,
          bottom: 50
        }}
        style={{ backgroundColor: "red", width: 100, height: 100 }}
      />
    </>
  );
}

to make a draggable div.

We add the drag prop to make the div draggable in all directions.

And we add the dragConstraints prop to constrain the positions that the div can be dragged to.

MotionValues

We can use MotionValues to track the state and velocity of all animation values.

For example, we can write:

import React from "react";
import { motion, useMotionValue, useTransform } from "framer-motion";

export default function App() {
  const x = useMotionValue(0);
  const background = useTransform(
    x,
    [-100, 0, 100],
    ["#f4fc03", "#ced41c", "#d4bb1c"]
  );

  return (
    <motion.div style={{ background }}>
      <motion.div
        drag="x"
        dragConstraints={{ left: 0, right: 0 }}
        style={{ x }}
      >
        <div x={x}>foo</div>
      </motion.div>
    </motion.div>
  );
}

to show a different background when we drag the ‘foo’ text across the screen.

We do this by using the useTransform hook with the useMotionValue hook to create the value to track.

useTransform takes the value to track as the first argument.

The 2nd argument has the position to drag to.

The 3rd argument has the color for that’s displayed when we drag the ‘foo’ text.

The x MotionValue is applied by setting the x property of the style prop of the div .

We do the same with the div.

And we add the drag and dragConstraints to enable dragging and set the location that we can drag to.

We set left and right to 0, so we bounce the ‘foo’ text back to its original position.

We set the background property in the style prop of motion.div to set the background.

Viewport Scroll

We can watch for viewport scroll progress and animate content accordingly.

For example, we can write:

import React, { useEffect, useState } from "react";
import {
  motion,
  useSpring,
  useTransform,
  useViewportScroll
} from "framer-motion";

export default function App() {
  const [isComplete, setIsComplete] = useState(false);
  const { scrollYProgress } = useViewportScroll();
  const yRange = useTransform(scrollYProgress, [0, 0.9], [0, 1]);
  const pathLength = useSpring(yRange, { stiffness: 400, damping: 90 });

  useEffect(() => yRange.onChange((v) => setIsComplete(v >= 1)), [yRange]);

  return (
    <>
      <svg
        className="progress-icon"
        viewBox="0 0 200 200"
        style={{ position: "fixed", top: 0, left: 50 }}
      >
        <motion.path
          fill="none"
          strokeWidth="5"
          stroke="green"
          strokeDasharray="0 1"
          d="M 0, 20 a 20, 20 0 1,0 40,0 a 20, 20 0 1,0 -40,0"
          style={{
            pathLength,
            rotate: 90,
            translateX: 5,
            translateY: 5,
            scaleX: -1
          }}
        />
        <motion.path
          fill="none"
          strokeWidth="5"
          stroke="green"
          d="M14,26 L 22,33 L 35,16"
          initial={false}
          strokeDasharray="0 1"
          animate={{ pathLength: isComplete ? 1 : 0 }}
        />
      </svg>
      <div>
        {Array(100)
          .fill()
          .map((_, i) => (
            <p key={i}>{i}</p>
          ))}
      </div>
    </>
  );
}

We add the svg with a circle and a checkmark.

The pathLength will increase as we scroll down.

This is because we used the useViewportScroll hook to watch the scrollYProgress .

Then we use the useTransform hook to watch that and create the yRange object.

We call setIsComplete when the yRange value changes so so that we only show the checkmark when we scroll to the bottom.

The first motion.path has the circle. We increase the pathLength as we scroll down to show more of the circle as we scroll down.

The 2nd motion.path has the checkmark, we only show this when isComplete is true .

We scroll all the way down to make isComplete true .

Conclusion

We can add drag and drop and track scroll progress with Framer Motion.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *