255 lines
10 KiB
HTML
255 lines
10 KiB
HTML
<!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>
|