elements
lifecycle
browser
- useAudio
- useBattery
- useBluetooth
- useBreakpoints
- useBroadcastChannel
- useBrowserLocation
- useClipboard
- useCopy
- useCssVar
- useDisplayMedia
- useDocumentEvent
- useDocumentTitle
- useDocumentVisibility
- useEventListener
- useEventSource
- useEyeDropper
- useFavicon
- useFileSystemAccess
- useFps
- useFullscreen
- useGamepad
- useGeolocation
- useMeasure
- useMediaControls
- useMediaQuery
- useMemory
- useNetwork
- useObjectUrl
- useOnline
- useOtpCredential
- usePermission
- usePictureInPicture
- usePointerLock
- usePostMessage
- useRaf
- useShare
- useSpeechRecognition
- useSpeechSynthesis
- useSticky
- useVibrate
- useVirtualKeyboard
- useWakeLock
- useWebSocket
utilities
state
- useBoolean
- useControllableState
- useCookie
- useCookies
- useCounter
- useCycleList
- useDefault
- useDisclosure
- useField
- useHash
- useList
- useLocalStorage
- useMap
- useMask
- useMergedRef
- useObject
- useOffsetPagination
- useQueue
- useRafState
- useRefState
- useSessionStorage
- useSet
- useStateHistory
- useStep
- useStorage
- useToggle
- useUrlSearchParam
- useUrlSearchParams
- useValidatedState
- useWizard
user
sensors
- useDeviceMotion
- useDeviceOrientation
- useHotkeys
- useIdle
- useInfiniteScroll
- useIntersectionObserver
- useKeyboard
- useKeyPress
- useKeysPressed
- useMouse
- useMutationObserver
- useOrientation
- usePageLeave
- useParallax
- usePerformanceObserver
- useResizeObserver
- useScroll
- useScrollIntoView
- useScrollTo
- useSwipe
- useTextSelection
- useVisibility
- useWindowEvent
- useWindowFocus
- useWindowScroll
- useWindowSize
00:00:00Stopwatch
import { useStopwatch } from '@siberiacancode/reactuse';
import { PauseIcon, PlayIcon, RotateCcwIcon } from 'lucide-react';
import { cn } from '@/utils/lib';
const SIZE = 240;
const CENTER = SIZE / 2;
const RADIUS = 100;
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
const TICKS = 60;
const pad = (value: number) => String(value).padStart(2, '0');
const Demo = () => {
const stopwatch = useStopwatch();
const running = !stopwatch.paused;
const secondInMinute = stopwatch.count % 60;
const progress = secondInMinute / 60;
const dashoffset = CIRCUMFERENCE * (1 - progress);
const animate = secondInMinute !== 0;
const onReset = () => {
stopwatch.pause();
stopwatch.reset();
};
return (
<section className='flex w-full max-w-xs flex-col items-center gap-8 p-6'>
<div className='relative' style={{ width: SIZE, height: SIZE }}>
<svg className='-rotate-90' height={SIZE} viewBox={`0 0 ${SIZE} ${SIZE}`} width={SIZE}>
{Array.from({ length: TICKS }).map((_, i) => {
const angle = (i / TICKS) * 2 * Math.PI;
const major = i % 5 === 0;
const outer = RADIUS - 8;
const inner = outer - (major ? 7 : 4);
const x1 = CENTER + outer * Math.cos(angle);
const y1 = CENTER + outer * Math.sin(angle);
const x2 = CENTER + inner * Math.cos(angle);
const y2 = CENTER + inner * Math.sin(angle);
return (
<line
key={i}
className='stroke-muted-foreground/25'
strokeLinecap='round'
strokeWidth={major ? 2.5 : 1.5}
x1={x1}
x2={x2}
y1={y1}
y2={y2}
/>
);
})}
<circle
className='stroke-muted/60'
cx={CENTER}
cy={CENTER}
fill='none'
r={RADIUS}
strokeWidth={9}
/>
<circle
className={cn(
'stroke-primary',
animate && 'transition-[stroke-dashoffset] duration-1000 ease-linear'
)}
cx={CENTER}
cy={CENTER}
fill='none'
r={RADIUS}
strokeDasharray={CIRCUMFERENCE}
strokeDashoffset={dashoffset}
strokeLinecap='round'
strokeWidth={9}
/>
</svg>
<div className='absolute inset-0 flex flex-col items-center justify-center'>
<span className='text-foreground font-mono text-3xl font-light tracking-tight tabular-nums'>
{pad(stopwatch.hours)}:{pad(stopwatch.minutes)}:{pad(stopwatch.seconds)}
</span>
<span className='text-muted-foreground mt-1 text-[10px] tracking-[0.2em] uppercase'>
{running ? 'Running' : stopwatch.count > 0 ? 'Paused' : 'Stopwatch'}
</span>
</div>
</div>
<div className='flex items-center gap-4'>
<button
aria-label='Reset'
className='rounded-full!'
data-size='icon-lg'
data-variant='ghost'
disabled={stopwatch.count === 0 && stopwatch.paused}
type='button'
onClick={onReset}
>
<RotateCcwIcon className='size-5' />
</button>
<button
aria-label={running ? 'Pause' : 'Start'}
className='size-14! rounded-full!'
data-size='icon-lg'
data-variant={running ? 'secondary' : 'default'}
type='button'
onClick={() => stopwatch.toggle()}
>
{running ? (
<PauseIcon className='size-5' fill='currentColor' />
) : (
<PlayIcon className='size-5 translate-x-0.5' fill='currentColor' />
)}
</button>
<span aria-hidden className='size-9' />
</div>
</section>
);
};
export default Demo;
Installation
pnpm add @siberiacancode/reactuseUsage
const { seconds, minutes, start, pause, reset } = useStopwatch(1000, { immediately: false });
// or
const { seconds, minutes, start, pause, reset } = useStopwatch({ initialTime: 1000, immediately: false });Type Declarations
export interface UseStopwatchReturn {
/** The total count of the stopwatch */
count: number;
/** The day count of the stopwatch */
days: number;
/** The hour count of the stopwatch */
hours: number;
/** The minute count of the stopwatch */
minutes: number;
/** The paused state of the stopwatch */
paused: boolean;
/** The second count of the stopwatch */
seconds: number;
/** The function to pause the stopwatch */
pause: () => void;
/** The function to reset the stopwatch */
reset: () => void;
/** The function to start the stopwatch */
start: () => void;
/** The function to toggle the stopwatch */
toggle: (active?: boolean) => void;
}
export interface UseStopwatchOptions {
/** The immediately state of the timer */
immediately?: boolean;
}
interface UseStopwatch {
(initialTime?: number, options?: UseStopwatchOptions): UseStopwatchReturn;
(options?: UseStopwatchOptions & { initialTime?: number }): UseStopwatchReturn;
}API
Parameters
| Name | Type | Default | Note |
|---|---|---|---|
| initialTime | number | 0 | The initial time of the timer |
| options.immediately | boolean | false | Start the stopwatch immediately |
Returns
UseStopwatchReturnParameters
| Name | Type | Default | Note |
|---|---|---|---|
| options.initialTime | number | 0 | The initial time of the timer |
| options.immediately | boolean | false | Start the stopwatch immediately |
Returns
UseStopwatchReturnContributors
ddebabinEEksiartssereda
Last updated on