static-countdown / index.html
philipp-zettl's picture
Add 3 files
82a58cc verified
raw
history blame
18.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Countdown Timer</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.glass-card {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1);
}
.countdown-digit {
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}
.countdown-digit:hover {
transform: translateY(-3px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
}
.firework {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
box-shadow: 0 0 10px 5px;
animation: firework-animation 1s ease-out;
opacity: 0;
}
@keyframes firework-animation {
0% { transform: translate(0, 0); opacity: 1; }
100% { transform: translate(var(--tx), var(--ty)); opacity: 0; }
}
.confetti {
position: absolute;
width: 10px;
height: 10px;
opacity: 0;
animation: confetti-fall 3s ease-in forwards;
}
@keyframes confetti-fall {
0% { transform: translateY(-100vh) rotate(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotate(360deg); opacity: 1; }
}
.pulse {
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.flip-in {
animation: flipIn 0.5s ease-out;
}
@keyframes flipIn {
0% { transform: rotateX(90deg); opacity: 0; }
100% { transform: rotateX(0deg); opacity: 1; }
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(102, 126, 234, 0.25);
}
.input-field {
transition: all 0.3s ease;
border: 1px solid #e2e8f0;
}
.input-field:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
</style>
</head>
<body class="min-h-screen text-gray-800 font-sans">
<div id="app" class="container mx-auto px-4 py-12">
<!-- Main Page -->
<div id="main-page" class="text-center max-w-3xl mx-auto">
<div class="glass-card p-8 mb-8">
<h1 class="text-4xl font-bold mb-4 text-gray-800">Countdown Timer</h1>
<p class="text-lg text-gray-600 mb-8">Create and share beautiful countdowns with custom durations and animations</p>
<form id="countdown-form" class="space-y-6">
<div class="text-left">
<label for="title" class="block mb-2 text-gray-700">Countdown Title</label>
<input type="text" id="title" placeholder="New Year's Eve"
class="input-field w-full px-4 py-3 rounded-lg bg-white">
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="text-left">
<label for="days" class="block mb-2 text-gray-700">Days</label>
<input type="number" id="days" min="0" value="0"
class="input-field w-full px-4 py-3 rounded-lg bg-white">
</div>
<div class="text-left">
<label for="hours" class="block mb-2 text-gray-700">Hours</label>
<input type="number" id="hours" min="0" max="23" value="0"
class="input-field w-full px-4 py-3 rounded-lg bg-white">
</div>
<div class="text-left">
<label for="minutes" class="block mb-2 text-gray-700">Minutes</label>
<input type="number" id="minutes" min="0" max="59" value="10"
class="input-field w-full px-4 py-3 rounded-lg bg-white">
</div>
<div class="text-left">
<label for="seconds" class="block mb-2 text-gray-700">Seconds</label>
<input type="number" id="seconds" min="0" max="59" value="0"
class="input-field w-full px-4 py-3 rounded-lg bg-white">
</div>
</div>
<div class="text-left">
<label class="block mb-2 text-gray-700">Finish Animation</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<label class="flex items-center space-x-2 cursor-pointer p-3 rounded-lg hover:bg-gray-100">
<input type="radio" name="animation" value="fireworks" checked class="form-radio h-5 w-5 text-indigo-600">
<span class="text-gray-700">Fireworks</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer p-3 rounded-lg hover:bg-gray-100">
<input type="radio" name="animation" value="confetti" class="form-radio h-5 w-5 text-indigo-600">
<span class="text-gray-700">Confetti</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer p-3 rounded-lg hover:bg-gray-100">
<input type="radio" name="animation" value="pulse" class="form-radio h-5 w-5 text-indigo-600">
<span class="text-gray-700">Pulse</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer p-3 rounded-lg hover:bg-gray-100">
<input type="radio" name="animation" value="none" class="form-radio h-5 w-5 text-indigo-600">
<span class="text-gray-700">None</span>
</label>
</div>
</div>
<button type="submit" class="btn-primary mt-6 px-8 py-3 rounded-full font-semibold">
Start Countdown <i class="fas fa-play ml-2"></i>
</button>
</form>
</div>
<div class="glass-card p-6">
<h2 class="text-2xl font-semibold mb-4 text-gray-800">How to Use</h2>
<div class="text-left space-y-2 text-gray-600">
<p>1. Set your countdown duration using days, hours, minutes and seconds</p>
<p>2. Add a title and choose a finish animation</p>
<p>3. Share the URL with others - all settings are included</p>
<p class="mt-4 font-medium">Example URL: <code class="bg-gray-100 px-2 py-1 rounded">?title=New+Year&days=30&hours=12&animation=fireworks</code></p>
</div>
</div>
</div>
<!-- Countdown Page -->
<div id="countdown-page" class="hidden text-center">
<div class="glass-card p-8 max-w-2xl mx-auto">
<h1 id="countdown-title" class="text-3xl font-bold mb-8 text-gray-800">Countdown</h1>
<div class="flex justify-center space-x-4 mb-8">
<div class="countdown-digit bg-white rounded-lg p-4 w-20 text-center">
<div id="days-display" class="text-3xl font-bold text-indigo-600">00</div>
<div class="text-sm text-gray-500">Days</div>
</div>
<div class="countdown-digit bg-white rounded-lg p-4 w-20 text-center">
<div id="hours-display" class="text-3xl font-bold text-indigo-600">00</div>
<div class="text-sm text-gray-500">Hours</div>
</div>
<div class="countdown-digit bg-white rounded-lg p-4 w-20 text-center">
<div id="minutes-display" class="text-3xl font-bold text-indigo-600">00</div>
<div class="text-sm text-gray-500">Minutes</div>
</div>
<div class="countdown-digit bg-white rounded-lg p-4 w-20 text-center">
<div id="seconds-display" class="text-3xl font-bold text-indigo-600">00</div>
<div class="text-sm text-gray-500">Seconds</div>
</div>
</div>
<div id="finished-message" class="hidden text-2xl font-bold mb-6">
<div class="inline-block px-6 py-3 bg-indigo-600 text-white rounded-full">
Time's Up!
</div>
</div>
<button id="new-countdown" class="btn-primary mt-4 px-6 py-2 rounded-full font-medium">
<i class="fas fa-redo mr-2"></i> New Countdown
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Check URL parameters
const urlParams = new URLSearchParams(window.location.search);
const hasParams = urlParams.has('days') || urlParams.has('hours') || urlParams.has('minutes') || urlParams.has('seconds');
if (hasParams) {
// Start countdown from URL params
const title = urlParams.get('title') || 'Countdown';
const days = parseInt(urlParams.get('days')) || 0;
const hours = parseInt(urlParams.get('hours')) || 0;
const minutes = parseInt(urlParams.get('minutes')) || 0;
const seconds = parseInt(urlParams.get('seconds')) || 0;
const animation = urlParams.get('animation') || 'fireworks';
startCountdown(title, days, hours, minutes, seconds, animation);
}
// Form submission
const form = document.getElementById('countdown-form');
form.addEventListener('submit', function(e) {
e.preventDefault();
const title = document.getElementById('title').value || 'Countdown';
const days = parseInt(document.getElementById('days').value) || 0;
const hours = parseInt(document.getElementById('hours').value) || 0;
const minutes = parseInt(document.getElementById('minutes').value) || 0;
const seconds = parseInt(document.getElementById('seconds').value) || 0;
const animation = document.querySelector('input[name="animation"]:checked').value;
// Update URL with parameters
const params = new URLSearchParams();
if (title !== 'Countdown') params.set('title', title);
if (days > 0) params.set('days', days);
if (hours > 0) params.set('hours', hours);
if (minutes > 0) params.set('minutes', minutes);
if (seconds > 0) params.set('seconds', seconds);
params.set('animation', animation);
window.history.pushState({}, '', `?${params.toString()}`);
startCountdown(title, days, hours, minutes, seconds, animation);
});
// New countdown button
document.getElementById('new-countdown').addEventListener('click', function() {
window.history.pushState({}, '', window.location.pathname);
document.getElementById('main-page').classList.remove('hidden');
document.getElementById('countdown-page').classList.add('hidden');
clearInterval(window.countdownInterval);
document.getElementById('finished-message').classList.add('hidden');
});
});
function startCountdown(title, days, hours, minutes, seconds, animationType) {
// Switch to countdown page
document.getElementById('main-page').classList.add('hidden');
document.getElementById('countdown-page').classList.remove('hidden');
document.getElementById('countdown-title').textContent = title;
document.getElementById('finished-message').classList.add('hidden');
// Calculate total seconds
let totalSeconds = days * 86400 + hours * 3600 + minutes * 60 + seconds;
// Start the countdown
updateCountdownDisplay(totalSeconds);
window.countdownInterval = setInterval(function() {
totalSeconds--;
updateCountdownDisplay(totalSeconds);
if (totalSeconds <= 0) {
clearInterval(window.countdownInterval);
countdownFinished(animationType);
}
}, 1000);
}
function updateCountdownDisplay(totalSeconds) {
const days = Math.floor(totalSeconds / 86400);
const hours = Math.floor((totalSeconds % 86400) / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
document.getElementById('days-display').textContent = days.toString().padStart(2, '0');
document.getElementById('hours-display').textContent = hours.toString().padStart(2, '0');
document.getElementById('minutes-display').textContent = minutes.toString().padStart(2, '0');
document.getElementById('seconds-display').textContent = seconds.toString().padStart(2, '0');
}
function countdownFinished(animationType) {
document.getElementById('finished-message').classList.remove('hidden');
switch(animationType) {
case 'fireworks':
createFireworks();
break;
case 'confetti':
createConfetti();
break;
case 'pulse':
document.getElementById('finished-message').classList.add('pulse');
break;
// 'none' case does nothing
}
}
function createFireworks() {
const colors = ['#ff0000', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#ff00ff'];
const container = document.getElementById('app');
for (let i = 0; i < 30; i++) {
setTimeout(() => {
const firework = document.createElement('div');
firework.className = 'firework';
firework.style.left = `${Math.random() * 100}%`;
firework.style.top = `${Math.random() * 100}%`;
firework.style.color = colors[Math.floor(Math.random() * colors.length)];
// Random direction for particles
const angle = Math.random() * Math.PI * 2;
const distance = 30 + Math.random() * 70;
const tx = Math.cos(angle) * distance;
const ty = Math.sin(angle) * distance;
firework.style.setProperty('--tx', `${tx}px`);
firework.style.setProperty('--ty', `${ty}px`);
container.appendChild(firework);
// Remove after animation
setTimeout(() => {
firework.remove();
}, 1000);
}, i * 200);
}
}
function createConfetti() {
const colors = ['#ff0000', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#ff00ff'];
const container = document.getElementById('app');
for (let i = 0; i < 80; i++) {
setTimeout(() => {
const confetti = document.createElement('div');
confetti.className = 'confetti';
confetti.style.left = `${Math.random() * 100}%`;
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
confetti.style.animationDelay = `${Math.random() * 2}s`;
// Random shape
if (Math.random() > 0.5) {
confetti.style.borderRadius = '50%';
} else {
confetti.style.transform = 'rotate(45deg)';
}
container.appendChild(confetti);
// Remove after animation
setTimeout(() => {
confetti.remove();
}, 3000);
}, i * 100);
}
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=philipp-zettl/static-countdown" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>