diff options
| author | Martin Hafskjold Thoresen <martin@vind.ai> | 2025-01-11 01:01:10 +0100 |
|---|---|---|
| committer | Martin Hafskjold Thoresen <martin@vind.ai> | 2025-01-11 01:01:10 +0100 |
| commit | 42fd8cd3560047c88a2167a4d5c5845dd81eb3ec (patch) | |
| tree | cdbd3817c28ae5264f8cf394684014f7bd4d509f /static | |
| parent | 2f2beb79720ec9fd35b993e7ad907ecf32005cde (diff) | |
| download | musicgame-42fd8cd3560047c88a2167a4d5c5845dd81eb3ec.tar.gz musicgame-42fd8cd3560047c88a2167a4d5c5845dd81eb3ec.zip | |
Add styling and hot reload
Diffstat (limited to 'static')
| -rw-r--r-- | static/main.js | 61 | ||||
| -rw-r--r-- | static/style.css | 160 |
2 files changed, 214 insertions, 7 deletions
diff --git a/static/main.js b/static/main.js index 144254f..183b117 100644 --- a/static/main.js +++ b/static/main.js @@ -6,10 +6,10 @@ function getContext() { return _context; } -function loadSample(url) { - return fetch(url) - .then((response) => response.arrayBuffer()) - .then((buffer) => getContext().decodeAudioData(buffer)); +async function loadSample(url) { + const res = await fetch(url); + const buffer = res.arrayBuffer(); + return getContext().decodeAudioData(buffer); } function playSoundSample(sample, sampleNote, noteToPlay) { @@ -20,3 +20,56 @@ function playSoundSample(sample, sampleNote, noteToPlay) { source.connect(ctx.destination); source.start(0); } + +function sigmoid(x) { + return 1 / (1 + Math.exp(-x)); +} + +function drawNoteLines(canvas) { + const ctx = canvas.getContext("2d"); + + let clicks = []; + + function render() { + const W = 256; + const H = 512; + ctx.clearRect(0, 0, W, H); + + const L = 1.0; + ctx.lineWidth = L; + ctx.strokeStyle = "#ccc"; + + for (let note = 0; note <= 12; note++) { + ctx.beginPath(); + const y = (note / 12) * (H - L) + L / 2; + ctx.moveTo(0, y); + ctx.lineTo(W, y); + ctx.stroke(); + } + + let now = Date.now(); + const R = 10.0; + for (const [x, y, t] of clicks) { + ctx.beginPath(); + ctx.arc(x - R, y - R, 10, 0, 2 * Math.PI); + const tt = (now - t) / 1000; // [0, 1] + const alpha = sigmoid(5 - 10 * tt); + ctx.fillStyle = `rgb(0, 99, 228, ${alpha})`; + ctx.fill(); + } + + clicks = clicks.filter((o) => now < o[2] + 1_000); + if (clicks.length > 0) { + requestAnimationFrame(render); + } + } + render(); + + canvas.addEventListener("mousedown", (e) => { + const { layerX, layerY } = e; + clicks.push([layerX, layerY, Date.now()]); + render(); + }); +} + +window.drawNoteLines = drawNoteLines; diff --git a/static/style.css b/static/style.css index 3b9ba9d..3dac7a4 100644 --- a/static/style.css +++ b/static/style.css @@ -1,18 +1,172 @@ +@import url("https://fonts.googleapis.com/css2?family=Chakra+Petch:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"); + :root { + font-family: "Chakra Petch", serif; +} + +* { + font-family: "Chakra Petch", serif; + padding: 0; + margin: 0; } body { + background: hsl(214 100% 44.7%); + color: white; + display: flex; + flex-direction: column; + align-items: center; +} + +h1 { + font-size: 48px; + text-align: center; +} + +button { + background: hsl(214 100% 100%); + color: hsl(214 100% 15%); + padding: 12px 24px; + border: none; + border-radius: 4px; + font-size: 16px; + + &:hover { + background: hsl(214 100% 95%); + cursor: pointer; + } +} + +form { + display: flex; + flex-direction: row; + gap: 10px; +} + +input:not([type]) { + padding: 4px 8px; + border: none; + border-radius: 4px; + font-size: 16px; + + &:focus { + outline: none; + } +} + +input[type="submit"] { + background: white; + padding: 4px 8px; + border: none; + border-radius: 4px; + font-size: 16px; + + &:hover { + background: #f0f0f0; + cursor: pointer; + } +} + +body > section { + padding: 48px 24px; + display: flex; + flex-direction: column; + gap: 10px; + + align-items: center; +} + +canvas { + background: white; + box-shadow: 0 0 10px 4px #00000080; + padding: 10px; + border-radius: 5px; +} + +footer { + border-top: 1px solid #00000020; + padding: 24px; + text-align: center; +} + +a { + color: #e1edfe; + &:visited { + color: #e1edfe; + } +} + +.main-page { + min-width: 30rem; + max-width: 50vw; +} + +.games-list { + list-style: none; + display: flex; + flex-direction: column; + gap: 8px; + + padding-bottom: 24px; + + li { + display: flex; + a { + flex: 1; + border-radius: 20px; + padding: 4px 16px; + background: #ffffff; + text-decoration: none; + &:hover { + background: #f0f8ff; + } + color: #333; + &:visited { + color: #333; + } + } + } +} + +.leaderboard { + font-size: 20px; + li { + margin-left: 20px; + } + padding-bottom: 36px; } #guesslist { list-style: none; + display: flex; + flex-direction: row; + gap: 10px; + min-width: 50vw; + flex-wrap: wrap; + + li { + border-radius: 4px; + padding: 4px 8px; + + background: white; + color: black; + } + [data-accept="none"] { - background: #aaa; + &::before { + content: "❓ "; + } } [data-accept="true"] { - background: #00ff00; + background: #d9ffd9; + &::before { + content: "✅ "; + } } [data-accept="false"] { - background: #ff0000; + background: #ffc0c0; + &::before { + content: "❌ "; + } } } |
