⏳ A small and smooth (60-fps) countdown timer and stopwatch for Vue and vanilla JS.
This project is part of #CreateWeekly, my attempt to create something new publicly every week in 2020.
npm install @josephuspaye/timer --save
The following creates a 15-second countdown timer without a user interface:
import{Timer}from'@josephuspaye/timer';consttimer=newTimer('countdown',10*1000);// change 'countdown' to 'stopwatch' for a stopwatchtimer.on('tick',(ms)=>{console.log('event: tick',ms);});timer.on('done',()=>{console.log('event: done');});timer.on('state',(state)=>{console.log('event: state',state);});timer.start();You can create a timer with the default template, which shows days, hours, minutes, seconds, and milliseconds. You can also use the following classes to style the resulting timer:
timer- added to the timer root element (adivelement).is-doneis appended when the timer reaches its length, andis-overflowedis appended when it exceeds its length.timer__delimiter- added to the:delimiters (spanelements)timer__days- added to the timer days (aspanelement)timer__hours- added to the timer hours (aspanelement)timer__seconds- added to the timer seconds (aspanelement)timer__milliseconds- added to the timer milliseconds (aspanelement)
The following creates a simple 15-second countdown timer that starts automatically (view on CodePen):
<template>
<div>
<Timer type="countdown" autoStart :length="15*1000" />
<!-- change the above to `type="stopwatch"` for a stopwatch -->
</div>
</template>
<script>import { TimerVueasTimer } from'@josephuspaye/timer';exportdefault { components: { Timer },};</script>You can use a scoped slot to render your own template.
The following renders a custom template with 15-second stopwatch that starts automatically and allows for overflow (view on CodePen):
<template>
<div>
<Timer
type="stopwatch"
auto-start
allow-overflow
:length="15*1000"v-slot="{ time, isDone, isOverflowed }"
>
<!-- change the above to `type="countdown"` for a countdown timer -->
<div>
<span:class="{ 'is-overflowed': isOverflowed }">
<span>{{ time.d }}d</span>
<span>{{ time.h }}h</span>
<span>{{ time.m }}m</span>
<span>{{ time.s }}s</span>
<span>{{ time.ms }}ms</span>
</span>
<span>{{ isDone ? '✅' : '⏳' }}</span>
</div>
</Timer>
</div>
</template>
<script>import { TimerVueasTimer } from'@josephuspaye/timer';exportdefault { components: { Timer },};</script>
<style>.is-overflowed {color: red;}</style>The Timer class can be used to create a timer without a user interface.
Create a timer using const timer = new Timer(type, options) and call methods using timer.method().
/** * The type of timer: 'countdown' or 'stopwatch' */exportdeclaretypeTimerType='countdown'|'stopwatch';/** * The timer state, one of 'stopped', 'running', 'paused' */exportdeclaretypeTimerState='stopped'|'running'|'paused';/** * Timer constructor options */exportinterfaceTimerOptions{/** * Whether the timer should allow overflow. If true, the timer will * set `isOverflowed` and continue counting when the length is * reached. Otherwise, the timer stops upon reaching the length. */allowOverflow?: boolean;}exportdeclareclassTimer{/** * The type of timer: 'countdown' or 'stopwatch' */type: TimerType;/** * The timer state, one of 'stopped', 'running', 'paused' */state: TimerState;/** * Indicates whether the timer allows overflow. When true, the timer * will set `isOverflowed` and continue counting when the length is * reached. Otherwise, the timer stops upon reaching the length. */allowOverflow: boolean;/** * Indicates whether the timer has overflowed (i.e. exceeded the length) */isOverflowed: boolean;/** * Indicates whether the timer is done (i.e. met or exceeded the length) */isDone: boolean;/** * The timer event emitter */events: Emitter;/** * Create a new timer * * @param type The type of timer: 'countdown' or 'stopwatch' * @param length How long the timer should count down from or count up to * @param options The timer options */constructor(type: TimerType,length: number,options?: TimerOptions);/** * The elapsed time in milliseconds. Is zero if the timer is stopped. */gettime(): number;/** * Set the timer's length. Will stop the timer and reset internal state for a new run. */setLength(length: number): void;/** * Reset the timer */reset(): void;/** * Start the timer */start(): void;/** * Stop the timer */stop(options?: {isDone: boolean}): void;/** * Pause the timer */pause(): void;/** * Resume the timer */resume(): void;/** * Destroy the timer and event listeners */destroy(): void;}Listen for any of the following timer events using timer.events.on('event', callbackFunction):
| Event | Description |
|---|---|
state | Emitted when the timer state changes. The handler is called with the new state, one of stopped, running or paused. |
overflow | Emitted when the timer exceeds its length. |
done | Emitted when the timer reaches its length. |
tick | Emitted when the timer advances. The handler is called with the total time elapsed, in milliseconds. When the timer is running, this is emitted 60 times a second. |
reset | Emitted when the timer is reset. The handler is called with the time it was reset to, in milliseconds. |
start | Emitted when the timer is started. |
stop | Emitted when the timer is stopped. |
pause | Emitted when the timer is paused. |
resume | Emitted when the timer is resumed. |
| Prop | Type | Default | Description |
|---|---|---|---|
type | String | countdown | The type of timer. One of countdown or stopwatch. This value can be changed while the timer is running. |
length | Number | How long the timer is running for, in milliseconds. Changing this while the timer is running will cause it to stop and reset. | |
autoStart | Boolean | false | When true, the timer will start automatically on mount. Otherwise the start() method has to be called to start it. |
allowOverflow | Boolean | false | When true, the timer will continue when its length is exceeded. Otherwise it will stop upon reaching its length. |
| Event | Description |
|---|---|
start() | Start the timer. |
stop() | Stop the timer. |
pause() | Pause the timer if it's running. |
resume() | Resume the timer if it's paused. |
toggle() | Toggle the timer. Will start the timer if stopped, pause if running, and resume if paused. |
reset() | Stop and reset the timer, for a possible restart. |
| Event | Description |
|---|---|
state | Emitted when the timer state changes. The handler is called with the new state, one of stopped, running or paused. |
overflow | Emitted when the timer exceeds its length. |
done | Emitted when the timer reaches its length. |
tick | Emitted when the timer advances. The handler is called with the total time elapsed, in milliseconds. When the timer is running, this is emitted 60 times a second. |
reset | Emitted when the timer is reset. The handler is called with the time it was reset to, in milliseconds. |
start | Emitted when the timer is started. |
stop | Emitted when the timer is stopped. |
pause | Emitted when the timer is paused. |
resume | Emitted when the timer is resumed. |
| Slot | Description |
|---|---|
| (default) | The default slot. Can hold any content with a single root element, and is passed the props listed below. |
| Prop | Type | Description |
|---|---|---|
time | Object | The timer's current time as an object with keys d (days), h (hours), m (minutes), s (seconds), and ms (milliseconds). Each value is a padded string ready for display. |
timeElapsed | Number | For a countdown, the total time remaining, in milliseconds. For a stopwatch, the total time elapsed, in milliseconds. |
state | String | The timer's current state, one of stopped, running or paused. |
isDone | Boolean | Whether or not the timer has reached its length. |
isOverflowed | Boolean | Whether or not the timer has exceeded its length. |