mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { cn } from "@/lib/utils"
|
|
|
|
interface Props {
|
|
max: number
|
|
value: number
|
|
min: number
|
|
className?: string
|
|
primaryColor?: string
|
|
}
|
|
|
|
export default function AnimatedCircularProgressBar({
|
|
max = 100,
|
|
min = 0,
|
|
value = 0,
|
|
primaryColor,
|
|
className,
|
|
}: Props) {
|
|
const circumference = 2 * Math.PI * 45
|
|
const percentPx = circumference / 100
|
|
const currentPercent = ((value - min) / (max - min)) * 100
|
|
|
|
return (
|
|
<div
|
|
className={cn("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 stroke-muted"
|
|
style={
|
|
{
|
|
"--stroke-percent": 90 - currentPercent,
|
|
"--offset-factor-secondary": "calc(1 - var(--offset-factor))",
|
|
strokeDasharray:
|
|
"calc(var(--stroke-percent) * var(--percent-to-px)) var(--circumference)",
|
|
transform:
|
|
"rotate(calc(1turn - 90deg - (var(--gap-percent) * var(--percent-to-deg) * var(--offset-factor-secondary)))) scaleY(-1)",
|
|
transition: "all var(--transition-length) ease var(--delay)",
|
|
transformOrigin: "calc(var(--circle-size) / 2) calc(var(--circle-size) / 2)",
|
|
} as React.CSSProperties
|
|
}
|
|
/>
|
|
)}
|
|
<circle
|
|
cx="50"
|
|
cy="50"
|
|
r="45"
|
|
strokeWidth="10"
|
|
strokeDashoffset="0"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className={cn("opacity-100 stroke-current", {
|
|
"stroke-[var(--stroke-primary-color)]": primaryColor,
|
|
})}
|
|
style={
|
|
{
|
|
"--stroke-primary-color": primaryColor,
|
|
"--stroke-percent": currentPercent,
|
|
strokeDasharray:
|
|
"calc(var(--stroke-percent) * var(--percent-to-px)) var(--circumference)",
|
|
transition:
|
|
"var(--transition-length) ease var(--delay),stroke var(--transition-length) ease var(--delay)",
|
|
transitionProperty: "stroke-dasharray,transform",
|
|
transform:
|
|
"rotate(calc(-90deg + var(--gap-percent) * var(--offset-factor) * var(--percent-to-deg)))",
|
|
transformOrigin: "calc(var(--circle-size) / 2) calc(var(--circle-size) / 2)",
|
|
} as React.CSSProperties
|
|
}
|
|
/>
|
|
</svg>
|
|
<span
|
|
data-current-value={currentPercent}
|
|
className="duration-[var(--transition-length)] delay-[var(--delay)] absolute inset-0 m-auto size-fit ease-linear animate-in fade-in"
|
|
>
|
|
{currentPercent}
|
|
</span>
|
|
</div>
|
|
)
|
|
}
|