Animate pathLength of chart with Framer Motion #1057
Replies: 3 comments 3 replies
-
Ok I think I got something that's almost there but not quite:
Instead of {(area) => (
<g>
<motion.path
d={area.path(formattedData)}
initial="initial"
animate="animate"
stroke="url(#hr)"
variants={pathVariants}
style={{
strokeDashoffset: pathLength,
strokeDasharray: pathLength,
}}
/>
</g>
)}
|
Beta Was this translation helpful? Give feedback.
-
Hey @MaximeHeckel 👋 thanks for checking out Re animation I'm not familiar with the Given all of the complexity described above^, I wonder if it would be simpler for you to animate a Re animating on mount This might because of two successive renders which happen very quickly. For the 1st render, you would maybe be interpolating from |
Beta Was this translation helpful? Give feedback.
-
@williaster thank you for your help and the examples you provided. After taking a further look today I managed to reproduce the effect I wanted: Screen.Recording.2021-02-10.at.7.11.50.PM.movThe ClipPath method worked perfectly, I was amazed to see it work on the first try 😄! I still haven't fully solved the multiple renders issue for the linepath animation, but I added a small hack with a timeout to work around it in the meantime (I'll make sure to update the code snippet below if I find a solution in the meantime) For anyone else looking for a similar animation powered by Framer Motion here's the rough implementation: const LineChart = ({}: any) => {
const [shouldAnimate, setShouldAnimate] = React.useState<boolean>(false);
const pathVariants = {
initial: {
pathLength: 0,
},
animate: {
pathLength: 1,
transition: {
ease: "easeInOut",
duration: 2,
},
},
};
const areaVariants = {
initial: {
width: "0%",
},
animate: {
width: "100%",
transition: {
delay: 0.5, // To keep the area and path animation synced we need to add a delay of 500ms
ease: "easeInOut",
duration: 2,
},
},
};
const pathLength = useMotionValue(0);
/**
* Work around for double render happening with server-side,
* we "toggle" the animation 500ms after mounting the UI
*/
React.useEffect(() => {
setTimeout(() => setShouldAnimate(true), 500);
}, []);
return (
<div>
<svg width={width} height={height}>
<rect
x={0}
y={0}
width={width}
height={height}
fill="#F9F9FA"
rx={20}
/>
<ClipPath id="myClip">
<motion.rect
x={0}
y={0}
height={height}
initial="initial"
animate="animate"
variants={areaVariants}
/>
</ClipPath>
<Group clipPath={"url(#myClip)"}>
<AreaClosed
data={formattedData}
x={...}
y={...}
yScale={,,,}
curve={curveBasisOpen}
fill="url(#hr)"
/>
<LinePath
data={formattedData}
x={...}
y={...}
curve={curveBasisOpen}
>
{({ path }) => {
const d = path(formattedData) || "";
return (
<>
{shouldAnimate && (
<motion.path
d={d}
strokeWidth={2}
strokeOpacity={0.8}
strokeLinecap="round"
fill="none"
stroke="#ff008c"
initial="initial"
animate="animate"
variants={pathVariants}
style={{
strokeDashoffset: pathLength,
strokeDasharray: pathLength,
}}
/>
)}
</>
);
}}
</LinePath>
</Group>
</svg>
</div>
);
};
export { LineChart }; |
Beta Was this translation helpful? Give feedback.
-
Hi 👋
First of all, I love visx and I think the approach you took with this library is the best: little opinionated, still higher level than raw D3.
I'm doing a couple of little experiment trying to integrate visx with framer-motion, it works perfectly, however, I'm struggling with the following:
I would like my
AreaClosed
chart to "draw" itself on render (from left to right), and for that I'd usually base my animation on thepathLength
attribute of the<path/>
element when using Framer Motion:Also I noticed that setting the
pathLength
attribute manually does not work either.Do you know if it's possible to achieve the desired effect with Framer Motion? Since it's my only "animation library" on my project I'd really want to avoid introducing something different for charts if possible
Thank you in advance for your help!
Beta Was this translation helpful? Give feedback.
All reactions