Files
stream-overlay/overlay/scoreboard.html
T
2026-06-24 13:00:51 +02:00

255 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Scoreboard</title>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%;
height: 100%;
overflow: hidden;
font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
background: transparent;
color: #fff;
user-select: none;
}
/* Contenitore responsive centra il widget */
.widget {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/* Scoreboard si adatta al contenuto */
.scoreboard {
display: inline-block;
min-width: 380px;
max-width: 100%;
background: linear-gradient(160deg, rgba(0,0,0,0.88), rgba(10,10,20,0.88));
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.1);
border-radius: calc(min(16px, 1.2vw));
overflow: hidden;
box-shadow: 0 8px 40px rgba(0,0,0,0.6);
}
/* Header (Sets) */
.sb-header {
display: grid;
grid-template-columns: minmax(0, 1fr) 48px 48px 48px 60px;
background: rgba(255,255,255,0.06);
border-bottom: 1px solid rgba(255,255,255,0.06);
padding: 0.5em 1.2em;
font-size: clamp(10px, 1.2vw, 14px);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
color: rgba(255,255,255,0.5);
}
.sb-header .col-player { text-align: left; }
.sb-header .col-set,
.sb-header .col-points { text-align: center; }
/* Player Row */
.player-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 48px 48px 48px 60px;
align-items: center;
padding: 0.6em 1.2em;
border-bottom: 1px solid rgba(255,255,255,0.04);
transition: background 0.3s;
position: relative;
font-size: clamp(14px, 1.8vw, 22px);
}
.player-row:last-child { border-bottom: none; }
.player-row.serving {
background: rgba(250, 204, 21, 0.06);
}
.player-row.serving::before {
content: '●';
position: absolute;
left: 0.3em;
top: 50%;
transform: translateY(-50%);
font-size: 0.5em;
color: #facc15;
text-shadow: 0 0 8px rgba(250,204,21,0.6);
}
.player-row .player-name {
font-weight: 700;
letter-spacing: 0.5px;
display: flex;
align-items: center;
gap: 0.5em;
}
.player-row .player-name .flag {
display: inline-block;
width: 1.6em;
height: 1.1em;
border-radius: 2px;
background-size: cover;
background-position: center;
flex-shrink: 0;
}
/* Bandiere inline SVG base64 */
.flag-it { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="1" height="2" fill="%23009246"/><rect x="1" width="1" height="2" fill="%23fff"/><rect x="2" width="1" height="2" fill="%23ce2b37"/></svg>'); }
.flag-es { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23c60b1e"/><rect y="0.5" width="3" height="1" fill="%23ffc400"/></svg>'); }
.flag-us { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23fff"/><rect width="3" height="1" fill="%23b22234"/><rect y="1" width="3" height="1" fill="%233b3b6d"/></svg>'); }
.flag-fr { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="1" height="2" fill="%23005240"/><rect x="1" width="1" height="2" fill="%23fff"/><rect x="2" width="1" height="2" fill="%23ce2b37"/></svg>'); }
.flag-gb { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23012369"/><rect width="3" height="1" fill="%23c8102e"/><rect y="0.33" width="3" height="0.33" fill="%23fff"/><rect y="1.33" width="3" height="0.33" fill="%23fff"/></svg>'); }
.flag-au { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%2300008b"/><rect width="1.5" height="1" fill="%23fff"/><path d="M0 0L1.5 1M0 1L1.5 0" stroke="%23c8102e" stroke-width="0.15"/></svg>'); }
.flag-de { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="0.67" fill="%23000"/><rect y="0.67" width="3" height="0.67" fill="%23dd0000"/><rect y="1.33" width="3" height="0.67" fill="%23ffce00"/></svg>'); }
.flag-ch { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23d52b1e"/><rect x="1.2" y="0.4" width="0.6" height="1.2" fill="%23fff"/><rect x="0.8" y="0.8" width="1.4" height="0.4" fill="%23fff"/></svg>'); }
.flag-rs { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23fff"/><rect y="0.67" width="3" height="0.67" fill="%23005240"/><rect y="1.33" width="3" height="0.67" fill="%23c8102e"/></svg>'); }
.flag-gr { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23005b94"/><rect x="0.5" y="0.5" width="1" height="1" fill="%23fff"/><rect y="0.5" width="3" height="0.2" fill="%23fff"/></svg>'); }
.flag-br { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23009246"/><circle cx="1.5" cy="1" r="0.6" fill="%23ffcc00"/><path d="M1.5 0.6l0.3 0.5h-0.6z" fill="%23005240"/></svg>'); }
.flag-ar { background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><rect width="3" height="2" fill="%23fff"/><rect y="0.33" width="3" height="0.67" fill="%2375aadb"/><rect y="1" width="3" height="0.67" fill="%2375aadb"/></svg>'); }
.player-row .set-score {
text-align: center;
font-weight: 700;
}
.player-row .set-score.current-set {
color: #facc15;
}
.player-row .points {
text-align: center;
font-weight: 800;
letter-spacing: 1px;
font-size: 1.2em;
}
.player-row .points.deuce {
font-size: 0.7em;
font-weight: 600;
text-transform: uppercase;
color: #facc15;
}
.player-row .points.advantage {
color: #4ade80;
}
/* Animazioni */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.scoreboard { animation: fadeInUp 0.5s ease-out; }
@keyframes pointFlash {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.25); }
}
.points.flash {
animation: pointFlash 0.25s ease;
}
</style>
</head>
<body>
<div class="widget">
<div class="scoreboard" id="scoreboard">
<div class="sb-header">
<span class="col-player">Giocatore</span>
<span class="col-set">S1</span>
<span class="col-set">S2</span>
<span class="col-set">S3</span>
<span class="col-points">Punti</span>
</div>
<div class="player-row" id="player1Row">
<div class="player-name">
<span class="flag" id="flag1"></span>
<span id="player1Name">J. Sinner</span>
</div>
<span class="set-score" id="p1s1">0</span>
<span class="set-score" id="p1s2">0</span>
<span class="set-score" id="p1s3">0</span>
<span class="points" id="p1points">0</span>
</div>
<div class="player-row" id="player2Row">
<div class="player-name">
<span class="flag" id="flag2"></span>
<span id="player2Name">C. Alcaraz</span>
</div>
<span class="set-score" id="p2s1">0</span>
<span class="set-score" id="p2s2">0</span>
<span class="set-score" id="p2s3">0</span>
<span class="points" id="p2points">0</span>
</div>
</div>
</div>
<script src="common.js"></script>
<script>
let prevPoints = [0, 0];
let prevDeuce = false;
let prevAdv = 0;
function updateScoreboard(state) {
// Nomi e bandiere
document.getElementById('player1Name').textContent = state.player1;
document.getElementById('player2Name').textContent = state.player2;
document.getElementById('flag1').className = 'flag flag-' + (state.flag1 || 'it');
document.getElementById('flag2').className = 'flag flag-' + (state.flag2 || 'es');
// Set
for (let s = 0; s < 3; s++) {
const e1 = document.getElementById('p1s' + (s + 1));
const e2 = document.getElementById('p2s' + (s + 1));
if (state.sets[s]) {
e1.textContent = state.sets[s][0] ?? '0';
e2.textContent = state.sets[s][1] ?? '0';
} else {
e1.textContent = '0';
e2.textContent = '0';
}
e1.classList.toggle('current-set', s === state.currentSet);
e2.classList.toggle('current-set', s === state.currentSet);
}
// Punti
const labels = pointLabel(state);
const p1el = document.getElementById('p1points');
const p2el = document.getElementById('p2points');
p1el.textContent = labels[0];
p2el.textContent = labels[1];
// Classi punti
p1el.className = 'points';
p2el.className = 'points';
if (state.deuce) {
p1el.classList.add('deuce');
p2el.classList.add('deuce');
if (state.advantage === 1) p1el.classList.add('advantage');
if (state.advantage === 2) p2el.classList.add('advantage');
}
// Flash su cambio punti
const pNow = state.points;
if (pNow[0] !== prevPoints[0] || pNow[1] !== prevPoints[1] ||
state.deuce !== prevDeuce || state.advantage !== prevAdv) {
const changed = pNow[0] !== prevPoints[0] ? 1 : 2;
const el = document.getElementById('p' + changed + 'points');
el.classList.remove('flash');
void el.offsetWidth;
el.classList.add('flash');
}
prevPoints = [...pNow];
prevDeuce = state.deuce;
prevAdv = state.advantage;
// Servizio
document.getElementById('player1Row').classList.toggle('serving', state.server === 1);
document.getElementById('player2Row').classList.toggle('serving', state.server === 2);
}
// Ascolta aggiornamenti
const unsub = onStateUpdate(updateScoreboard);
</script>
</body>
</html>