piechart wip

pull/32/head
eric 2025-08-10 21:44:56 +02:00
parent 1eb8c8cbe6
commit c0ffb4d2f1
2 changed files with 75 additions and 44 deletions

View File

@ -75,13 +75,12 @@
#roundArea[hidden]{display:none} #roundArea[hidden]{display:none}
#options{ #options{
position:relative; width:100%; max-width:520px; aspect-ratio:1; margin:14px 0 6px; position:relative; width:100%; max-width:520px; aspect-ratio:1; margin:14px 0 6px;
border-radius:50%; border:1px dashed var(--border); border-radius:50%;
background: radial-gradient(240px 240px at 50% 50%, rgba(29,185,84,.08), transparent 60%);
} }
.player-option{ .player-option{
position:absolute; position:absolute;
left:50%; /* neu */ left:50%;
top:50%; /* neu */ top:50%;
transform-origin:center; transform-origin:center;
background:#0f0f0f; color:var(--text); background:#0f0f0f; color:var(--text);
border:1px solid var(--border); border:1px solid var(--border);
@ -124,7 +123,7 @@
padding:10px 14px; border-radius:999px; box-shadow:var(--shadow); z-index:50; display:none} padding:10px 14px; border-radius:999px; box-shadow:var(--shadow); z-index:50; display:none}
.toast.show{display:block} .toast.show{display:block}
/* Slide-out Drawer für Songliste (wie im Beispiel) */ /* Slide-out Drawer */
.drawer{position:fixed; top:0; right:0; height:100%; width:340px; background:#0f0f0f; .drawer{position:fixed; top:0; right:0; height:100%; width:340px; background:#0f0f0f;
border-left:1px solid var(--border); transform:translateX(100%); transition:transform .25s ease; border-left:1px solid var(--border); transform:translateX(100%); transition:transform .25s ease;
z-index:60; display:flex; flex-direction:column} z-index:60; display:flex; flex-direction:column}
@ -137,6 +136,15 @@
.drawer .meta{display:flex; flex-direction:column} .drawer .meta{display:flex; flex-direction:column}
.drawer .meta b{font-size:13px} .drawer .meta b{font-size:13px}
.drawer .meta span{font-size:12px; color:var(--muted)} .drawer .meta span{font-size:12px; color:var(--muted)}
/* === Pie-Slices === */
#options svg.options-svg { width:100%; height:auto; display:block; border-radius:50%; }
.wedge { fill:#181818; stroke:#2a2a2a; stroke-width:2; cursor:pointer; transition:filter .15s, fill .2s; }
.wedge:hover { filter:brightness(1.08); }
.wedge.selected { fill:#1db954; }
.wedge.correct { fill:#1db954; }
.wedge.disabled { pointer-events:none; opacity:.85; }
.wedge-label { fill:#fff; font-weight:700; font-size:14px; pointer-events:none; }
</style> </style>
</head> </head>
<body> <body>
@ -166,17 +174,6 @@
</div> </div>
</div> </div>
<div class="card-bd"> <div class="card-bd">
<!-- <div class="now" style="margin-bottom:10px">-->
<!-- <div class="cover" aria-hidden="true">-->
<!-- <svg width="48" height="48" viewBox="0 0 24 24" fill="#333"><path d="M12 3a9 9 0 1 0 .001 18.001A9 9 0 0 0 12 3Zm0 2a7 7 0 1 1-.001 14.001A7 7 0 0 1 12 5Zm0 3a4 4 0 1 0 .001 8.001A4 4 0 0 0 12 8Z"/></svg>-->
<!-- </div>-->
<!-- <div>-->
<!-- <p id="trackTitle" class="track-title">—</p>-->
<!-- <p id="trackArtists" class="artists">—</p>-->
<!-- </div>-->
<!-- </div>-->
<!-- IDs exakt wie dein game.js -->
<section id="roundArea" hidden> <section id="roundArea" hidden>
<div id="songEmbed" style="margin-bottom:12px"></div> <div id="songEmbed" style="margin-bottom:12px"></div>
<div id="options"></div> <div id="options"></div>
@ -208,7 +205,7 @@
</aside> </aside>
</div> </div>
<!-- Slide-out Drawer für Songliste --> <!-- Drawer -->
<aside class="drawer" id="songDrawer" aria-label="Zuletzt gehörte Songs"> <aside class="drawer" id="songDrawer" aria-label="Zuletzt gehörte Songs">
<header> <header>
<h3 style="margin:0">Zuletzt gehört</h3> <h3 style="margin:0">Zuletzt gehört</h3>
@ -217,10 +214,10 @@
<div class="list" id="songListArea"></div> <div class="list" id="songListArea"></div>
</aside> </aside>
<!-- Toast für Clipboard-Notice --> <!-- Toast -->
<div id="toast" class="toast" role="status" aria-live="polite"></div> <div id="toast" class="toast" role="status" aria-live="polite"></div>
<!-- Deine existierenden 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>
<script type="module" src="/js/device-select.js"></script> <!-- füllt #deviceSelectArea --> <script type="module" src="/js/device-select.js"></script> <!-- füllt #deviceSelectArea -->

View File

@ -117,6 +117,22 @@ const optionsDiv = document.getElementById("options");
const resultP = document.getElementById("result"); const resultP = document.getElementById("result");
const scoreboard = document.getElementById("scoreboard"); const scoreboard = document.getElementById("scoreboard");
// --- SVG helpers für Pie-Slices ---
const SVGNS = "http://www.w3.org/2000/svg";
const VB = 1000; // viewBox 0..1000
const CX = 500, CY = 500, R = 480;
function polar(cx, cy, r, deg){
const rad = (deg - 90) * Math.PI/180; // 0° oben
return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
}
function wedgePath(cx, cy, r, a0, a1){
const p0 = polar(cx, cy, r, a0);
const p1 = polar(cx, cy, r, a1);
const largeArc = ((a1 - a0 + 360) % 360) > 180 ? 1 : 0;
return `M ${cx} ${cy} L ${p0.x} ${p0.y} A ${r} ${r} 0 ${largeArc} 1 ${p1.x} ${p1.y} Z`;
}
// 6) Neue Runde anzeigen // 6) Neue Runde anzeigen
async function handleRoundStart({ ownerOptions, songUri, trackInfos }) { async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
// UI zurücksetzen // UI zurücksetzen
@ -138,31 +154,45 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
await window.playOnSpotify(songUri, username); await window.playOnSpotify(songUri, username);
} }
// Buttons kreisförmig verteilen // Optionen als Tortenstücke (SVG) rendern
requestAnimationFrame(() => { const svg = document.createElementNS(SVGNS, "svg");
const optsRect = optionsDiv.getBoundingClientRect(); svg.setAttribute("viewBox", `0 0 ${VB} ${VB}`);
const radius = Math.min(optsRect.width, optsRect.height) / 2 - 40; // Abstand zum Rand svg.setAttribute("class", "options-svg");
optionsDiv.innerHTML = "";
optionsDiv.appendChild(svg);
const n = ownerOptions.length;
ownerOptions.forEach((user, i) => { ownerOptions.forEach((user, i) => {
const btn = document.createElement("button"); const a0 = (360 / n) * i;
btn.textContent = user; const a1 = (360 / n) * (i + 1);
btn.classList.add("player-option");
//const angle = 360 * i / ownerOptions.length;
//btn.style.transform = `rotate(${angle}deg) translateY(-${radius}px) rotate(${-angle}deg)`;
const angle = (360 / ownerOptions.length) * i; const path = document.createElementNS(SVGNS, "path");
// zentrieren -> dann drehen -> nach außen schieben -> Text aufrecht drehen path.setAttribute("d", wedgePath(CX, CY, R, a0, a1));
btn.style.transform = `translate(-50%, -50%) rotate(${angle}deg) translate(0, -${radius}px) rotate(${-angle}deg)`; path.setAttribute("class", "wedge");
path.setAttribute("data-user", user);
btn.addEventListener("click", () => { path.addEventListener("click", () => {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: "guess", type: "guess",
username: username, username: username,
guess: user guess: user
})); }));
optionsDiv.querySelectorAll("button").forEach(b => b.disabled = true); svg.querySelectorAll(".wedge").forEach(w => w.classList.add("disabled"));
}); path.classList.add("selected");
optionsDiv.appendChild(btn);
}); });
// Label mittig im Segment
const mid = (a0 + a1) / 2;
const P = polar(CX, CY, R * 0.58, mid);
const text = document.createElementNS(SVGNS, "text");
text.setAttribute("x", P.x);
text.setAttribute("y", P.y);
text.setAttribute("class", "wedge-label");
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.textContent = user;
svg.appendChild(path);
svg.appendChild(text);
}); });
// Start-Button ausblenden + Rundensektion einblenden // Start-Button ausblenden + Rundensektion einblenden
@ -210,6 +240,12 @@ function renderScoreboard(scores) {
function handleRoundResult({ scores, guesses, owner }) { function handleRoundResult({ scores, guesses, owner }) {
renderScoreboard(scores); renderScoreboard(scores);
// korrektes Segment hervorheben
try {
document.querySelectorAll("#options .wedge").forEach(w => {
if (w.getAttribute("data-user") === owner) w.classList.add("correct");
});
} catch(e) {}
resultP.innerHTML = ""; resultP.innerHTML = "";
Object.entries(guesses).forEach(([user, guess]) => { Object.entries(guesses).forEach(([user, guess]) => {
@ -268,5 +304,3 @@ async function playOnSpotify(trackUri, username) {
} }
} }
window.playOnSpotify = playOnSpotify; window.playOnSpotify = playOnSpotify;