20. SVG path length
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.
Code component
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 CC_20_SVG_path_length(props) {
const [isChecked, setIsChecked] = useState(true)
const pathLength = useMotionValue(0)
const opacity = useTransform(pathLength, [0.05, 0.15], [0, 1])
return (
<div>
<motion.div
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>
<motion.path
d="M38 74.707l24.647 24.646L116.5 45.5"
fill="transparent"
strokeWidth="20"
stroke="#39e"
strokeLinecap="round"
initial={{ pathLength: 0.9, opacity: 1 }}
animate={{ pathLength: isChecked ? 0.9 : 0 }}
style={{ pathLength, opacity }}
/>
</svg>
</motion.div>
</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 CC_20_SVG_path_length(props) {
const [isChecked, setIsChecked] = useState(true)
const pathLength = useMotionValue(0)
const opacity = useTransform(pathLength, [0.05, 0.15], [0, 1])
return (
<div>
<motion.div
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>
<motion.path
d="M38 74.707l24.647 24.646L116.5 45.5"
fill="transparent"
strokeWidth="20"
stroke="#39e"
strokeLinecap="round"
initial={{ pathLength: 0.9, opacity: 1 }}
animate={{ pathLength: isChecked ? 0.9 : 0 }}
style={{ pathLength, opacity }}
/>
</svg>
</motion.div>
</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.
Code override
A Graphic on the canvas is also (technically) an SVG, but you can’t attach an override to it. So, how do you animate an SVG with code overrides? By inserting the SVG as the override’s children
:
export function SVG_path_length(Component): ComponentType {
return (props) => {
const [isChecked, setIsChecked] = useState(true)
const pathLength = useMotionValue(0)
const opacity = useTransform(pathLength, [0.05, 0.15], [0, 1])
return (
<Component
{...props}
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)}
children={
<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: pathLength, opacity: opacity }}
/>
</svg>
}
/>
)
}
}
Other examples of using children
in a code override:
- 22. Keyframes: Morphing an SVG path
- 30. SVG gradient animation
- 31. Animate Presence
- 34. Swapping elements