jkorstad's picture
Update index.html
99b9f38 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dot Counter Stopwatch/Timer</title>
<style>
:root {
--color-bg: #1e1e1e;
--color-dot: #ffffff;
--color-active: #f39c12;
--color-inactive: #555555;
--dot-size: 8px;
--grid-gap: 4px;
--animation-duration: 0.3s;
--font-size: 16px;
--container-width: 300px;
}
body {
margin: 0;
padding: 0;
background-color: var(--color-bg);
color: var(--color-dot);
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
overflow: hidden;
box-sizing: border-box;
padding: 20px;
}
.controls {
margin-bottom: 20px;
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
.controls button, .controls input {
padding: 8px 12px;
font-size: var(--font-size);
border: none;
border-radius: 5px;
cursor: pointer;
}
.controls input[type="color"] {
border: none;
padding: 0;
width: 40px;
height: 40px;
}
.controls button {
background-color: var(--color-active);
color: var(--color-bg);
transition: background-color var(--animation-duration);
}
.controls button:hover {
background-color: #d35400;
}
.controls input {
background-color: var(--color-bg);
color: var(--color-dot);
border: 1px solid var(--color-dot);
width: 60px;
text-align: center;
}
.timer-display {
font-size: 24px;
margin-bottom: 20px;
text-align: center;
}
.label {
font-size: var(--font-size);
margin: 10px 0;
text-align: center;
}
.grid-row {
display: flex;
gap: var(--grid-gap);
margin-bottom: 10px;
}
.grid-container {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.dot {
width: var(--dot-size);
height: var(--dot-size);
background-color: var(--color-dot);
border-radius: 50%;
transition: background-color var(--animation-duration);
}
.dot.active {
background-color: var(--color-active);
}
.dot.inactive {
background-color: var(--color-inactive);
}
@media (min-width: 600px) {
:root {
--dot-size: 10px;
--grid-gap: 6px;
--container-width: 400px;
}
}
@media (min-width: 900px) {
:root {
--dot-size: 12px;
--grid-gap: 8px;
--container-width: 500px;
}
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.dot.active {
animation: fadeIn var(--animation-duration);
}
.dot.inactive {
animation: fadeIn var(--animation-duration);
}
</style>
</head>
<body>
<div class="controls">
<div>
<label for="hours-input">Hours:</label>
<input type="number" id="hours-input" value="0" min="0">
</div>
<div>
<label for="minutes-input">Minutes:</label>
<input type="number" id="minutes-input" value="0" min="0">
</div>
<div>
<label for="seconds-input">Seconds:</label>
<input type="number" id="seconds-input" value="0" min="0">
</div>
<button id="start-timer">Start Timer</button>
<button id="pause">Pause</button>
<button id="reset">Reset</button>
<div> | </div>
<button id="start-stopwatch">Start Stopwatch</button>
</div>
<div>
<label for="active-dot-color">Active Dot Color:</label>
<input type="color" id="active-dot-color" value="#f39c12">
</div>
<div>
<label for="inactive-dot-color">Inactive Dot Color:</label>
<input type="color" id="inactive-dot-color" value="#555555">
</div>
<div class="timer-display" id="timer-display">00:00:00</div>
<div class="grid-container">
<div class="label">Seconds:</div>
<div class="grid-row" id="seconds-row-1"></div>
<div class="grid-row" id="seconds-row-2"></div>
<div class="grid-row" id="seconds-row-3"></div>
</div>
<div class="grid-container">
<div class="label">Minutes:</div>
<div class="grid-row" id="minutes-row-1"></div>
<div class="grid-row" id="minutes-row-2"></div>
<div class="grid-row" id="minutes-row-3"></div>
</div>
<div class="grid-container">
<div class="label">Hours:</div>
<div class="grid-row" id="hours-row-1"></div>
<div class="grid-row" id="hours-row-2"></div>
<div class="grid-row" id="hours-row-3"></div>
</div>
<script>
const startStopwatchButton = document.getElementById('start-stopwatch');
const startTimerButton = document.getElementById('start-timer');
const pauseButton = document.getElementById('pause');
const resetButton = document.getElementById('reset');
const timerDisplay = document.getElementById('timer-display');
const secondsRows = [
document.getElementById('seconds-row-1'),
document.getElementById('seconds-row-2'),
document.getElementById('seconds-row-3')
];
const minutesRows = [
document.getElementById('minutes-row-1'),
document.getElementById('minutes-row-2'),
document.getElementById('minutes-row-3')
];
const hoursRows = [
document.getElementById('hours-row-1'),
document.getElementById('hours-row-2'),
document.getElementById('hours-row-3')
];
const hoursInput = document.getElementById('hours-input');
const minutesInput = document.getElementById('minutes-input');
const secondsInput = document.getElementById('seconds-input');
let timerInterval;
let totalSeconds = 0;
let isRunning = false;
let isPaused = false;
let isStopwatch = true;
let previousSeconds = 0, previousMinutes = 0, previousHours = 0;
function updateDisplay() {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
if (!isStopwatch && totalSeconds <= 0) {
timerDisplay.textContent = "DONE";
timerDisplay.style.color = "green";
timerDisplay.style.fontSize = "3em"; // Large font size for "DONE"
} else {
timerDisplay.textContent = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
timerDisplay.style.color = ""; // Reset color
timerDisplay.style.fontSize = "24px"; // Reset font size
}
}
function initializeGrid(rowGroup, maxCount) {
for (let i = 0; i < maxCount; i++) {
const dot = document.createElement('div');
dot.classList.add('dot', 'inactive');
const rowIndex = Math.floor(i / (maxCount / rowGroup.length));
rowGroup[rowIndex].appendChild(dot);
}
}
// Initialize all grids
initializeGrid(secondsRows, 60);
initializeGrid(minutesRows, 60);
initializeGrid(hoursRows, 24);
function updateGrids() {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
updateGridRow(secondsRows, previousSeconds, seconds, 60);
previousSeconds = seconds;
updateGridRow(minutesRows, previousMinutes, minutes, 60);
previousMinutes = minutes;
updateGridRow(hoursRows, previousHours, hours, 24);
previousHours = hours;
}
function updateGridRow(rows, previous, current, max) {
const start = Math.min(previous, current);
const end = Math.max(previous, current);
for (let i = start; i < end; i++) {
const rowIndex = Math.floor(i / (max / rows.length));
const dotIndex = i % (max / rows.length);
const dot = rows[rowIndex].children[dotIndex];
if (dot) {
dot.classList.remove('active', 'inactive');
dot.classList.add(i < current ? 'active' : 'inactive');
}
}
}
//ADDED
// Get references to the new color inputs
const activeDotColorInput = document.getElementById('active-dot-color');
const inactiveDotColorInput = document.getElementById('inactive-dot-color');
// Function to update CSS variables for dot colors
function updateDotColors() {
document.documentElement.style.setProperty('--color-active', activeDotColorInput.value);
document.documentElement.style.setProperty('--color-inactive', inactiveDotColorInput.value);
// Update all dots' colors immediately
const allDots = document.querySelectorAll('.dot');
allDots.forEach(dot => {
if (dot.classList.contains('active')) {
dot.style.backgroundColor = activeDotColorInput.value;
} else {
dot.style.backgroundColor = inactiveDotColorInput.value;
}
});
}
// Event listeners for color changes
activeDotColorInput.addEventListener('input', updateDotColors);
inactiveDotColorInput.addEventListener('input', updateDotColors);
// Modify the updateGridRow function to use the new color system
function updateGridRow(rows, previous, current, max) {
const start = Math.min(previous, current);
const end = Math.max(previous, current);
for (let i = start; i < end; i++) {
const rowIndex = Math.floor(i / (max / rows.length));
const dotIndex = i % (max / rows.length);
const dot = rows[rowIndex].children[dotIndex];
if (dot) {
dot.classList.remove('active', 'inactive');
dot.classList.add(i < current ? 'active' : 'inactive');
// Apply the color directly here for immediate visual feedback
dot.style.backgroundColor = i < current ? activeDotColorInput.value : inactiveDotColorInput.value;
}
}
}
// Initial update to set default colors
updateDotColors();
//ADDED STOP
//NEW ADDED - Keyboard Functionality
// Function to handle key press events
function handleKeyPress(event) {
// Check if the pressed key corresponds to any action
if (event.key in keyActions) {
keyActions[event.key]();
event.preventDefault(); // Prevent the default action of the key if necessary
}
}
// Add event listener for keydown events
document.addEventListener('keydown', handleKeyPress);
//NEW ADDED STOP
// Make sure the start function doesn't reset totalSeconds when resuming
function start() {
if (!isRunning) {
// Set totalSeconds only if we're starting anew or if it's a timer
if (isStopwatch && totalSeconds === 0) {
totalSeconds = 0; // For stopwatch, only reset if starting from 0
} else if (!isStopwatch && totalSeconds <= 0) {
totalSeconds = parseInt(hoursInput.value) * 3600 + parseInt(minutesInput.value) * 60 + parseInt(secondsInput.value);
}
timerInterval = setInterval(() => {
if (isStopwatch) {
totalSeconds++;
} else {
totalSeconds--;
if (totalSeconds < 0) {
totalSeconds = 0;
clearInterval(timerInterval);
isRunning = false;
updateDisplay(); // Update display one last time to show "DONE"
}
}
updateDisplay();
updateGrids();
}, 1000);
isRunning = true;
}
}
function pause() {
if (isRunning) {
if (!isPaused) {
clearInterval(timerInterval);
isPaused = true;
pauseButton.textContent = "Play"; // Change button text to 'Play'
isRunning = false; // Set isRunning to false when paused
} else {
// Use start() to resume, which now doesn't reset totalSeconds for stopwatch
start();
isPaused = false;
pauseButton.textContent = "Pause"; // Change button text back to 'Pause'
}
} else if (isPaused) {
// This case is for when you click "Play" after pausing
isPaused = false;
pauseButton.textContent = "Pause";
start(); // Call start to resume from where it was paused
}
}
function reset() {
clearInterval(timerInterval);
isRunning = false;
totalSeconds = 0;
previousSeconds = previousMinutes = previousHours = 0;
isStopwatch = true;
updateDisplay();
// Clear all dots
clearGrid(secondsRows);
clearGrid(minutesRows);
clearGrid(hoursRows);
// Reinitialize the grids
initializeGrid(secondsRows, 60);
initializeGrid(minutesRows, 60);
initializeGrid(hoursRows, 24);
hoursInput.value = minutesInput.value = secondsInput.value = '0';
isPaused = false; // Reset pause state
pauseButton.textContent = "Pause"; // Reset button text
}
function clearGrid(rows) {
rows.forEach(row => row.innerHTML = '');
}
startStopwatchButton.addEventListener('click', () => {
isStopwatch = true;
start();
});
startTimerButton.addEventListener('click', () => {
isStopwatch = false;
start();
});
pauseButton.addEventListener('click', pause);
resetButton.addEventListener('click', reset);
// Initial display update
updateDisplay();
updateGrids();
</script>
</body>
</html>