initial commit
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user