dino-land: add autoplay-safe music toggle on correct CI/CD path
This commit is contained in:
parent
d57951466e
commit
800880ed30
@ -13,6 +13,7 @@
|
||||
</div>
|
||||
|
||||
<canvas id="game" width="960" height="540" aria-label="Dino Land game canvas"></canvas>
|
||||
<button id="musicToggle" type="button" aria-pressed="false" aria-label="Toggle music">🔇 Music: Off</button>
|
||||
|
||||
<div id="startScreen">
|
||||
<div class="panel">
|
||||
|
||||
60
js/game.js
60
js/game.js
@ -8,6 +8,36 @@ const leaderboardEl = document.getElementById('leaderboard');
|
||||
const saveScoreFormEl = document.getElementById('saveScoreForm');
|
||||
const playerNameEl = document.getElementById('playerName');
|
||||
const restartBtnEl = document.getElementById('restartBtn');
|
||||
const musicToggleEl = document.getElementById('musicToggle');
|
||||
|
||||
const MUSIC_STORAGE_KEY = 'dinoLand.musicEnabled';
|
||||
|
||||
function resolveInitialMusicEnabled() {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(MUSIC_STORAGE_KEY);
|
||||
if (raw === null) return false;
|
||||
return raw !== '0' && raw !== 'false';
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function persistMusicEnabled(enabled) {
|
||||
try {
|
||||
window.localStorage.setItem(MUSIC_STORAGE_KEY, enabled ? '1' : '0');
|
||||
} catch (_) {
|
||||
// no-op in restricted contexts
|
||||
}
|
||||
}
|
||||
|
||||
let musicEnabled = resolveInitialMusicEnabled();
|
||||
|
||||
function refreshMusicToggleUi() {
|
||||
if (!musicToggleEl) return;
|
||||
musicToggleEl.textContent = musicEnabled ? '🎵 Music: On' : '🔇 Music: Off';
|
||||
musicToggleEl.setAttribute('aria-pressed', musicEnabled ? 'true' : 'false');
|
||||
musicToggleEl.setAttribute('title', musicEnabled ? 'Disable music' : 'Enable music (first click also unlocks audio)');
|
||||
}
|
||||
|
||||
const startScreenEl = document.getElementById('startScreen');
|
||||
const startFormEl = document.getElementById('startForm');
|
||||
@ -64,10 +94,22 @@ let gameConfig = { accessMode: 'standard', difficulty: 'medium' };
|
||||
|
||||
function ensureAudioCtx() {
|
||||
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
if (audioCtx.state === 'suspended') audioCtx.resume();
|
||||
return audioCtx;
|
||||
}
|
||||
|
||||
async function resumeAudioContext() {
|
||||
const ctx = ensureAudioCtx();
|
||||
if (ctx.state !== 'suspended') return true;
|
||||
try {
|
||||
await ctx.resume();
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function playSfx(type) {
|
||||
if (!musicEnabled) return;
|
||||
try {
|
||||
ensureAudioCtx();
|
||||
const now = audioCtx.currentTime;
|
||||
@ -238,6 +280,22 @@ window.addEventListener('keydown', (e) => {
|
||||
});
|
||||
window.addEventListener('keyup', (e) => keys.delete(e.key));
|
||||
|
||||
if (musicToggleEl) {
|
||||
refreshMusicToggleUi();
|
||||
musicToggleEl.addEventListener('click', async (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (!musicEnabled) {
|
||||
await resumeAudioContext();
|
||||
}
|
||||
|
||||
musicEnabled = !musicEnabled;
|
||||
persistMusicEnabled(musicEnabled);
|
||||
refreshMusicToggleUi();
|
||||
});
|
||||
}
|
||||
|
||||
startFormEl.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const pass = passwordEl.value.trim();
|
||||
|
||||
17
styles.css
17
styles.css
@ -25,6 +25,23 @@ body {
|
||||
border: 2px solid #1b3a47;
|
||||
background: #b5ecff;
|
||||
}
|
||||
#musicToggle {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
z-index: 9;
|
||||
width: auto;
|
||||
min-width: 132px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(255,255,255,0.24);
|
||||
background: rgba(0,0,0,0.65);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#deathScreen,
|
||||
#startScreen {
|
||||
position: fixed;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user