// Il telefonino del Barone (story.closeups, type 'telefono'; Andrea, 31 ottobre: «prenda il cellulare, nello stile // di come si sfoglia la storia infinita, e quando si clicca viene a tutto schermo il sito di San Piero»). // Come il libro: la scena si scurisce e le mani alzano il telefonino dal basso; nello schermo c'è il sito // (`site`, un'immagine 16:9 della home di sanpiero50037.it). Un tocco: lo schermo si allarga a tutta la scena. // Sul sito si tocca il link (`link`, in pixel dell'immagine del sito) e si apre il muro: una pagina vera // (assets/muro, la mette main.js sopra la scena), che qui sotto lascia solo il fondo. A muro aperto partono le // battute `comment`, e chiudendo `closing`. Unità: i pixel della scena 640×360. export const RISE = 0.45, ZOOM = 0.55; const W = 640, H = 360; const clamp01 = k => Math.max(0, Math.min(1, k)); const ease = k => (1 - Math.cos(Math.PI * clamp01(k))) / 2; export function newTelefono(id) { return { id, step: 'phone', t: 0, age: 0, aim: null }; } // Il tempo: lo zoom finisce sul sito; a muro aperto, dopo un attimo, le battute (una alla volta, come il libro). export function updateTelefono(c, dt) { c.t += dt; if (c.step === 'zoom' && c.t >= ZOOM) Object.assign(c, { step: 'site', t: 0 }); } // Dove sta il link del muro sulla scena, a sito aperto: il riquadro `link` riportato da immagine a scena. export function linkBox(def, site) { const [x, y, w, h] = def.link, kx = W / site[0], ky = H / site[1]; return [x * kx, y * ky, w * kx, h * ky]; } const inside = ([x, y, w, h], [px, py]) => px >= x && px <= x + w && py >= y && py <= y + h; // Un tocco: sul telefonino apre il sito; sul sito, solo sul link, apre il muro. Torna true se è successo qualcosa. export function tapTelefono(c, def) { if (c.step === 'phone') { if (c.age < RISE) return false; Object.assign(c, { step: 'zoom', t: 0 }); return true; } if (c.step === 'site' && c.aim && inside(linkBox(def, def.siteSize), c.aim)) { Object.assign(c, { step: 'wall', t: 0 }); return true; } return false; } // Il riquadro dello schermo sulla scena, col telefonino che sale dal basso. function phoneRect(def, img, age) { const rise = 1 - clamp01(age / RISE), k = def.phoneScale ?? 1; const w = img.width * k, h = img.height * k, x = Math.round((W - w) / 2), y = Math.round(H - h + 140 * rise * rise); return { x, y, w, h, k }; } export function drawTelefono(ctx, c, def, { phone, site }, time) { ctx.save(); ctx.fillStyle = `rgba(8, 5, 12, ${Math.min(0.78, c.age / 0.35 * 0.78)})`; ctx.fillRect(0, 0, W, H); if (!phone || !site) { ctx.restore(); return; } const p = phoneRect(def, phone, c.age), [sx, sy, sw, sh] = def.screen; const screen = [p.x + sx * p.k, p.y + sy * p.k, sw * p.k, sh * p.k]; const z = c.step === 'phone' ? 0 : c.step === 'zoom' ? ease(c.t / ZOOM) : 1; // Il telefonino, finché lo schermo non ha preso tutta la scena; poi sfuma. if (z < 1) { ctx.globalAlpha = 1 - z; ctx.imageSmoothingEnabled = false; ctx.drawImage(phone, p.x, p.y, p.w, p.h); ctx.globalAlpha = 1; } // Il sito: dentro lo schermo, poi sempre più grande fino a tutta la scena. Lo schermo ha le sue proporzioni: // il sito ci sta intero, centrato, sul fondo del sito. const r = screen.map((v, i) => v + ([0, 0, W, H][i] - v) * z); const fit = Math.min(r[2] / site.width, r[3] / site.height), dw = site.width * fit, dh = site.height * fit; ctx.fillStyle = def.paper ?? '#f7e0c0'; ctx.fillRect(r[0], r[1], r[2], r[3]); ctx.imageSmoothingEnabled = true; ctx.drawImage(site, r[0] + (r[2] - dw) / 2, r[1] + (r[3] - dh) / 2, dw, dh); // Sotto il muro (la pagina vera) resta il fondo del sito. if (c.step === 'wall') { ctx.fillStyle = def.paper ?? '#f7e0c0'; ctx.fillRect(0, 0, W, H); } // Il link da toccare: un riquadro che pulsa intorno al mattone del muro. if (c.step === 'site') { const [x, y, w, h] = linkBox(def, def.siteSize), pulse = 0.5 + 0.5 * Math.sin(time / 260); ctx.strokeStyle = `rgba(255, 210, 90, ${0.5 + 0.5 * pulse})`; ctx.lineWidth = 2 + pulse; ctx.strokeRect(Math.round(x) - 2, Math.round(y) - 2, Math.round(w) + 4, Math.round(h) + 4); } ctx.imageSmoothingEnabled = false; ctx.restore(); } // Il suggerimento in alto, finché c'è qualcosa da toccare. export const telefonoHint = c => c.step === 'phone' && c.age > RISE ? 'Clic o tocco: guarda il sito' : c.step === 'site' ? 'Clic o tocco sul Muro del Sanpierino' : null;