Most of these examples use motion.divs, but you can animate any HTML or SVG element with Framer Motion; there are even a few extra properties for SVG paths.
This is primarily a simple animate-driven animation, toggled by the isChecked state.
When isChecked is false:
- The
motion.divwill have ascaleof0.8and a 50% transparent whitebackgroundColor, - and the
pathLengthof themotion.pathwill be0.
When isChecked is true:
- The
motion.divwill have ascaleof1, and itsbackgroundColorbecomes fully opaque, - and the
pathLengthof themotion.pathwill be0.9.
export function Example() {
const [isChecked, setIsChecked] = useState(true);
const pathLength = useMotionValue(0);
const opacity = useTransform(pathLength, [0.05, 0.15], [0, 1]);
return (
<motion.div
style={{
width: 150,
height: 150,
borderRadius: 30,
backgroundColor: "rgba(255,255,255,0.5)",
cursor: "pointer"
}}
animate={{
scale: isChecked ? 1 : 0.8,
backgroundColor: isChecked
? "rgba(255, 255, 255, 1)"
: "rgba(255, 255, 255, 0.5)"
}}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
onTap={() => setIsChecked(!isChecked)}
>
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
<motion.path
d="M38 74.707l24.647 24.646L116.5 45.5"
fill="transparent"
strokeWidth="20"
stroke="#39e"
strokeLinecap="round"
animate={{ pathLength: isChecked ? 0.9 : 0 }}
style={{ pathLength, opacity }}
// transition={{ duration: 3 }}
/>
</svg>
</motion.div>
);
}
But we’re also hooking into the pathLength animation to change the motion.path’s opacity.
A pathLength Motion value is passed to the path’s pathLength property, and a useTransform() is used to change the path’s opacity at the very beginning of the pathLength animation: The checkmark quickly fades in when pathLength changes from 0.05 to 0.15.
export function Example() {
const [isChecked, setIsChecked] = useState(true);
const pathLength = useMotionValue(0);
const opacity = useTransform(pathLength, [0.05, 0.15], [0, 1]);
return (
<motion.div
style={{
width: 150,
height: 150,
borderRadius: 30,
backgroundColor: "rgba(255,255,255,0.5)",
cursor: "pointer"
}}
animate={{
scale: isChecked ? 1 : 0.8,
backgroundColor: isChecked
? "rgba(255, 255, 255, 1)"
: "rgba(255, 255, 255, 0.5)"
}}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
onTap={() => setIsChecked(!isChecked)}
>
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
<motion.path
d="M38 74.707l24.647 24.646L116.5 45.5"
fill="transparent"
strokeWidth="20"
stroke="#39e"
strokeLinecap="round"
animate={{ pathLength: isChecked ? 0.9 : 0 }}
style={{ pathLength, opacity }}
// transition={{ duration: 3 }}
/>
</svg>
</motion.div>
);
}
It’s hardly noticeable, but you can uncomment the transition line inside the motion.path to see a three-second-long version of the animation.