Cartello d'apertura «Manzolo (Poca) Art Presents»; il primo fotogramma non congela più il gioco
Il cartello dura 5 secondi prima del titolo e si salta con un clic o un tasto; il test nel browser lo controlla. Il primo timestamp di requestAnimationFrame può precedere quello del caricamento: il dt negativo dava alla musica un volume sotto zero, il browser lanciava un errore e il ciclo dei fotogrammi si fermava (una volta su tre nella prova in orizzontale). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdeKd62opy9PKgpQXtVctF
This commit is contained in:
+6
-1
@@ -15,7 +15,12 @@
|
||||
<button id="mute" aria-label="Musica: togli l'audio" title="Musica (M)">♪</button>
|
||||
<input id="volume" type="range" min="0" max="100" step="5" aria-label="Volume della musica">
|
||||
</div>
|
||||
<section id="title" class="overlay">
|
||||
<section id="splash" class="overlay" aria-label="Manzolo (Poca) Art Presents">
|
||||
<p class="splash-name">Manzolo</p>
|
||||
<p class="splash-art"><span>(Poca)</span> Art</p>
|
||||
<p class="splash-present">Presents</p>
|
||||
</section>
|
||||
<section id="title" class="overlay" hidden>
|
||||
<h1>La Leggenda<br>del <span>Regolo</span></h1>
|
||||
<p class="subtitle">San Piero a Sieve · Palio della Fortezza · 2007</p>
|
||||
<div class="buttons"><button id="continue" hidden>Continua</button><button id="new-game">Nuova partita</button></div>
|
||||
|
||||
+19
-3
@@ -187,9 +187,11 @@ function updateUi() {
|
||||
|
||||
let last = performance.now();
|
||||
function frame(now) {
|
||||
const dt = Math.min(0.1, (now - last) / 1000);
|
||||
// Il primo timestamp di requestAnimationFrame può precedere `last`: un dt negativo darebbe alla musica un volume
|
||||
// sotto zero, il browser lancerebbe un errore e il ciclo dei fotogrammi si fermerebbe (gioco congelato).
|
||||
const dt = Math.max(0, Math.min(0.1, (now - last) / 1000));
|
||||
last = now;
|
||||
const screen = !$('title').hidden ? 'title' : !$('intro').hidden ? 'intro' : !$('ending').hidden ? 'ending' : 'game';
|
||||
const screen = !$('title').hidden || !$('splash').hidden ? 'title' : !$('intro').hidden ? 'intro' : !$('ending').hidden ? 'ending' : 'game';
|
||||
music.update(trackFor(story, { screen, scene: game?.state.scene }), dt);
|
||||
if (game) {
|
||||
if (running) game.update(dt);
|
||||
@@ -204,8 +206,22 @@ $('continue').addEventListener('click', () => start(readSave(storage)));
|
||||
$('restart').addEventListener('click', () => { if (storage) clearSave(storage); start(null); });
|
||||
|
||||
// Dietro il titolo si vede già la piazza.
|
||||
// --- Cartello d'apertura «Manzolo (Poca) Art Presents»: 5 secondi (un clic o un tasto lo salta), poi il titolo.
|
||||
function showSplash() {
|
||||
let done = false;
|
||||
const end = () => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
$('splash').hidden = true;
|
||||
showTitle();
|
||||
};
|
||||
setTimeout(end, 5000);
|
||||
$('splash').addEventListener('click', end);
|
||||
addEventListener('keydown', end, { once: true });
|
||||
}
|
||||
|
||||
game = new Game(story);
|
||||
command = new Command(game);
|
||||
showTitle();
|
||||
showSplash();
|
||||
requestAnimationFrame(frame);
|
||||
loadAssets(story).then(ok => { if (ok.includes(false)) console.warn('Alcune immagini non sono state caricate.'); });
|
||||
|
||||
@@ -68,6 +68,21 @@ body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-h
|
||||
#skip-intro { position: absolute; right: 10px; bottom: 10px; font: bold 14px var(--font); padding: .3em .9em; border-radius: 6px; border: 0;
|
||||
color: var(--bg); background: var(--dim); cursor: pointer; }
|
||||
@keyframes appear { from { opacity: 0; transform: translateY(6px); } }
|
||||
/* Cartello d'apertura «Manzolo (Poca) Art Presents»: le parole entrano una alla volta, il «(Poca)» cade dall'alto
|
||||
come un'aggiunta a mano; dopo 4,3 s sfuma (il JavaScript lo toglie a 5 s). */
|
||||
#splash { z-index: 3; gap: .15em; cursor: pointer; background: radial-gradient(circle at 50% 45%, #2a2030, #0d0910 70%);
|
||||
animation: splash-out .7s ease-in 4.3s forwards; }
|
||||
#splash p { margin: 0; max-width: none; font-weight: bold; text-shadow: 3px 3px 0 #000; opacity: 0; }
|
||||
#splash .splash-name { font-size: clamp(34px, 8vw, 88px); letter-spacing: .14em; text-transform: uppercase; color: var(--ink);
|
||||
animation: splash-in .9s ease-out .2s forwards; }
|
||||
#splash .splash-art { font-size: clamp(22px, 4.5vw, 50px); color: var(--accent); animation: splash-in .9s ease-out 1s forwards; }
|
||||
#splash .splash-art span { display: inline-block; font-size: .7em; font-style: italic; color: #7fe08a; opacity: 0;
|
||||
animation: splash-poca .45s cubic-bezier(.3, 1.6, .6, 1) 1.9s forwards; }
|
||||
#splash .splash-present { font-size: clamp(14px, 2.4vw, 26px); letter-spacing: .5em; text-transform: uppercase; color: var(--dim);
|
||||
animation: splash-in .9s ease-out 2.6s forwards; }
|
||||
@keyframes splash-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
|
||||
@keyframes splash-poca { from { opacity: 0; transform: translateY(-1.5em) rotate(-8deg) scale(1.8); } to { opacity: 1; transform: rotate(-8deg); } }
|
||||
@keyframes splash-out { to { opacity: 0; } }
|
||||
.overlay h1 { margin: 0; font-size: clamp(30px, 7vw, 76px); line-height: 1; letter-spacing: .02em; color: var(--ink); text-shadow: 3px 3px 0 #000; }
|
||||
.overlay h1 span { color: #7fe08a; }
|
||||
.overlay h2 { margin: 0; font-size: clamp(24px, 5vw, 52px); color: var(--accent); }
|
||||
|
||||
+9
-1
@@ -44,7 +44,13 @@ const hotspotCenter = (page, id) => page.evaluate(id => { const h = game.hotspot
|
||||
|
||||
try {
|
||||
const page = await open({ width: 1280, height: 860 });
|
||||
await page.waitForSelector('#title:not([hidden])');
|
||||
// Il cartello d'apertura, poi da solo il titolo.
|
||||
assert.match(await page.textContent('#splash'), /Manzolo\s*\(Poca\)\s*Art\s*Presents/);
|
||||
assert.ok(await page.isHidden('#title'), 'il titolo aspetta la fine del cartello');
|
||||
await page.waitForTimeout(2800);
|
||||
await page.screenshot({ path: `${OUT}/00-cartello.png` });
|
||||
await page.waitForSelector('#title:not([hidden])', { timeout: 8000 });
|
||||
assert.ok(await page.isHidden('#splash'), 'il cartello sparisce');
|
||||
await page.screenshot({ path: `${OUT}/01-titolo.png` });
|
||||
await page.click('#new-game');
|
||||
// L'introduzione: una riga per clic, poi comincia la partita.
|
||||
@@ -108,6 +114,8 @@ try {
|
||||
assert.equal(await page.evaluate(() => game.state.scene), 'trebbio');
|
||||
|
||||
const phone = await open({ width: 390, height: 844 });
|
||||
await phone.click('#splash'); // un clic salta il cartello
|
||||
assert.ok(await phone.isVisible('#title'), 'il clic sul cartello porta al titolo');
|
||||
await phone.click('#new-game');
|
||||
await phone.click('#skip-intro');
|
||||
await settle(phone);
|
||||
|
||||
Reference in New Issue
Block a user