summaryrefslogtreecommitdiff
path: root/trick-or-treat-run
diff options
context:
space:
mode:
authorBenjamin Sanders <hello@bensanders.net>2026-10-09 11:19:02 -0400
committerBenjamin Sanders <hello@bensanders.net>2026-10-09 11:19:02 -0400
commitc84b0637b1840cd11105e283ac1efe60c578627c (patch)
tree811a5c76350bd0ee498d6b3145cece9a55b762fe /trick-or-treat-run
parent93efa1b793710caaa838cca7f6231d8ad768f5dd (diff)
added potrait mode to trick or treat game
Diffstat (limited to 'trick-or-treat-run')
-rw-r--r--trick-or-treat-run/index.html77
1 files changed, 58 insertions, 19 deletions
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 @@
<div id="toast"></div>
<div id="screen"></div>
</div>
- <div id="rotate">Turn your phone sideways for a bigger view.</div>
<div id="bar">
<button id="mute" type="button">Sound: on</button>
<button id="pause" type="button">Pause</button>
@@ -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) {