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>` : '';
|
||||
|
||||
// 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 ? `
|
||||
<div class="hype-meter">
|
||||
<span class="hype-label">Hype Meter</span>
|
||||
@@ -53,7 +60,7 @@ function renderLiveGame(game) {
|
||||
<div class="card-header">
|
||||
<div class="badges">
|
||||
${periodLabel}
|
||||
<span class="badge">${time}</span>
|
||||
<span class="badge" ${clockAttrs}>${time}</span>
|
||||
</div>
|
||||
${dot}
|
||||
</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 ──────────────────────────────────────────
|
||||
|
||||
function ordinalPeriod(period) {
|
||||
@@ -149,4 +181,7 @@ function autoRefresh() {
|
||||
setTimeout(autoRefresh, 5000);
|
||||
}
|
||||
|
||||
window.addEventListener('load', autoRefresh);
|
||||
window.addEventListener('load', () => {
|
||||
autoRefresh();
|
||||
setInterval(tickClocks, 1000);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user