@whitevulpes/border-progress-bar
Version:
This package creates a border that can be used to show progress around any div element.
92 lines (91 loc) • 4.13 kB
JavaScript
import React, { useEffect, useRef, useState } from "react";
const BorderProgressBar = (props) => {
const { strokeWidth, strokeColor, progress, loading = false } = props;
const svgRef = useRef(null);
const [dimensions, setDimensions] = useState({
width: 0,
height: 0,
borderRadius: 0,
});
useEffect(() => {
const updateDimensions = () => {
if (svgRef.current && svgRef.current.parentElement) {
const parentElement = svgRef.current.parentElement;
const { width, height } = parentElement.getBoundingClientRect();
const computedStyle = window.getComputedStyle(parentElement);
const borderRadius = parseInt(computedStyle.borderRadius.replace("px", ""), 10);
setDimensions({
width: width + strokeWidth / 2,
height: height + strokeWidth / 2,
borderRadius: borderRadius + strokeWidth / 2,
});
}
};
updateDimensions();
}, [strokeWidth]);
function createSvgStrokeForBox(width, height, borderRadius) {
const r = Math.min(borderRadius, width / 2, height / 2);
const midX = width / 2;
return `
M ${midX},0
H ${width - r}
A ${r},${r} 0 0 1 ${width},${r}
V ${height - r}
A ${r},${r} 0 0 1 ${width - r},${height}
H ${r}
A ${r},${r} 0 0 1 0,${height - r}
V ${r}
A ${r},${r} 0 0 1 ${r},0
H ${midX}
`;
}
function calculateStrokeLength(width, height, borderRadius) {
const r = Math.min(borderRadius, width / 2, height / 2);
const straightLength = (width - 2 * r) * 2 + (height - 2 * r) * 2;
const cornerLength = ((2 * Math.PI * r) / 4) * 4;
return straightLength + cornerLength;
}
const strokeLength = calculateStrokeLength(dimensions.width, dimensions.height, dimensions.borderRadius);
// CSS Keyframes for continuous motion animation
const animationStyle = `
@keyframes continuous-motion {
0% {
stroke-dasharray: ${strokeLength * 0.1} ${strokeLength * 0.9};
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: ${strokeLength * 0.5} ${strokeLength * 0.5};
stroke-dashoffset: -${strokeLength * 0.25};
}
100% {
stroke-dasharray: ${strokeLength * 0.1} ${strokeLength * 0.9};
stroke-dashoffset: -${strokeLength};
}
}
`;
// Calculate the dash offset for the progress bar when loading is false
const strokeDasharray = loading
? undefined // Animated stroke-dasharray for loading
: `${progress * strokeLength} ${strokeLength - progress * strokeLength}`;
const strokeDashoffset = loading
? undefined // Animated stroke-dashoffset for loading
: 0; // Static stroke-dashoffset for progress
return (React.createElement(React.Fragment, null,
React.createElement("style", null, animationStyle),
React.createElement("svg", { ref: svgRef, width: dimensions.width + strokeWidth, height: dimensions.height + strokeWidth, viewBox: `-${strokeWidth / 2} -${strokeWidth / 2} ${dimensions.width + strokeWidth} ${dimensions.height + strokeWidth}`, style: {
position: "absolute",
zIndex: 1,
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
} },
React.createElement("path", { strokeWidth: strokeWidth, stroke: strokeColor, fill: "none", style: {
strokeDasharray: strokeDasharray,
strokeDashoffset: strokeDashoffset,
animation: loading
? "continuous-motion 1.5s linear infinite"
: undefined, // Animation only when loading
transition: loading ? undefined : "stroke-dasharray 0.5s ease", // Smooth transition for progress
}, d: createSvgStrokeForBox(dimensions.width, dimensions.height, dimensions.borderRadius) }))));
};
export default BorderProgressBar;