| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| <title>Countdown Timer</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Arial', sans-serif; |
| -webkit-tap-highlight-color: transparent; |
| } |
| |
| body { |
| background: linear-gradient(135deg, #1a1a2e, #16213e, #0f3460); |
| color: white; |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| overflow: hidden; |
| position: relative; |
| touch-action: manipulation; |
| } |
| |
| .timer-container { |
| text-align: center; |
| z-index: 1; |
| padding: 1.5rem; |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 20px; |
| backdrop-filter: blur(10px); |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| width: 95%; |
| max-width: 600px; |
| transition: all 0.3s ease; |
| margin: 1rem; |
| } |
| |
| h1 { |
| font-size: clamp(1.5rem, 5vw, 2.5rem); |
| margin-bottom: 1.5rem; |
| text-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); |
| letter-spacing: 1px; |
| } |
| |
| .timer { |
| display: flex; |
| justify-content: space-around; |
| gap: 0.5rem; |
| margin-bottom: 1.5rem; |
| } |
| |
| .time-box { |
| position: relative; |
| width: 30%; |
| max-width: 120px; |
| min-width: 70px; |
| aspect-ratio: 0.7; |
| perspective: 1000px; |
| } |
| |
| .time-value { |
| position: absolute; |
| width: 100%; |
| height: 70%; |
| top: 0; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: clamp(2.5rem, 8vw, 5rem); |
| font-weight: bold; |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 10px; |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); |
| transition: all 0.3s ease; |
| backface-visibility: hidden; |
| transform-style: preserve-3d; |
| } |
| |
| .time-value.flip { |
| animation: flip 0.6s cubic-bezier(0.455, 0.03, 0.515, 0.955) both; |
| } |
| |
| @keyframes flip { |
| 0% { |
| transform: rotateX(0deg); |
| } |
| 100% { |
| transform: rotateX(-180deg); |
| } |
| } |
| |
| .time-label { |
| position: absolute; |
| bottom: 0; |
| width: 100%; |
| padding-top: 1rem; |
| font-size: clamp(0.8rem, 3vw, 1.2rem); |
| opacity: 0.8; |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| } |
| |
| .progress-bar { |
| width: 100%; |
| height: 6px; |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 3px; |
| margin: 1rem 0; |
| overflow: hidden; |
| } |
| |
| .progress { |
| height: 100%; |
| background: linear-gradient(90deg, #ff6b6b, #ff8e8e); |
| width: 100%; |
| transition: width 0.3s ease; |
| } |
| |
| .controls { |
| display: flex; |
| gap: 0.5rem; |
| justify-content: center; |
| margin-top: 1rem; |
| } |
| |
| button { |
| padding: 0.7rem 1.2rem; |
| font-size: clamp(0.9rem, 3.5vw, 1rem); |
| background: rgba(255, 255, 255, 0.2); |
| color: white; |
| border: none; |
| border-radius: 50px; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| backdrop-filter: blur(5px); |
| flex: 1; |
| max-width: 120px; |
| justify-content: center; |
| user-select: none; |
| } |
| |
| button:active { |
| transform: scale(0.95); |
| } |
| |
| button i { |
| font-size: 1rem; |
| } |
| |
| .set-timer { |
| display: flex; |
| gap: 0.5rem; |
| margin-top: 1.5rem; |
| justify-content: center; |
| align-items: center; |
| flex-wrap: wrap; |
| } |
| |
| .time-input-wrapper { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| flex: 1; |
| min-width: 80px; |
| } |
| |
| .time-input { |
| position: relative; |
| width: 100%; |
| } |
| |
| .time-input input { |
| padding: 0.6rem; |
| width: 100%; |
| text-align: center; |
| background: rgba(255, 255, 255, 0.1); |
| border: 1px solid rgba(255, 255, 255, 0.2); |
| border-radius: 5px; |
| color: white; |
| font-size: clamp(1rem, 4vw, 1.5rem); |
| font-weight: bold; |
| appearance: textfield; |
| -moz-appearance: textfield; |
| min-height: 50px; |
| } |
| |
| .time-input input::-webkit-outer-spin-button, |
| .time-input input::-webkit-inner-spin-button { |
| -webkit-appearance: none; |
| margin: 0; |
| } |
| |
| .arrows { |
| position: absolute; |
| right: 5px; |
| top: 0; |
| height: 100%; |
| pointer-events: none; |
| display: flex; |
| flex-direction: column; |
| justify-content: space-between; |
| } |
| |
| .arrow-btn { |
| position: relative; |
| width: 20px; |
| height: calc(50% - 2px); |
| padding: 0; |
| background: transparent; |
| border: none; |
| cursor: pointer; |
| pointer-events: all; |
| } |
| |
| .arrow-btn:after { |
| content: ''; |
| width: 0; |
| height: 0; |
| border-left: 5px solid transparent; |
| border-right: 5px solid transparent; |
| border-bottom: 5px solid white; |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| opacity: 0.7; |
| } |
| |
| .arrow-btn.down:after { |
| transform: translate(-50%, -50%) rotate(180deg); |
| } |
| |
| .time-label-input { |
| margin-top: 0.5rem; |
| font-size: clamp(0.7rem, 3vw, 1rem); |
| opacity: 0.8; |
| text-transform: uppercase; |
| } |
| |
| .modal { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: rgba(0, 0, 0, 0.8); |
| z-index: 100; |
| justify-content: center; |
| align-items: center; |
| flex-direction: column; |
| animation: fadeIn 0.5s ease; |
| } |
| |
| .modal-content { |
| background: linear-gradient(135deg, #16213e, #0f3460); |
| padding: 2rem; |
| border-radius: 15px; |
| text-align: center; |
| width: 90%; |
| max-width: 400px; |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); |
| position: relative; |
| margin: 1rem; |
| } |
| |
| .close-modal { |
| position: absolute; |
| top: -10px; |
| right: -10px; |
| background: #ff6b6b; |
| width: 30px; |
| height: 30px; |
| border-radius: 50%; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| .close-modal i { |
| font-size: 0.9rem; |
| } |
| |
| .modal h2 { |
| font-size: clamp(1.8rem, 6vw, 3rem); |
| margin-bottom: 1rem; |
| color: #ff6b6b; |
| } |
| |
| .modal p { |
| font-size: clamp(1rem, 4vw, 1.2rem); |
| margin-bottom: 1.5rem; |
| } |
| |
| #newTimerBtn { |
| width: 100%; |
| max-width: 200px; |
| margin: 0 auto; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| |
| .confetti { |
| position: absolute; |
| width: 10px; |
| height: 10px; |
| background: #f00; |
| opacity: 0; |
| pointer-events: none; |
| } |
| |
| |
| @media (orientation: portrait) and (max-height: 700px) { |
| .timer-container { |
| padding: 1rem; |
| margin-top: 0; |
| } |
| h1 { |
| margin-bottom: 1rem; |
| } |
| .time-value { |
| height: 60%; |
| } |
| .time-label { |
| padding-top: 0.5rem; |
| } |
| } |
| |
| @media (orientation: landscape) and (max-height: 500px) { |
| .timer-container { |
| padding: 0.5rem; |
| max-width: 90%; |
| } |
| h1 { |
| margin-bottom: 0.5rem; |
| font-size: 1.5rem; |
| } |
| .timer { |
| margin-bottom: 0.5rem; |
| } |
| .controls { |
| margin-top: 0.5rem; |
| } |
| .set-timer { |
| margin-top: 0.5rem; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="particles" id="particles"></div> |
|
|
| <div class="timer-container"> |
| <h1>Countdown Timer</h1> |
| <div class="timer"> |
| <div class="time-box"> |
| <div class="time-value" id="hours">00</div> |
| <div class="time-label">Hours</div> |
| </div> |
| <div class="time-box"> |
| <div class="time-value" id="minutes">00</div> |
| <div class="time-label">Minutes</div> |
| </div> |
| <div class="time-box"> |
| <div class="time-value" id="seconds">00</div> |
| <div class="time-label">Seconds</div> |
| </div> |
| </div> |
| |
| <div class="progress-bar"> |
| <div class="progress" id="progress"></div> |
| </div> |
|
|
| <div class="controls"> |
| <button id="startBtn"><i class="fas fa-play"></i> Start</button> |
| <button id="pauseBtn"><i class="fas fa-pause"></i> Pause</button> |
| <button id="resetBtn"><i class="fas fa-redo"></i> Reset</button> |
| </div> |
|
|
| <div class="set-timer"> |
| <div class="time-input-wrapper"> |
| <div class="time-input"> |
| <input type="number" id="inputHours" min="0" max="99" value="0"> |
| <div class="arrows"> |
| <button class="arrow-btn" onclick="incrementHours()"></button> |
| <button class="arrow-btn down" onclick="decrementHours()"></button> |
| </div> |
| </div> |
| <div class="time-label-input">Hours</div> |
| </div> |
| <div class="time-input-wrapper"> |
| <div class="time-input"> |
| <input type="number" id="inputMinutes" min="0" max="59" value="10"> |
| <div class="arrows"> |
| <button class="arrow-btn" onclick="incrementMinutes()"></button> |
| <button class="arrow-btn down" onclick="decrementMinutes()"></button> |
| </div> |
| </div> |
| <div class="time-label-input">Minutes</div> |
| </div> |
| <div class="time-input-wrapper"> |
| <div class="time-input"> |
| <input type="number" id="inputSeconds" min="0" max="59" value="0"> |
| <div class="arrows"> |
| <button class="arrow-btn" onclick="incrementSeconds()"></button> |
| <button class="arrow-btn down" onclick="decrementSeconds()"></button> |
| </div> |
| </div> |
| <div class="time-label-input">Seconds</div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="modal" id="modal"> |
| <div class="modal-content"> |
| <div class="close-modal" id="closeModal"> |
| <i class="fas fa-times"></i> |
| </div> |
| <h2>Time's Up!</h2> |
| <p>Your countdown has reached zero.</p> |
| <button id="newTimerBtn"><i class="fas fa-plus"></i> New Timer</button> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const hoursElement = document.getElementById('hours'); |
| const minutesElement = document.getElementById('minutes'); |
| const secondsElement = document.getElementById('seconds'); |
| const startBtn = document.getElementById('startBtn'); |
| const pauseBtn = document.getElementById('pauseBtn'); |
| const resetBtn = document.getElementById('resetBtn'); |
| const inputHours = document.getElementById('inputHours'); |
| const inputMinutes = document.getElementById('inputMinutes'); |
| const inputSeconds = document.getElementById('inputSeconds'); |
| const modal = document.getElementById('modal'); |
| const closeModal = document.getElementById('closeModal'); |
| const newTimerBtn = document.getElementById('newTimerBtn'); |
| const progress = document.getElementById('progress'); |
| const particles = document.getElementById('particles'); |
| |
| |
| let countdownInterval; |
| let totalSeconds = 0; |
| let remainingSeconds = 0; |
| let isRunning = false; |
| |
| |
| function createParticles() { |
| for (let i = 0; i < 30; i++) { |
| const particle = document.createElement('div'); |
| particle.className = 'confetti'; |
| particle.style.left = `${Math.random() * 100}vw`; |
| particle.style.top = `${Math.random() * 100}vh`; |
| particle.style.width = `${5 + Math.random() * 8}px`; |
| particle.style.height = particle.style.width; |
| particle.style.backgroundColor = `hsl(${Math.random() * 360}, 100%, 50%)`; |
| particles.appendChild(particle); |
| } |
| } |
| |
| |
| createParticles(); |
| |
| |
| function updateDisplay(seconds) { |
| const hours = Math.floor(seconds / 3600); |
| const minutes = Math.floor((seconds % 3600) / 60); |
| const secs = seconds % 60; |
| |
| |
| if (hoursElement.textContent !== hours.toString().padStart(2, '0')) { |
| hoursElement.classList.add('flip'); |
| setTimeout(() => { |
| hoursElement.textContent = hours.toString().padStart(2, '0'); |
| hoursElement.classList.remove('flip'); |
| }, 300); |
| } |
| |
| if (minutesElement.textContent !== minutes.toString().padStart(2, '0')) { |
| minutesElement.classList.add('flip'); |
| setTimeout(() => { |
| minutesElement.textContent = minutes.toString().padStart(2, '0'); |
| minutesElement.classList.remove('flip'); |
| }, 300); |
| } |
| |
| if (secondsElement.textContent !== secs.toString().padStart(2, '0')) { |
| secondsElement.classList.add('flip'); |
| setTimeout(() => { |
| secondsElement.textContent = secs.toString().padStart(2, '0'); |
| secondsElement.classList.remove('flip'); |
| }, 300); |
| } |
| |
| |
| if (totalSeconds > 0) { |
| const progressPercent = (seconds / totalSeconds) * 100; |
| progress.style.width = `${progressPercent}%`; |
| |
| if (progressPercent < 20) { |
| progress.style.background = 'linear-gradient(90deg, #ff6b6b, #ff3e3e)'; |
| } else if (progressPercent < 50) { |
| progress.style.background = 'linear-gradient(90deg, #ffa36b, #ff6b6b)'; |
| } |
| } |
| } |
| |
| |
| function startCountdown() { |
| if (isRunning) return; |
| |
| |
| const hours = parseInt(inputHours.value) || 0; |
| const minutes = parseInt(inputMinutes.value) || 0; |
| const seconds = parseInt(inputSeconds.value) || 0; |
| |
| if (hours === 0 && minutes === 0 && seconds === 0) { |
| alert('Please set a valid time before starting.'); |
| return; |
| } |
| |
| remainingSeconds = hours * 3600 + minutes * 60 + seconds; |
| totalSeconds = remainingSeconds; |
| |
| isRunning = true; |
| updateDisplay(remainingSeconds); |
| |
| countdownInterval = setInterval(() => { |
| remainingSeconds--; |
| |
| if (remainingSeconds < 0) { |
| clearInterval(countdownInterval); |
| isRunning = false; |
| showModal(); |
| triggerConfetti(); |
| return; |
| } |
| |
| updateDisplay(remainingSeconds); |
| }, 1000); |
| } |
| |
| |
| function pauseCountdown() { |
| if (!isRunning) return; |
| clearInterval(countdownInterval); |
| isRunning = false; |
| } |
| |
| |
| function resetCountdown() { |
| clearInterval(countdownInterval); |
| isRunning = false; |
| |
| const hours = parseInt(inputHours.value) || 0; |
| const minutes = parseInt(inputMinutes.value) || 0; |
| const seconds = parseInt(inputSeconds.value) || 0; |
| remainingSeconds = hours * 3600 + minutes * 60 + seconds; |
| totalSeconds = remainingSeconds; |
| updateDisplay(remainingSeconds); |
| progress.style.background = 'linear-gradient(90deg, #ff6b6b, #ff8e8e)'; |
| } |
| |
| |
| function showModal() { |
| modal.style.display = 'flex'; |
| } |
| |
| |
| function closeModalFunc() { |
| modal.style.display = 'none'; |
| |
| document.querySelectorAll('.confetti').forEach(el => { |
| el.style.transform = 'translate(0, 0) rotate(0deg)'; |
| el.style.opacity = '0'; |
| }); |
| } |
| |
| |
| function startNewTimer() { |
| closeModalFunc(); |
| inputHours.value = '0'; |
| inputMinutes.value = '10'; |
| inputSeconds.value = '0'; |
| resetCountdown(); |
| } |
| |
| |
| function triggerConfetti() { |
| const confettiElements = document.querySelectorAll('.confetti'); |
| confettiElements.forEach((el, index) => { |
| setTimeout(() => { |
| el.style.opacity = '1'; |
| el.style.transform = `translate(${(Math.random() - 0.5) * 100}px, ${(Math.random() - 0.5) * 100}px) rotate(${Math.random() * 360}deg)`; |
| el.style.transition = 'all 1s ease'; |
| |
| setTimeout(() => { |
| el.style.opacity = '0'; |
| }, 1000); |
| }, index * 50); |
| }); |
| } |
| |
| |
| function incrementHours() { |
| const current = parseInt(inputHours.value) || 0; |
| if (current < 99) inputHours.value = current + 1; |
| } |
| |
| function decrementHours() { |
| const current = parseInt(inputHours.value) || 0; |
| if (current > 0) inputHours.value = current - 1; |
| } |
| |
| function incrementMinutes() { |
| const current = parseInt(inputMinutes.value) || 0; |
| if (current < 59) inputMinutes.value = current + 1; |
| else inputMinutes.value = 0; |
| } |
| |
| function decrementMinutes() { |
| const current = parseInt(inputMinutes.value) || 0; |
| if (current > 0) inputMinutes.value = current - 1; |
| else inputMinutes.value = 59; |
| } |
| |
| function incrementSeconds() { |
| const current = parseInt(inputSeconds.value) || 0; |
| if (current < 59) inputSeconds.value = current + 1; |
| else inputSeconds.value = 0; |
| } |
| |
| function decrementSeconds() { |
| const current = parseInt(inputSeconds.value) || 0; |
| if (current > 0) inputSeconds.value = current - 1; |
| else inputSeconds.value = 59; |
| } |
| |
| |
| function handleInputKeyPress(e) { |
| if (e.key === 'ArrowUp') { |
| e.preventDefault(); |
| if (e.target.id === 'inputHours') incrementHours(); |
| if (e.target.id === 'inputMinutes') incrementMinutes(); |
| if (e.target.id === 'inputSeconds') incrementSeconds(); |
| } |
| if (e.key === 'ArrowDown') { |
| e.preventDefault(); |
| if (e.target.id === 'inputHours') decrementHours(); |
| if (e.target.id === 'inputMinutes') decrementMinutes(); |
| if (e.target.id === 'inputSeconds') decrementSeconds(); |
| } |
| } |
| |
| |
| startBtn.addEventListener('click', startCountdown); |
| pauseBtn.addEventListener('click', pauseCountdown); |
| resetBtn.addEventListener('click', resetCountdown); |
| closeModal.addEventListener('click', closeModalFunc); |
| newTimerBtn.addEventListener('click', startNewTimer); |
| inputHours.addEventListener('keydown', handleInputKeyPress); |
| inputMinutes.addEventListener('keydown', handleInputKeyPress); |
| inputSeconds.addEventListener('keydown', handleInputKeyPress); |
| |
| |
| resetCountdown(); |
| </script> |
| </body> |
| </html> |