Campomigliaio collegato (strada, circolino, ponte), il Poggio verso la Fortezza; Ringraziamenti coi ritratti e il carosello

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApqNBvNbNCwXqR2RLo8QJh
This commit is contained in:
2026-10-24 19:43:28 +02:00
co-authored by Claude Opus 5.5
parent 3e99fb094c
commit b76e2ad1b8
55 changed files with 390 additions and 76 deletions
+52 -2
View File
@@ -106,11 +106,61 @@ function openCredits() {
$('credits-list').replaceChildren(...shuffledCredits(story.credits ?? []).flatMap(({ role, names }) => {
const dt = document.createElement('dt'), dd = document.createElement('dd');
dt.textContent = role;
dd.replaceChildren(...names.map(n => Object.assign(document.createElement('span'), { textContent: n })));
dd.replaceChildren(...names.map(n => {
if (typeof n === 'string') return Object.assign(document.createElement('span'), { textContent: n });
// Chi ha il ritratto: il nome è un pulsante che lo mostra.
const b = Object.assign(document.createElement('button'), { type: 'button', className: 'credit-face', textContent: n.name });
b.addEventListener('click', () => showFace(n));
return b;
}));
return [dt, dd];
}));
fillReel();
$('credits').hidden = false;
rollCredits();
}
// Sotto i nomi, un carosello che scorre da solo: gli sfondi del paese e i ritratti, due giri uguali in fila per non
// vedere mai la cucitura.
function fillReel() {
const track = $('credits-reel').querySelector('.track');
if (track.children.length) return;
const pictures = [...new Set(Object.values(story.scenes).map(s => s.background).filter(Boolean))];
const faces = (story.credits ?? []).flatMap(c => c.names).filter(n => n.face).map(n => n.face);
// I ritratti sparsi fra gli sfondi, uno ogni quattro.
const all = pictures.flatMap((p, i) => i % 4 === 3 && faces.length ? [p, faces[(i >> 2) % faces.length]] : [p]);
for (const src of [...all, ...all]) {
const img = Object.assign(document.createElement('img'), { src, alt: '', loading: 'lazy', decoding: 'async' });
if (src.includes('/credits/')) img.className = 'face';
track.append(img);
}
}
// I nomi salgono piano da soli; si fermano se ci si passa sopra o si tocca, e ricominciano dall'alto in fondo.
let rolling = 0, rollWait = 0;
$('credits-list').addEventListener('pointerdown', () => { rollWait = 4000; });
$('credits-list').addEventListener('wheel', () => { rollWait = 4000; }, { passive: true });
function rollCredits() {
const list = $('credits-list');
list.scrollTop = 0;
cancelAnimationFrame(rolling);
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
let last = performance.now(), pos = 0;
rollWait = 1500;
const step = now => {
if ($('credits').hidden) return;
const dt = now - last; last = now;
if (list.matches(':hover') || !$('face').hidden || (rollWait -= dt) > 0) pos = list.scrollTop;
else if (list.scrollTop + list.clientHeight >= list.scrollHeight - 1) { rollWait = 2500; list.scrollTop = pos = 0; }
else list.scrollTop = pos += dt * 0.025;
rolling = requestAnimationFrame(step);
};
rolling = requestAnimationFrame(step);
}
function showFace({ name, face }) {
$('face').querySelector('img').src = face;
$('face').querySelector('figcaption').textContent = name;
$('face').hidden = false;
}
$('face').addEventListener('click', () => { $('face').hidden = true; });
for (const b of document.querySelectorAll('.open-credits')) b.addEventListener('click', openCredits);
$('close-credits').addEventListener('click', () => { $('credits').hidden = true; });
@@ -414,7 +464,7 @@ window.addEventListener('keydown', e => {
return;
}
if (debugKey(e)) return;
if (!$('credits').hidden) { if (e.key === 'Escape') $('credits').hidden = true; return; }
if (!$('credits').hidden) { if (e.key === 'Escape') { if (!$('face').hidden) $('face').hidden = true; else $('credits').hidden = true; } return; }
if (!$('pause').hidden) { if (e.key === 'Escape') closePause(); return; }
if (!$('map').hidden) { if (e.key === 'Escape' || e.key === 'p' || e.key === 'P') $('map').hidden = true; return; }
if ((e.key === 'p' || e.key === 'P') && running) { openMap(); return; }