Animate SVG with React and Framer Motion

2023-02-24

Hi there πŸ‘‹
In this post, I'll show you how i created a draw effect for my blog logo.

Here what you will get ( you can do it basically with any svg, is really simple)

How it works

Basically we are writing our SVG using motiov.svg element.

example.jsx
Copy

_30
import { motion } from "framer-motion";
_30
_30
function MotionLogo(){
_30
const [progress, setProgress] = useState(0);
_30
_30
<motion.svg
_30
width="200"
_30
height="200"
_30
fill="none"
_30
viewBox="0 0 331.25 326.77"
_30
xmlns="http://www.w3.org/2000/svg"
_30
>
_30
<motion.path
_30
fill={"currentColor"}
_30
stroke={"currentColor"}
_30
animate={{
_30
_30
fillOpacity: progress,
_30
_30
pathLength: progress
_30
_30
}}
_30
transition={{ ease: "easeInOut" }}
_30
strokeWidth={4}
_30
strokeDasharray="0 1"
_30
d="M0 257.25V69.51h27.89v187.74H0Zm113.5 0-48.03-87.8-1.81-5.17-2.58 5.04-19.24 25.18c-1.64-4.56-2.35-9.77-2.13-15.62.21-5.85 1.29-11.75 3.23-17.69 1.94-5.94 4.71-11.36 8.33-16.27l55.39-75.41h31.25l-58.23 75.67 66.88 112.08H113.5ZM233.15 257.25c-4.91 0-9.99-.3-15.24-.9-5.25-.6-11.67-1.81-19.24-3.62 1.63-3.01 3.59-5.89 5.88-8.65 2.28-2.75 4.99-4.93 8.13-6.52 3.14-1.59 6.73-2.13 10.78-1.61 1.55.17 2.97.3 4.26.39 1.29.09 2.8.13 4.52.13 6.02 0 11.51-1.51 16.46-4.52 4.95-3.01 8.91-7.1 11.88-12.27s4.46-11.06 4.46-17.69c0-8.09-2.17-14.8-6.52-20.14s-10.2-9.34-17.56-12.01c-7.36-2.67-15.56-4-24.6-4h-28.02v88.45h-27.63V68.99c7.14-1.38 13.77-2.47 19.88-3.29 6.11-.82 12.05-1.44 17.82-1.87 5.77-.43 11.75-.65 17.95-.65 14.8 0 27.18 2.22 37.12 6.65 9.94 4.43 17.39 10.46 22.34 18.08 4.95 7.62 7.42 16.29 7.42 26.02 0 6.2-1.72 12.83-5.17 19.88-3.44 7.06-8.35 12.61-14.72 16.66l-7.49 3.62 7.23 1.81c6.37 2.84 11.9 6.54 16.59 11.1 4.69 4.56 8.31 9.75 10.85 15.56 2.54 5.81 3.81 12.07 3.81 18.79s-1.4 13.26-4.2 19.88c-2.8 6.63-6.82 12.65-12.07 18.08-5.25 5.42-11.67 9.77-19.24 13.04-7.58 3.27-16.14 4.91-25.7 4.91Zm-44.8-111.04h28.02c7.83 0 14.61-1.42 20.34-4.26 5.72-2.84 10.14-6.69 13.24-11.56 3.1-4.86 4.65-10.35 4.65-16.46 0-5.34-1.42-10.27-4.26-14.78-2.84-4.52-7.08-8.13-12.72-10.85-5.64-2.71-12.72-4.07-21.24-4.07-4.65 0-9.32.13-14.01.39-4.69.26-9.36.65-14.01 1.16v60.43Z"
_30
/>
_30
</motion.svg>
_30
);
_30
};

The animation is pretty easy, we are just changing two properties: fillOpacity and pathLength

Note that the value of fillOpacity and pathLength must be between 0 and 1.

PLayground: play with the fill opacity and path length

Path length0.20
Fill Opacity0.05

You may have noticed that in my case the logo path is fully written
with a Path length of 0.43. If you try with other SVG the value may be different.

🀯
The real path length is not between 0 and 1, but in my case is 2123 units.
We don't need to know the realPathLength of our path because motion-framer handle this for us.

For a more detailed explanation of the code, check out the Framer Motion docs.


Conclusion

This is a basic example of how to animate SVG with Framer Motion, but you can do much more with it.
I just wanted to share how i did it for my logo, and maybe inspire you to try it out.

I hope you enjoyed this post, and if you have any questions or suggestions, feel free to contact me on twitter.

🀯
For reference here's the code of the SVG displayed as example at the top of this post
example.jsx
Copy

