Docs
Circular Progress
Animated Circular Progress Bar is a component that displays a circular gauge with a percentage value.
Installation
Install dependencies
npm install framer-motion lucide-react
Run the following command
It will create a new file circular-progress.tsx
inside the components/mage-ui/preloader
directory.
mkdir -p components/mage-ui/preloader && touch components/mage-ui/preloader/circular-progress.tsx
Paste the code
Open the newly created file and paste the following code:
"use client";
"use client";
import { useEffect, useState } from "react";
interface AnimatedCircularProgressBarProps {
max: number;
value: number;
min: number;
gaugePrimaryColor: string;
gaugeSecondaryColor: string;
className?: string;
}
export function AnimatedCircularProgressBar({
max = 100,
min = 0,
value = 0,
gaugePrimaryColor,
gaugeSecondaryColor,
className,
}: AnimatedCircularProgressBarProps) {
const circumference = 2 * Math.PI * 45;
const percentPx = circumference / 100;
const currentPercent = Math.round(((value - min) / (max - min)) * 100);
return (
<div
className={`relative size-40 text-2xl font-semibold ${className}`}
style={{
"--circle-size": "100px",
"--circumference": circumference,
"--percent-to-px": `${percentPx}px`,
"--gap-percent": "5",
"--offset-factor": "0",
"--transition-length": "1s",
"--transition-step": "200ms",
"--delay": "0s",
"--percent-to-deg": "3.6deg",
transform: "translateZ(0)",
} as React.CSSProperties}
>
<svg fill="none" className="size-full" strokeWidth="2" viewBox="0 0 100 100">
{currentPercent <= 90 && currentPercent >= 0 && (
<circle
cx="50"
cy="50"
r="45"
strokeWidth="10"
strokeDashoffset="0"
strokeLinecap="round"
strokeLinejoin="round"
className="opacity-100"
style={{
stroke: gaugeSecondaryColor,
strokeDasharray: `calc(${90 - currentPercent} * var(--percent-to-px)) var(--circumference)`,
transform:
"rotate(calc(1turn - 90deg - (var(--gap-percent) * var(--percent-to-deg)))) scaleY(-1)",
transition: "all var(--transition-length) ease var(--delay)",
transformOrigin: "50% 50%",
} as React.CSSProperties}
/>
)}
<circle
cx="50"
cy="50"
r="45"
strokeWidth="10"
strokeDashoffset="0"
strokeLinecap="round"
strokeLinejoin="round"
className="opacity-100"
style={{
stroke: gaugePrimaryColor,
strokeDasharray: `calc(${currentPercent} * var(--percent-to-px)) var(--circumference)`,
transition: "var(--transition-length) ease var(--delay)",
transform:
"rotate(calc(-90deg + var(--gap-percent) * var(--offset-factor) * var(--percent-to-deg)))",
transformOrigin: "50% 50%",
} as React.CSSProperties}
/>
</svg>
<span className="absolute inset-0 m-auto size-fit text-center">
{currentPercent}%
</span>
</div>
);
}
export default function AnimatedCircularProgressBarPage() {
const [value, setValue] = useState(0);
useEffect(() => {
const handleIncrement = (prev: number) => (prev === 100 ? 0 : prev + 10);
setValue(handleIncrement);
const interval = setInterval(() => setValue(handleIncrement), 2000);
return () => clearInterval(interval);
}, []);
return (
<div className="flex justify-center items-center h-screen text-[#f59e0a]">
<AnimatedCircularProgressBar
max={100}
min={0}
value={value}
gaugePrimaryColor="rgb(245,158,10)"
gaugeSecondaryColor="rgba(0, 0, 0, 0.1)"
/>
</div>
);
}
Props
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | The class name to be applied to the component |
max | number | 100 | The maximum value of the gauge |
min | number | 0 | The minimum value of the gauge |
value | number | 0 | The current value of the gauge |
gaugePrimaryColor | string | - | The primary color of the gauge |
gaugeSecondaryColor | string | - | The secondary color of the gauge |