feat: smooth clock countdown for intermission and live play
Store seconds + received-at timestamp on time badge. A 1s interval decrements locally so the clock never stutters between API polls. Drift-corrected: always computed from the anchored API value, not accumulated ticks. Re-render on each API response reanchors to the real value. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,13 @@ function renderLiveGame(game) {
|
|||||||
|
|
||||||
const dot = running ? `<span class="live-dot"></span>` : '';
|
const dot = running ? `<span class="live-dot"></span>` : '';
|
||||||
|
|
||||||
|
// Tick the clock locally when the clock is running or during intermission
|
||||||
|
const shouldTick = running || intermission;
|
||||||
|
const rawSeconds = timeToSeconds(time);
|
||||||
|
const clockAttrs = shouldTick
|
||||||
|
? `data-seconds="${rawSeconds}" data-received-at="${Date.now()}"`
|
||||||
|
: '';
|
||||||
|
|
||||||
const hype = !intermission ? `
|
const hype = !intermission ? `
|
||||||
<div class="hype-meter">
|
<div class="hype-meter">
|
||||||
<span class="hype-label">Hype Meter</span>
|
<span class="hype-label">Hype Meter</span>
|
||||||
@@ -53,7 +60,7 @@ function renderLiveGame(game) {
|
|||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="badges">
|
<div class="badges">
|
||||||
${periodLabel}
|
${periodLabel}
|
||||||
<span class="badge">${time}</span>
|
<span class="badge" ${clockAttrs}>${time}</span>
|
||||||
</div>
|
</div>
|
||||||
${dot}
|
${dot}
|
||||||
</div>
|
</div>
|
||||||
@@ -132,6 +139,31 @@ function updateGauges() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Clock ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
function timeToSeconds(str) {
|
||||||
|
if (!str || str === 'END') return 0;
|
||||||
|
const [m, s] = str.split(':').map(Number);
|
||||||
|
return m * 60 + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function secondsToTime(s) {
|
||||||
|
if (s <= 0) return 'END';
|
||||||
|
const m = Math.floor(s / 60);
|
||||||
|
const sec = s % 60;
|
||||||
|
return `${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tickClocks() {
|
||||||
|
const now = Date.now();
|
||||||
|
document.querySelectorAll('[data-seconds][data-received-at]').forEach(el => {
|
||||||
|
const seconds = parseInt(el.dataset.seconds, 10);
|
||||||
|
const receivedAt = parseInt(el.dataset.receivedAt, 10);
|
||||||
|
const elapsed = Math.floor((now - receivedAt) / 1000);
|
||||||
|
el.textContent = secondsToTime(Math.max(0, seconds - elapsed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ── Helpers ──────────────────────────────────────────
|
// ── Helpers ──────────────────────────────────────────
|
||||||
|
|
||||||
function ordinalPeriod(period) {
|
function ordinalPeriod(period) {
|
||||||
@@ -149,4 +181,7 @@ function autoRefresh() {
|
|||||||
setTimeout(autoRefresh, 5000);
|
setTimeout(autoRefresh, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', autoRefresh);
|
window.addEventListener('load', () => {
|
||||||
|
autoRefresh();
|
||||||
|
setInterval(tickClocks, 1000);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user