react-circular-progressbar
react-circular-progressbar copied to clipboard
AnimatedProgressProvider is outdated
The package react-move, used in the docs, is quite outdated. It doesn't support react v18, which is required by next.js 13 and frameworks. You can easily rewrite it in framer-motion.
Example animatedProgressProvider:
import React, { useEffect, useState } from "react";
import { motion, useAnimation } from "framer-motion";
const AnimatedProgressProvider = ({
valueStart = 0,
valueEnd,
duration,
easingFunction,
children,
repeat = false
}) => {
const controls = useAnimation();
const [value, setValue] = useState(valueStart);
useEffect(() => {
const animate = async () => {
await controls.start({
value: valueEnd,
transition: { duration, ease: easingFunction }
});
if (repeat) {
await controls.start({
value: valueStart,
transition: { duration: 0 }
});
}
};
animate();
if (repeat) {
const interval = setInterval(animate, duration * 1000);
return () => clearInterval(interval);
}
}, [controls, valueEnd, valueStart, duration, easingFunction, repeat]);
return (
<motion.div
initial={{ value: valueStart }}
animate={controls}
onUpdate={latest => setValue(latest.value)}
>
{children(value)}
</motion.div>
);
};
export default AnimatedProgressProvider;
Example app.jsx:
import React from "react";
import { CircularProgressbar, buildStyles } from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
import { easeQuadInOut } from "d3-ease";
import AnimatedProgressProvider from "./AnimatedProgressProvider";
const App = () => (
<div style={{ width: 200 }}>
<AnimatedProgressProvider
valueStart={0}
valueEnd={66}
duration={1.4}
easingFunction={easeQuadInOut}
>
{(value) => {
const roundedValue = Math.round(value);
return (
<CircularProgressbar
value={value}
text={`${roundedValue}%`}
styles={buildStyles({ pathTransition: "none" })}
/>
);
}}
</AnimatedProgressProvider>
</div>
);
export default App;
Sharing mine if anyone is interested - superminimal, using the latest motion npm package:
import { animate } from "motion";
import { ReactNode, useEffect, useState } from "react";
export default function ProgressAnimator({
valueEnd,
children,
}: {
valueEnd: number;
children: (value: number) => ReactNode;
}) {
const [value, setValue] = useState(0);
useEffect(() => {
setValue(0);
const controls = animate(0, valueEnd, {
duration: 0.8,
ease: "linear",
onUpdate: (latest) => setValue(latest),
});
return () => controls.stop();
}, [valueEnd]);
return <>{children(value)}</>;
}
Usage:
import ProgressAnimator from "@/components/widgets/progress-animator";
import {
buildStyles,
CircularProgressbarWithChildren,
} from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
<ProgressAnimator valueEnd={progress}>
{(value) => {
return (
<CircularProgressbarWithChildren
value={value}
pathTransition: "none",
})}
>
<span className="text-base font-semibold">
{Math.round(value)}%
</span>
</CircularProgressbarWithChildren>
);
}}
</ProgressAnimator>
@kevinsqi please update docs + example animator/wrapper/provider when possible 😁