scoreboard hat reihenfolge

GUI
eric 2025-08-12 05:56:33 +02:00
parent 3b97d9a39a
commit 8c39cd3937
2 changed files with 105 additions and 11 deletions

View File

@ -147,12 +147,27 @@
.wedge.wrong { fill: #e22134 !important; } /* rot */ .wedge.wrong { fill: #e22134 !important; } /* rot */
.wedge.disabled { pointer-events:none; opacity:.85; } .wedge.disabled { pointer-events:none; opacity:.85; }
.wedge-label { fill:#fff; font-weight:700; font-size:35px; pointer-events:none; } .wedge-label { fill:#fff; font-weight:700; font-size:35px; pointer-events:none; }
/* === Winner Overlay === */
.win-overlay{position:fixed; inset:0; display:none; align-items:center; justify-content:center;
background: radial-gradient(1000px 600px at 50% -10%, rgba(29,185,84,.18), transparent 60%), rgba(0,0,0,.72);
z-index:999}
.win-modal{width:min(720px,92vw); background:#121212; border:1px solid var(--border);
border-radius:24px; padding:28px 26px; box-shadow:0 24px 80px rgba(0,0,0,.6); text-align:center}
.win-trophy{width:88px; height:88px; margin:0 auto 10px; color:var(--accent); filter:drop-shadow(0 0 16px rgba(29,185,84,.35))}
.win-title{font-size:clamp(22px,4.5vw,28px); color:var(--muted); margin:6px 0 4px}
.win-name{font-size:clamp(32px,6vw,48px); font-weight:900; letter-spacing:.2px; margin:0 0 10px}
.win-points{color:#cfcfcf; margin:0 0 16px}
.win-actions{display:flex; gap:10px; justify-content:center; flex-wrap:wrap; margin-top:10px}
.confetti{position:absolute; top:-10vh; width:10px; height:16px; opacity:.9; animation:fall 3.2s linear infinite}
@keyframes fall{0%{transform:translateY(-10vh) rotate(0deg)}100%{transform:translateY(110vh) rotate(360deg)}}
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<header> <header>
<div class="brand"> <div class="brand">
<!--TO DO eigenes Logo-->
<div class="logo" aria-hidden="true"> <div class="logo" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 2 2 7l10 5 10-5-10-5Zm10 7-10 5v9l10-5V9ZM2 9v9l10 5v-9L2 9Z"/> <path d="M12 2 2 7l10 5 10-5-10-5Zm10 7-10 5v9l10-5V9ZM2 9v9l10 5v-9L2 9Z"/>
@ -218,6 +233,25 @@
<!-- Toast --> <!-- Toast -->
<div id="toast" class="toast" role="status" aria-live="polite"></div> <div id="toast" class="toast" role="status" aria-live="polite"></div>
<!-- Winner Overlay -->
<div id="winnerOverlay" class="win-overlay" role="dialog" aria-modal="true" aria-labelledby="winName">
<div class="win-modal">
<div class="win-trophy" aria-hidden="true">
<svg viewBox="0 0 24 24" width="88" height="88" fill="currentColor">
<path d="M19 3h-3V2a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v1H5a1 1 0 0 0-1 1v2a5 5 0 0 0 5 5h.1A5.98 5.98 0 0 0 11 14.9V17H8a1 1 0 1 0 0 2h8a1 1 0 1 0 0-2h-3v-2.1A5.98 5.98 0 0 0 15.9 11H16a5 5 0 0 0 5-5V4a1 1 0 0 0-1-1ZM6 6V5h2v2a3 3 0 0 1-3-1Zm14 0a3 3 0 0 1-3 1V5h3v1Z"/>
</svg>
</div>
<p class="win-title">Gewonnen!</p>
<h2 id="winName" class="win-name"></h2>
<p id="winPoints" class="win-points"></p>
<div class="win-actions">
<button id="winNext" class="btn btn-primary">Neue Runde</button>
<button id="winClose" class="btn btn-ghost">Schließen</button>
<button id="winShare" class="btn btn-ghost">Teilen</button>
</div>
</div>
</div>
<!-- Module --> <!-- Module -->
<script type="module" src="/js/utils.js"></script> <script type="module" src="/js/utils.js"></script>
<script type="module" src="/js/start-round.js"></script> <script type="module" src="/js/start-round.js"></script>

View File

@ -242,15 +242,25 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
// 7) Ergebnis + Weiter-Button // 7) Ergebnis + Weiter-Button
function renderScoreboard(scores) { function renderScoreboard(scores) {
scoreboard.innerHTML = ""; scoreboard.innerHTML = "";
Object.entries(scores).forEach(([user, pts]) => { if (!scores) return;
const entries = Object.entries(scores)
.sort((a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0])); // Punkte absteigend
for (const [user, pts] of entries) {
const li = document.createElement("li"); const li = document.createElement("li");
li.textContent = `${user}: ${pts} Punkte`; li.textContent = `${user}: ${pts} Punkte`;
scoreboard.append(li); scoreboard.append(li);
});
} }
}
let lastScores = null;
function handleRoundResult({ scores, guesses, owner }) { function handleRoundResult({ scores, guesses, owner }) {
renderScoreboard(scores); renderScoreboard(scores);
lastScores = scores;
try { try {
const wedges = document.querySelectorAll("#options .wedge"); const wedges = document.querySelectorAll("#options .wedge");
@ -301,16 +311,66 @@ function handleRoundResult({ scores, guesses, owner }) {
// 8) Spielende -> Start-Button zurück // 8) Spielende -> Start-Button zurück
function handleGameEnd({ winner }) { function handleGameEnd({ winner }) {
const overlay = document.getElementById("winnerOverlay");
const nameEl = document.getElementById("winName");
const ptsEl = document.getElementById("winPoints");
const btnNext = document.getElementById("winNext");
const btnClose= document.getElementById("winClose");
const btnShare= document.getElementById("winShare");
nameEl.textContent = winner;
const pts = lastScores && typeof lastScores[winner] !== "undefined" ? lastScores[winner] : null;
ptsEl.textContent = pts != null ? `${winner} endet mit ${pts} Punkten.` : "";
// Konfetti erzeugen (einmalig pro Anzeige)
Array.from(overlay.querySelectorAll(".confetti")).forEach(n => n.remove());
const colors = [ "var(--accent)", "#21d07a", "#b3b3b3", "#ffffff" ];
for (let i=0;i<24;i++){
const c = document.createElement("div");
c.className = "confetti";
c.style.left = Math.random()*100 + "vw";
c.style.background = colors[i % colors.length];
c.style.animationDelay = (Math.random()*1.2) + "s";
c.style.transform = `translateY(-10vh) rotate(${Math.random()*360}deg)`;
overlay.appendChild(c);
}
// Buttons
btnNext.onclick = () => {
socket.send(JSON.stringify({ type: "next-round" }));
overlay.style.display = "none";
roundArea.hidden = true;
resultP.textContent = "";
};
btnClose.onclick = () => { overlay.style.display = "none"; };
btnShare.onclick = async () => {
const text = `🏆 ${winner} hat Spotify Roulette gewonnen!`;
try {
if (navigator.share) await navigator.share({ text });
else {
await navigator.clipboard.writeText(text);
showToast("Text kopiert teilen!");
}
} catch (_) {}
};
const nextBtn = document.getElementById("nextRound"); const nextBtn = document.getElementById("nextRound");
resultP.textContent = `🎉 ${winner} hat gewonnen!`; //resultP.textContent = `🎉 ${winner} hat gewonnen!`;
nextBtn.hidden = true; //nextBtn.hidden = true;
nextBtn.disabled = true; //nextBtn.disabled = true;
setTimeout(() => { if (nextBtn) { nextBtn.hidden = true; nextBtn.disabled = true; }
startBtn.hidden = false; startBtn.hidden = false;
startBtn.disabled = false; startBtn.disabled = false;
roundArea.hidden = true;
scoreboard.innerHTML = ""; // Overlay zeigen
}, 6000); overlay.style.display = "flex";
//setTimeout(() => {
// startBtn.hidden = false;
// startBtn.disabled = false;
// roundArea.hidden = true;
// scoreboard.innerHTML = "";
//}, 6000);
} }
// Spotify-Playback Funktion (unverändert) // Spotify-Playback Funktion (unverändert)