diff --git a/src/CircularProgressbar.tsx b/src/CircularProgressbar.tsx index 705f009..ed7bd60 100644 --- a/src/CircularProgressbar.tsx +++ b/src/CircularProgressbar.tsx @@ -1,12 +1,12 @@ import React from 'react'; - -const MIN_PERCENTAGE = 0; -const MAX_PERCENTAGE = 100; -const MAX_X = 100; -const MAX_Y = 100; -const FULL_RADIUS = 50; -const CENTER_X = 50; -const CENTER_Y = 50; +import { + VIEWBOX_WIDTH, + VIEWBOX_HEIGHT, + VIEWBOX_HEIGHT_HALF, + VIEWBOX_CENTER_X, + VIEWBOX_CENTER_Y, +} from './constants'; +import Path from './Path'; type CircularProgressbarDefaultProps = { strokeWidth: number; @@ -122,7 +122,7 @@ class CircularProgressbar extends React.Component< getPathRadius() { // The radius of the path is defined to be in the middle, so in order for the path to // fit perfectly inside the 100x100 viewBox, need to subtract half the strokeWidth - return FULL_RADIUS - this.props.strokeWidth / 2 - this.getBackgroundPadding(); + return VIEWBOX_HEIGHT_HALF - this.props.strokeWidth / 2 - this.getBackgroundPadding(); } render() { @@ -143,15 +143,15 @@ class CircularProgressbar extends React.Component< {this.props.background ? ( ) : null} @@ -174,7 +174,12 @@ class CircularProgressbar extends React.Component< /> {text ? ( - + {text} ) : null} @@ -183,79 +188,4 @@ class CircularProgressbar extends React.Component< } } -function Path({ - className, - counterClockwise, - pathRadius, - percentage, - strokeWidth, - style, -}: { - className?: string; - counterClockwise: boolean; - pathRadius: number; - percentage: number; - strokeWidth: number; - style?: object; -}) { - return ( - - ); -} - -// SVG path description specifies how the path should be drawn -function getPathDescription({ - pathRadius, - counterClockwise, -}: { - pathRadius: number; - counterClockwise: boolean; -}) { - const radius = pathRadius; - const rotation = counterClockwise ? 1 : 0; - - // Move to center of canvas - // Relative move to top canvas - // Relative arc to bottom of canvas - // Relative arc to top of canvas - return ` - M ${CENTER_X},${CENTER_Y} - m 0,-${radius} - a ${radius},${radius} ${rotation} 1 1 0,${2 * radius} - a ${radius},${radius} ${rotation} 1 1 0,-${2 * radius} - `; -} - -function getDashStyle({ - pathRadius, - percentage, - counterClockwise, -}: { - pathRadius: number; - percentage: number; - counterClockwise: boolean; -}) { - const diameter = Math.PI * 2 * pathRadius; - - // Keep percentage within range (MIN_PERCENTAGE, MAX_PERCENTAGE) - const truncatedPercentage = Math.min(Math.max(percentage, MIN_PERCENTAGE), MAX_PERCENTAGE); - const gapLength = (1 - truncatedPercentage / 100) * diameter; - - return { - // Have dash be full diameter, and gap be full diameter - strokeDasharray: `${diameter}px ${diameter}px`, - // Shift dash backward by gapLength, so gap starts appearing at correct distance - strokeDashoffset: `${counterClockwise ? -gapLength : gapLength}px`, - }; -} - export default CircularProgressbar; diff --git a/src/Path.tsx b/src/Path.tsx new file mode 100644 index 0000000..f57832a --- /dev/null +++ b/src/Path.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { VIEWBOX_CENTER_X, VIEWBOX_CENTER_Y } from './constants'; + +const MIN_PERCENTAGE = 0; +const MAX_PERCENTAGE = 100; + +function Path({ + className, + counterClockwise, + pathRadius, + percentage, + strokeWidth, + style, +}: { + className?: string; + counterClockwise: boolean; + pathRadius: number; + percentage: number; + strokeWidth: number; + style?: object; +}) { + return ( + + ); +} + +// SVG path description specifies how the path should be drawn +function getPathDescription({ + pathRadius, + counterClockwise, +}: { + pathRadius: number; + counterClockwise: boolean; +}) { + const radius = pathRadius; + const rotation = counterClockwise ? 1 : 0; + + // Move to center of canvas + // Relative move to top canvas + // Relative arc to bottom of canvas + // Relative arc to top of canvas + return ` + M ${VIEWBOX_CENTER_X},${VIEWBOX_CENTER_Y} + m 0,-${radius} + a ${radius},${radius} ${rotation} 1 1 0,${2 * radius} + a ${radius},${radius} ${rotation} 1 1 0,-${2 * radius} + `; +} + +function getDashStyle({ + pathRadius, + percentage, + counterClockwise, +}: { + pathRadius: number; + percentage: number; + counterClockwise: boolean; +}) { + const diameter = Math.PI * 2 * pathRadius; + + // Keep percentage within range (MIN_PERCENTAGE, MAX_PERCENTAGE) + const truncatedPercentage = Math.min(Math.max(percentage, MIN_PERCENTAGE), MAX_PERCENTAGE); + const gapLength = (1 - truncatedPercentage / 100) * diameter; + + return { + // Have dash be full diameter, and gap be full diameter + strokeDasharray: `${diameter}px ${diameter}px`, + // Shift dash backward by gapLength, so gap starts appearing at correct distance + strokeDashoffset: `${counterClockwise ? -gapLength : gapLength}px`, + }; +} + +export default Path; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..0f638bd --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,5 @@ +export const VIEWBOX_WIDTH = 100; +export const VIEWBOX_HEIGHT = 100; +export const VIEWBOX_HEIGHT_HALF = 50; +export const VIEWBOX_CENTER_X = 50; +export const VIEWBOX_CENTER_Y = 50;