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.
I explained this example in more detail in Chaining and Transforming Motion Values.
Code component
Note that the pathLength
Motion value transforms the motion.path
’s opacity
. The checkmark quickly fades in at the beginning of the pathLength
animation. (Uncomment the transition
code to see it slowed down.)
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
style={{
width: 150,
height: 150,
borderRadius: 30,
backgroundColor: "#fff",
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"
initial={{ pathLength: 0.9, opacity: 1 }}
animate={{ pathLength: isChecked ? 0.9 : 0 }}
style={{ pathLength: pathLength, opacity: opacity }}
/>
</svg>
</motion.div>
</div>
)
}
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
8 comments on “20. SVG path length”
Leave a Reply
You must be logged in to post a comment.
Hi,
for the code component version, how is it done when you have SVG’s in a separate, imported code component?
I don’t think you can, because those SVGs will contain normal
path
s (and notmotion.path
s).I suppose you would have to extract the
path
from each SVG (preferably directly from the.svg
files, with something like this) and insert that in your code.Thank you for the quick reply!
I actually had my SVG’s (icons) in a code component in Framer, so I was able to add motion to the paths, but I still could not get it to work ( I am quite the novice both in React and Framer ).
I was also just trying to animate a change in colour of the SVG stroke. After a lot of messing about, I found out that, since I was also changing the SVG’s stroke colour via property controls, it was those colour changes that interfered with the animation. The property control for colour will give off both HSL and RGB values it seems (If I start with HSL, changing alpha will result in RGB for example). So when I was attempting to animate with RGB values, that did not work.
Now I don’t know how to safely “bypass” this behaviour, but as long as I know about it I can move on. Well, well 🙂
This error popped up when I was animation colors with Framer Motion:
Error: Both colors must be hex/RGBA, OR both must be HSLA.
That might be part of the problem.
Also, when I did animate with only HSL(A) colors it cycled through a few colors (instead of animating straight from one color to the other)
Is it possible to morph two SVG’s directly in Framer, for example a menu icon-to-close icon animation? Or would you have to install some sort of npm package (e.g like animated-burgers)?
Seeing that even in Framer Motion you can only animate a SVG path’s length, offset and spacing… you’ll probably have to use something else.
But, when it’s just a hamburger button, you might do it with Frames as well. And not necessarily manually (writing all the values): take a look at the ‘Icon Morphing’ example in the example file of the Switch package.
Thanks! I’ll have a look.
Update: You can morph between paths when they’re similar enough.