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.