import clsx from "clsx";
import { motion } from "framer-motion";
import { useEffect, useState } from "react";
interface LogoProps {
progress?: number;
}
export default function AnimateSvgLogoSlider() {
const [progress, setProgress] = useState(100);
const [isRunning, setIsRunning] = useState(false);
const togglePlay = () => {
setProgress(0);
setTimeout(() => {
setIsRunning(!isRunning);
}, 300);
};
useEffect(() => {
if (!isRunning) return;
const animationDuration = 3000;
setTimeout(() => {
setIsRunning(false);
}, animationDuration);
const interval = setInterval(() => {
if (progress >= 100) {
setProgress(100);
setIsRunning(false);
clearInterval(interval);
return;
}
setProgress((progress) => progress + 1);
}, animationDuration / 100);
return () => clearInterval(interval);
}, [isRunning]);
return (
<div>
<div className="flex flex-row justify-center items-center">
<Logo progress={progress / 100} />
</div>
<div className=" w-full flex flex-row gap-3 items-center">
<input
type="range"
min="0"
max="100"
onChange={(e) => setProgress(parseInt(e.target.value))}
value={progress}
className="range range-error"
/>
<button
onClick={togglePlay}
disabled={isRunning}
className={clsx("btn btn-square", {
loading: isRunning,
})}
>
{!isRunning && (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="current-color"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-6 h-6"
width={20}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z"
/>
</svg>
)}
</button>
</div>
</div>
);
}
function Logo(props: LogoProps) {
const { progress = 0.1 } = props;
return (
<motion.svg
width="200"
height="200"
fill="none"
viewBox="0 0 331.25 326.77"
xmlns="http://www.w3.org/2000/svg"
>
<motion.path
fill={"currentColor"}
stroke={"currentColor"}
animate={{ fillOpacity: progress, pathLength: progress }}
transition={{ ease: "easeInOut" }}
strokeWidth={4}
strokeDasharray="0 1"
d="M0 257.25V69.51h27.89v187.74H0Zm113.5 0-48.03-87.8-1.81-5.17-2.58 5.04-19.24 25.18c-1.64-4.56-2.35-9.77-2.13-15.62.21-5.85 1.29-11.75 3.23-17.69 1.94-5.94 4.71-11.36 8.33-16.27l55.39-75.41h31.25l-58.23 75.67 66.88 112.08H113.5ZM233.15 257.25c-4.91 0-9.99-.3-15.24-.9-5.25-.6-11.67-1.81-19.24-3.62 1.63-3.01 3.59-5.89 5.88-8.65 2.28-2.75 4.99-4.93 8.13-6.52 3.14-1.59 6.73-2.13 10.78-1.61 1.55.17 2.97.3 4.26.39 1.29.09 2.8.13 4.52.13 6.02 0 11.51-1.51 16.46-4.52 4.95-3.01 8.91-7.1 11.88-12.27s4.46-11.06 4.46-17.69c0-8.09-2.17-14.8-6.52-20.14s-10.2-9.34-17.56-12.01c-7.36-2.67-15.56-4-24.6-4h-28.02v88.45h-27.63V68.99c7.14-1.38 13.77-2.47 19.88-3.29 6.11-.82 12.05-1.44 17.82-1.87 5.77-.43 11.75-.65 17.95-.65 14.8 0 27.18 2.22 37.12 6.65 9.94 4.43 17.39 10.46 22.34 18.08 4.95 7.62 7.42 16.29 7.42 26.02 0 6.2-1.72 12.83-5.17 19.88-3.44 7.06-8.35 12.61-14.72 16.66l-7.49 3.62 7.23 1.81c6.37 2.84 11.9 6.54 16.59 11.1 4.69 4.56 8.31 9.75 10.85 15.56 2.54 5.81 3.81 12.07 3.81 18.79s-1.4 13.26-4.2 19.88c-2.8 6.63-6.82 12.65-12.07 18.08-5.25 5.42-11.67 9.77-19.24 13.04-7.58 3.27-16.14 4.91-25.7 4.91Zm-44.8-111.04h28.02c7.83 0 14.61-1.42 20.34-4.26 5.72-2.84 10.14-6.69 13.24-11.56 3.1-4.86 4.65-10.35 4.65-16.46 0-5.34-1.42-10.27-4.26-14.78-2.84-4.52-7.08-8.13-12.72-10.85-5.64-2.71-12.72-4.07-21.24-4.07-4.65 0-9.32.13-14.01.39-4.69.26-9.36.65-14.01 1.16v60.43Z"
/>
</motion.svg>
);
}