From c84b0637b1840cd11105e283ac1efe60c578627c Mon Sep 17 00:00:00 2001 From: Benjamin Sanders Date: Fri, 9 Oct 2026 11:19:02 -0400 Subject: added potrait mode to trick or treat game --- trick-or-treat-run/index.html | 77 ++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 19 deletions(-) (limited to 'trick-or-treat-run') diff --git a/trick-or-treat-run/index.html b/trick-or-treat-run/index.html index 3050380..3e7faca 100644 --- a/trick-or-treat-run/index.html +++ b/trick-or-treat-run/index.html @@ -127,8 +127,17 @@ cursor: pointer; } #bar button:hover { border-color: var(--orange); } - #rotate { display: none; color: var(--dim); font-size: 13px; } - @media (orientation: portrait) and (max-width: 600px) { #rotate { display: block; } } + @media (orientation: portrait) and (max-width: 700px) { + body { padding: 8px; gap: 8px; justify-content: flex-start; } + h1 { font-size: 18px; margin-top: 4px; } + #wrap { + aspect-ratio: auto; + width: calc(100vw - 16px); + height: calc(100vh - 100px); + height: calc(100svh - 100px); + } + #bar span { display: none; } + } @media (max-height: 520px) { body { padding: 8px; gap: 8px; } h1, #bar span { display: none; } @@ -148,7 +157,6 @@
-
Turn your phone sideways for a bigger view.
@@ -172,12 +180,31 @@ const rand = (a, b) => a + Math.random() * (b - a); const TAU = Math.PI * 2; + // The game world is always 800 x 450. V is the part of it on screen. In landscape that is + // the whole world. In portrait the camera shows a narrower slice with extra sky above and + // extra ground below, and the game runs a little slower since you see less road ahead. + let V = { x0: 0, x1: W, y0: 0, y1: H }; + let PACE = 1; + let portrait = false; function resize() { const r = cv.getBoundingClientRect(); const dpr = Math.min(2, window.devicePixelRatio || 1); cv.width = Math.max(1, Math.round(r.width * dpr)); cv.height = Math.max(1, Math.round(r.height * dpr)); - ctx.setTransform(cv.width / W, 0, 0, cv.height / H, 0, 0); + portrait = r.height > r.width * 1.05; + if (!portrait) { + V = { x0: 0, x1: W, y0: 0, y1: H }; + PACE = 1; + ctx.setTransform(cv.width / W, 0, 0, cv.height / H, 0, 0); + return; + } + const VW = 490, CX = 95; // visible world width, camera left edge + const s = cv.width / VW; // device pixels per world unit + const VH = cv.height / s; // visible world height + const offY = VH * 0.6 - GROUND; // ground line sits a bit below the middle + V = { x0: CX, x1: CX + VW, y0: -offY, y1: VH - offY }; + PACE = 0.76; + ctx.setTransform(s, 0, 0, s, -CX * s, offY * s); } window.addEventListener('resize', resize); @@ -219,7 +246,7 @@ setMuteLabel(); /* ---------- scenery (generated once) ---------- */ - const STARS = Array.from({ length: 80 }, () => ({ x: Math.random() * W, y: Math.random() * 250, r: Math.random() * 1.3 + 0.3, p: Math.random() * TAU })); + const STARS = Array.from({ length: 320 }, () => ({ x: Math.random() * W, y: rand(-900, 250), r: Math.random() * 1.3 + 0.3, p: Math.random() * TAU })); function makeHouses() { const arr = []; @@ -278,7 +305,7 @@ function reset() { g = { - t: 0, dist: 0, candy: 0, speed: 360, rush: 0, shake: 0, + t: 0, dist: 0, candy: 0, speed: 360 * PACE, rush: 0, shake: 0, obs: [], items: [], parts: [], nextSpawn: 700, toastIdx: 0, player: { x: 140, y: GROUND, vy: 0, onGround: true, jumps: 0, holding: false, run: 0 } }; @@ -453,7 +480,7 @@ function update(dt) { g.t += dt; - g.speed = Math.min(900, 360 + g.t * 9); + g.speed = PACE * Math.min(900, 360 + g.t * 9); if (g.rush > 0) g.rush = Math.max(0, g.rush - dt); const v = worldSpeed(); g.dist += v * dt; @@ -533,26 +560,28 @@ /* ---------- drawing ---------- */ function drawSky(time) { - const sky = ctx.createLinearGradient(0, 0, 0, GROUND); + const sky = ctx.createLinearGradient(0, V.y0, 0, GROUND); sky.addColorStop(0, '#0d0920'); sky.addColorStop(1, '#2b163b'); ctx.fillStyle = sky; - ctx.fillRect(0, 0, W, GROUND); + ctx.fillRect(V.x0, V.y0, V.x1 - V.x0, GROUND - V.y0); + ctx.fillStyle = '#fff'; for (const s of STARS) { + if (s.y < V.y0 || s.x < V.x0 || s.x > V.x1) continue; ctx.globalAlpha = 0.45 + 0.4 * Math.sin(time * 2 + s.p); - ctx.fillStyle = '#fff'; ctx.fillRect(s.x, s.y, s.r, s.r); } ctx.globalAlpha = 1; - const mg = ctx.createRadialGradient(640, 92, 20, 640, 92, 120); + const mx = V.x1 - 160, my = V.y0 + (GROUND - V.y0) * 0.24; + const mg = ctx.createRadialGradient(mx, my, 20, mx, my, 120); mg.addColorStop(0, 'rgba(255,236,190,.35)'); mg.addColorStop(1, 'rgba(255,236,190,0)'); ctx.fillStyle = mg; - ctx.fillRect(500, 0, 280, 240); + ctx.fillRect(mx - 140, my - 120, 280, 240); ctx.fillStyle = '#f6e7c1'; - ctx.beginPath(); ctx.arc(640, 92, 38, 0, TAU); ctx.fill(); + ctx.beginPath(); ctx.arc(mx, my, 38, 0, TAU); ctx.fill(); ctx.fillStyle = 'rgba(200,180,140,.35)'; - [[628, 82, 7], [652, 102, 5], [646, 76, 4], [626, 104, 4]].forEach(([x, y, r]) => { ctx.beginPath(); ctx.arc(x, y, r, 0, TAU); ctx.fill(); }); + [[-12, -10, 7], [12, 10, 5], [6, -16, 4], [-14, 12, 4]].forEach(([x, y, r]) => { ctx.beginPath(); ctx.arc(mx + x, my + y, r, 0, TAU); ctx.fill(); }); } function drawHills(off) { @@ -669,9 +698,16 @@ ctx.beginPath(); ctx.moveTo(x, GROUND + 2); ctx.lineTo(x - 12, GROUND + 44); ctx.stroke(); } ctx.fillStyle = '#17141e'; - ctx.fillRect(0, GROUND + 44, W, H - GROUND - 44); + ctx.fillRect(0, GROUND + 44, W, Math.max(H, V.y1) - GROUND - 44); ctx.fillStyle = '#3a3448'; ctx.fillRect(0, GROUND + 44, W, 3); + if (V.y1 > H) { + // portrait: a dashed center line on the road below the sidewalk + ctx.fillStyle = '#4a4258'; + const ly = GROUND + 44 + Math.min(70, (V.y1 - GROUND - 44) / 2); + const off2 = dist % 120; + for (let x = -off2; x < W + 20; x += 120) ctx.fillRect(x, ly, 60, 4); + } } function drawPumpkin(o) { @@ -850,19 +886,22 @@ ctx.fillStyle = gr; ctx.fillRect(b.x - b.r, b.y - b.r, b.r * 2, b.r * 2); } - const edge = ctx.createLinearGradient(W * 0.45, 0, W, 0); + const vw = V.x1 - V.x0; + const edge = ctx.createLinearGradient(V.x0 + vw * 0.45, 0, V.x1, 0); edge.addColorStop(0, 'rgba(14,10,22,0)'); edge.addColorStop(1, `rgba(14,10,22,${Math.min(0.85, f * 1.35)})`); ctx.fillStyle = edge; - ctx.fillRect(0, 0, W, H); + ctx.fillRect(V.x0, V.y0, vw, V.y1 - V.y0); } function drawVignette() { - const vg = ctx.createRadialGradient(W / 2, H / 2, H * 0.35, W / 2, H / 2, H * 0.95); + const cx = (V.x0 + V.x1) / 2, cy = (V.y0 + V.y1) / 2; + const span = Math.max(V.x1 - V.x0, V.y1 - V.y0) / 2; + const vg = ctx.createRadialGradient(cx, cy, span * 0.6, cx, cy, span * 1.15); vg.addColorStop(0, 'rgba(0,0,0,0)'); vg.addColorStop(1, 'rgba(0,0,0,.55)'); ctx.fillStyle = vg; - ctx.fillRect(0, 0, W, H); + ctx.fillRect(V.x0, V.y0, V.x1 - V.x0, V.y1 - V.y0); } function draw(time) { -- cgit v1.2.3