Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 54 additions & 30 deletions projects/animated-clock/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,61 @@
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Animated Clock</title>
<link rel="stylesheet" href="./styles.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Animated Clock</title>
<link rel="stylesheet" href="./styles.css">
</head>

<body>
<main>
<h1>Animated Clock</h1>
<div class="controls">
<label for="timezone-select">Time Zone:</label>
<select id="timezone-select" aria-label="Select time zone">
<option value="local">Local Time</option>
<option value="UTC">UTC</option>
<option value="America/New_York">New York</option>
<option value="America/Los_Angeles">Los Angeles</option>
<option value="America/Chicago">Chicago</option>
<option value="Europe/London">London</option>
<option value="Europe/Paris">Paris</option>
<option value="Europe/Berlin">Berlin</option>
<option value="Asia/Tokyo">Tokyo</option>
<option value="Asia/Shanghai">Shanghai</option>
<option value="Asia/Kolkata">Mumbai</option>
<option value="Australia/Sydney">Sydney</option>
<option value="Pacific/Auckland">Auckland</option>
</select>
</div>
<canvas id="clock" width="240" height="240"></canvas>
<p class="notes">Smooth animation with requestAnimationFrame. Select a time zone to view different regional times.</p>
</main>
<script type="module" src="./main.js"></script>
</body>
<script>
const theme = localStorage.getItem('theme') || 'light';
if(theme === 'dark') {
document.documentElement.classList.add('dark-mode');
}
</script>

<button id="themeToggle" class="theme-toggle" aria-label="Toggle theme">
<svg class="sun-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"/>
<line x1="12" y1="1" x2="12" y2="3"/>
<line x1="12" y1="21" x2="12" y2="23"/>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
<line x1="1" y1="12" x2="3" y2="12"/>
<line x1="21" y1="12" x2="23" y2="12"/>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
</svg>
<svg class="moon-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>

</html>
<main>
<h1>Animated Clock</h1>
<div class="controls">
<label for="timezone-select">Time Zone:</label>
<select id="timezone-select" aria-label="Select time zone">
<option value="local">Local Time</option>
<option value="UTC">UTC</option>
<option value="America/New_York">New York</option>
<option value="America/Los_Angeles">Los Angeles</option>
<option value="America/Chicago">Chicago</option>
<option value="Europe/London">London</option>
<option value="Europe/Paris">Paris</option>
<option value="Europe/Berlin">Berlin</option>
<option value="Asia/Tokyo">Tokyo</option>
<option value="Asia/Shanghai">Shanghai</option>
<option value="Asia/Kolkata">Mumbai</option>
<option value="Australia/Sydney">Sydney</option>
<option value="Pacific/Auckland">Auckland</option>
</select>
</div>
<canvas id="clock" width="240" height="240"></canvas>
<p class="notes">Smooth animation with requestAnimationFrame. Select a time zone to view different regional times.</p>
</main>

<script src="./main.js" type="module"></script>
</body>
</html>
104 changes: 61 additions & 43 deletions projects/animated-clock/main.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,81 @@
// ------------------------------
// CLOCK FUNCTION
// ------------------------------
const c = document.getElementById('clock');
const ctx = c.getContext('2d');
const timezoneSelect = document.getElementById('timezone-select');

let selectedTimezone = 'local';

timezoneSelect.addEventListener('change', (e) => {
selectedTimezone = e.target.value;
selectedTimezone = e.target.value;
});

function getCurrentTime() {
const now = new Date();
if (selectedTimezone === 'local') {
return now;
}

try {
const timeInTimezone = new Date(now.toLocaleString("en-US", {timeZone: selectedTimezone}));
const localTime = new Date(now.toLocaleString("en-US"));
const diff = localTime.getTime() - timeInTimezone.getTime();
return new Date(now.getTime() - diff);
} catch (error) {
return now;
}
const now = new Date();
if (selectedTimezone === 'local') return now;

try {
const timeInTimezone = new Date(now.toLocaleString("en-US", { timeZone: selectedTimezone }));
const localTime = new Date(now.toLocaleString("en-US"));
const diff = localTime.getTime() - timeInTimezone.getTime();
return new Date(now.getTime() - diff);
} catch (error) {
return now;
}
}

function draw() {
const now = getCurrentTime();
const w = c.width, h = c.height, r = w / 2;

ctx.clearRect(0, 0, w, h);
ctx.translate(r, r);
ctx.strokeStyle = '#93c5fd';
const now = getCurrentTime();
const w = c.width, h = c.height, r = w / 2;

ctx.clearRect(0, 0, w, h);
ctx.translate(r, r);
ctx.strokeStyle = '#93c5fd';
ctx.beginPath();
ctx.arc(0, 0, r - 6, 0, Math.PI * 2);
ctx.stroke();

function hand(angle, len, width) {
ctx.save();
ctx.rotate(angle);
ctx.beginPath();
ctx.arc(0, 0, r - 6, 0, Math.PI * 2);
ctx.lineWidth = width;
ctx.moveTo(0, 0);
ctx.lineTo(0, -len);
ctx.stroke();

function hand(angle, len, width) {
ctx.save();
ctx.rotate(angle);
ctx.beginPath();
ctx.lineWidth = width;
ctx.moveTo(0, 0);
ctx.lineTo(0, -len);
ctx.stroke();
ctx.restore();
}

const sec = now.getSeconds() + now.getMilliseconds() / 1000;
const min = now.getMinutes() + sec / 60;
const hr = ((now.getHours() % 12) + min / 60);

hand(hr * Math.PI / 6, r * 0.5, 4);
hand(min * Math.PI / 30, r * 0.75, 3);
hand(sec * Math.PI / 30, r * 0.85, 1);

ctx.setTransform(1, 0, 0, 1, 0, 0);
requestAnimationFrame(draw);
ctx.restore();
}

const sec = now.getSeconds() + now.getMilliseconds() / 1000;
const min = now.getMinutes() + sec / 60;
const hr = ((now.getHours() % 12) + min / 60);

hand(hr * Math.PI / 6, r * 0.5, 4);
hand(min * Math.PI / 30, r * 0.75, 3);
hand(sec * Math.PI / 30, r * 0.85, 1);

ctx.setTransform(1, 0, 0, 1, 0, 0);
requestAnimationFrame(draw);
}

ctx.strokeStyle = '#6ee7b7';
ctx.lineCap = 'round';
draw();

const themeToggle = document.getElementById('themeToggle');
const body = document.body;


const currentTheme = localStorage.getItem('theme') || 'light';
if (currentTheme === 'dark') {
body.classList.add('dark-mode');
}

themeToggle.addEventListener('click', () => {
body.classList.toggle('dark-mode');


const theme = body.classList.contains('dark-mode') ? 'dark' : 'light';
localStorage.setItem('theme', theme);
});
Loading