Most of these examples use motion.div
s, 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.div
will have ascale
of0.8
and a 50% transparent whitebackgroundColor
, - and the
pathLength
of themotion.path
will be0
.
When isChecked
is true
:
- The
motion.div
will have ascale
of1
, and itsbackgroundColor
becomes fully opaque, - and the
pathLength
of themotion.path
will 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.