JSFiddle - React, Tailwind, and code Playground

by kirito0709

HTML

<nav class="controls">
			<a href="#" class="button" onClick="stopwatch.start();">Start</a>
			<a href="#" class="button" onClick="stopwatch.lap();">Lap</a>
			<a href="#" class="button" onClick="stopwatch.stop();">Stop</a>
			<a href="#" class="button" onClick="stopwatch.restart();">Restart</a>
			<a href="#" class="button" onClick="stopwatch.clear();">Clear Laps</a>
		</nav>
		<div class="stopwatch"></div>
		<ul class="results"></ul>

CSS

* {
  margin: 0;
  padding: 0;
}

html {
  background: #333;
  color: #bbb;
  font-family: Menlo;
}

.controls {
  text-align: center;
  top: 1em;
  width: 100%;
}

.button {
  color: #bbb;
  font-size: 18px;
  margin: 0 0.5em;
  text-decoration: none;
  display: inline-block;
}

.button:first-child {
    margin-left: 0;
}

.button:last-child {
    margin-right: 0;
}

.button:hover {
  color: white;
}

.stopwatch {
  font-size: 40px;
  text-align: center;
}

.results {
  border-color: lime;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
}

JavaScript

class Stopwatch {
    constructor(display, results) {
        this.running = false;
        this.display = display;
        this.results = results;
        this.laps = [];
        this.reset();
        this.print(this.times);
    }
    
    reset() {
        this.times = [ 0, 0, 0 ];
    }
    
    start() {
        if (!this.time) this.time = performance.now();
        if (!this.running) {
            this.running = true;
            requestAnimationFrame(this.step.bind(this));
        }
    }
    
    lap() {
        let times = this.times;
        let li = document.createElement('li');
        li.innerText = this.format(times);
        this.results.appendChild(li);
    }
    
    stop() {
        this.running = false;
        this.time = null;
    }

    restart() {
        if (!this.time) this.time = performance.now();
        if (!this.running) {
            this.running = true;
            requestAnimationFrame(this.step.bind(this));
        }
        this.reset();
    }
    
    clear() {
        clearChildren(this.results);
    }
    
    step(timestamp) {
        if (!this.running) return;
        this.calculate(timestamp);
        this.time = timestamp;
        this.print();
        requestAnimationFrame(this.step.bind(this));
    }
    
    calculate(timestamp) {
        var diff = timestamp - this.time;
        // Hundredths of a second are 100 ms
        this.times[2] += diff / 10;
        // Seconds are 100 hundredths of a second
        if (this.times[2] >= 100) {
            this.times[1] += 1;
            this.times[2] -= 100;
        }
        // Minutes are 60 seconds
        if (this.times[1] >= 60) {
            this.times[0] += 1;
            this.times[1] -= 60;
        }
    }
    
    print() {
        this.display.innerText = this.format(this.times);
    }
    
    format(times) {
        return `\
${pad0(times[0], 2)}:\
${pad0(times[1], 2)}:\
${pad0(Math.floor(times[2]), 2)}`;
    }
}

function pad0(value, count) {
    var result =...