Once I needed to create a countdown component in the form of a circle and decided to share the implementation:
Stack: ReactJS, TypeScript, scss(modules).
The first step needed to create a component(CircleCountDown.tsx):
import { useEffect, useState, FC, useMemo } from 'react';
import styles from './CircleCountDown.module.scss';
interface CircleCountDownProps {
time: number;
size: number;
stroke: string;
strokeWidth: number;
onComplete?: VoidFunction;
strokeLinecap?: 'butt' | 'round' | 'square' | 'inherit' | undefined;
}
const CircleCountDown: FC<CircleCountDownProps> = ({
time,
size,
stroke,
onComplete,
strokeWidth,
strokeLinecap = 'round',
}) => {
const radius = size / 2;
const milliseconds = time * 1000;
const circumference = size * Math.PI;
const [countdown, setCountdown] = useState(milliseconds);
const seconds = (countdown / 1000).toFixed();
const strokeDashoffset = circumference - (countdown / milliseconds) * circumference;
useEffect(() => {
const interval = setInterval(() => {
if (countdown > 0) {
setCountdown(countdown - 10);
} else {
clearInterval(interval);
onComplete && onComplete();
}
}, 10);
return () => clearInterval(interval);
}, [countdown]);
return (
<div className={styles.root}>
<label className={styles.seconds}>{seconds}</label>
<div className={styles.countDownContainer}>
<svg className={styles.svg} width={size} height={size}>
<circle
fill="none"
r={radius}
cx={radius}
cy={radius}
stroke={stroke}
strokeWidth={strokeWidth}
strokeLinecap={strokeLinecap}
strokeDasharray={circumference}
strokeDashoffset={strokeDashoffset}
/>
</svg>
</div>
</div>
);
};
export default CircleCountDown;
The second step is to create styles(CircleCountDown.module.scss):
@mixin font-serif($name, $size: false, $color: false, $fontStyle: false, $lineHeight: false, $weight: false) {
font-family: $name, sans-serif;
@if $size {
font-size: $size;
}
@if $color {
color: $color;
}
@if $fontStyle {
font-style: $fontStyle;
}
@if $lineHeight {
line-height: $lineHeight;
}
@if $weight {
font-weight: $weight;
}
}
.root {
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.seconds {
position: absolute;
@include font-serif('Roboto', 16px, false, normal, 20px, 500);
}
.svg {
transform: scale(-1, 1);
overflow: visible;
}
.countDownContainer {
transform: rotate(90deg);
}
The third step is to use(App.tsx):
import React from 'react';
import CircleDownCircle from "./components/CircleCountDown/CircleCountDown";
function App() {
return (
<CircleDownCircle time={114} size={45} stroke="#000" strokeWidth={2} />
);
}
export default App;
As you can see, we have the method onComplete. When the timer finished onComplete will call.
Well, that's all. I hope you like this example ^^)
Source code: Github
Live Demo:
Top comments (0)