32
loading...
This website collects cookies to deliver better user experience
const Stopwatch = () => {
const start = () => {} // also restarts
const mark = () => {}
const stop = () => {}
const reset = () => {}
}
Watch hands, one each to indicate minutes, seconds, and milliseconds. These were abstracted into a general Hand
component. Each hand will have some common styling, but will be differentiated by color. At any given time, each hand will be rotated according to its value, which we'll achieve by an inline style that applies a transform rule with translateX(-50%)
, and rotate
set to the applicable value via a value
prop.
Tick marks: A ring of light gray tick marks are spaced for each second (1/60), darker and thicker tick marks at 5-second intervals, and darker and even thicker tick marks at 15-second intervals. I used zero HTML/JSX elements to make these. They are created using a conical gradient in CSS applied to the ::before
pseudo element of the watch face. This was admittedly a little tricky to figure out at first, but the math was ultimately embarassingly simple:
background-image: repeating-conic-gradient(
from 359deg,
#555 0 2deg, transparent 2deg 90deg
), repeating-conic-gradient(
from 359.5deg,
#555 0 1deg, transparent 1deg 30deg
), repeating-conic-gradient(
from 359.75deg,
#ccc 0 .5deg, transparent .5deg 6deg
);
mask-image: radial-gradient(
circle at center,
transparent 66%,
#fff 66.1%
);
Math.floor(value)
to always round down to the nearest whole number, and the latter by applying Math.max(0, value)
to replace any value less than zero with zero. I saved this as a convenience function, and define some useful constants:const getNumOrZero = num => Math.floor(Math.max(0, num))
const ONE_SECOND_MS = 1000
const ONE_MINUTE_MS = ONE_SECOND_MS * 60
elapsed
milliseconds by the number of milliseconds in a minute (ONE_MINUTE_MS
), rounding down to get the whole minutes without the remainder (Math.floor
via getNumOrZero()
):const wholeMinutesValue = getNumOrZero(elapsed / ONE_MINUTE_MS)
ONE_MINUTE_MS
:const wholeMinutesInMs = wholeMinutesValue * ONE_MINUTE_MS
elapsed
milliseconds, minus the wholeMinutesInMs
calculated above, by ONE_SECOND_MS
(milliseconds in a second). This gives me the number of whole seconds remaining after subtracting the whole minutes:const wholeSecondsValue = getNumOrZero((elapsed - wholeMinutesInMs) / ONE_SECOND_MS)
const wholeSecondsInMs = wholeSecondsValue * ONE_SECOND_MS
wholeMinutesInMs
and wholeSecondsInMs
from the total elapsed time in milliseconds:const millisecsValue = elapsed - wholeMinutesInMs - wholeSecondsInMs
const elapsedFormatted = `${wholeMinutesValue.toString().padStart(2, '0')}:` +
`${wholeSecondsValue.toString().padStart(2, '0')}:` +
`${millisecsValue.toString().padStart(3, '0')}`
<ol className="time lap" reversed>{ lapList }</ol>
lapList
is an array of lap timestamps in the same MM:SS:ss format as the digital readout. Note the reversed
HTML attribute, which (as you might suspect) reverses the order of an ordered list.