Clear the frame to the apron colour

That band at the bottom was not a background mismatch — the canvas was
transparent there. render() never cleared: the stands cover the top and the
out-area quad covers the middle, so it looked complete, but the quad only
reached z=30 and everything below where that projects was see-through. The
page showed through it, which is why it read as a slightly duller green.

The frame is now cleared to pal.out before anything draws, oversized by 8px so
a camera shake cannot expose an edge, and the out-area quad runs to z=60 so the
perspective floor reaches the bottom on any aspect. Verified by sampling alpha
down the bottom third: opaque everywhere, one uniform colour, matching the page
gradient behind it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-07-30 19:22:23 +02:00
1 parent 7b15e10494
commit c9699da6f1
3 files changed
+17 -4

No files matched your search

+7
View File
@@ -186,6 +186,13 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
because the wave grows with `hordeGrow` while the panic button was a flat 1 s; he chose scaling the because the wave grows with `hordeGrow` while the panic button was a flat 1 s; he chose scaling the
melee over shrinking the horde. **Measure the crowd BEFORE calling `meleeSwipe`** — it kills melee over shrinking the horde. **Measure the crowd BEFORE calling `meleeSwipe`** — it kills
everything in reach, so counting after counts the survivors and the cooldown barely moves. everything in reach, so counting after counts the survivors and the cooldown barely moves.
- **`render()` clears the whole frame to `pal.out` first.** There was NO clear for a long time: the
stands cover the top and the out-area quad covers the middle, so it looked complete — but the quad
only reached z=30 and BELOW where that projects the canvas was simply transparent. On a phone that
read as a duller band across the bottom of the screen. The quad now runs to z=60 as well, so the
perspective floor reaches the bottom on any aspect ratio. If you ever add a new orientation or
stretch the canvas further, check the bottom rows are opaque — sample `getImageData` alpha, the
colour alone will not tell you, because the page behind is deliberately the same green.
- **The page background behind the canvas tracks the live court apron** (`pageBackground` in - **The page background behind the canvas tracks the live court apron** (`pageBackground` in
render.js). It is visible in exactly two places: the strip above the canvas under the status bar, render.js). It is visible in exactly two places: the strip above the canvas under the status bar,
and the slivers a phone's ROUNDED DISPLAY CORNERS clip out of the canvas — a flat dark body drew a and the slivers a phone's ROUNDED DISPLAY CORNERS clip out of the canvas — a flat dark body drew a
+1 -1
View File
@@ -80,6 +80,6 @@
<p class="start">TAP TO START</p> <p class="start">TAP TO START</p>
</div> </div>
</div> </div>
<script type="module" src="main.js?v=48"></script> <script type="module" src="main.js?v=49"></script>
</body> </body>
</html> </html>
+9 -3
View File
@@ -129,7 +129,7 @@ export function proj(x, y, z) {
// ---------------------------------------------------------------- sprites ---- // ---------------------------------------------------------------- sprites ----
// assets/<name>.png; game falls back to code-drawn figures until sprites load. // assets/<name>.png; game falls back to code-drawn figures until sprites load.
const SPRV = 48; // asset cache-buster — bump with main.js?v=N (see the img.src note below) const SPRV = 49; // asset cache-buster — bump with main.js?v=N (see the img.src note below)
const SPR = {}; const SPR = {};
for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_swing1','p_swing2', for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_swing1','p_swing2',
'o_idle1','o_idle2','o_bounce','o_bounce2','o_toss','o_smash','o_run1','o_run2','o_swing1','o_swing2', 'o_idle1','o_idle2','o_bounce','o_bounce2','o_toss','o_smash','o_run1','o_run2','o_swing1','o_swing2',
@@ -611,7 +611,13 @@ export function render() {
} }
const pal = palette(), pt = paletteT(); const pal = palette(), pt = paletteT();
pageBackground(pal.out); // keep the page behind the canvas matching the court's apron // CLEAR THE WHOLE FRAME to the apron colour first. There was no clear at all: the stands cover the
// top and the out-area quad covers the middle, but the quad only reaches z=30, and below where that
// projects the canvas was simply TRANSPARENT — you saw the page through it as a duller band across
// the bottom of the screen. Oversized by 8px so a camera shake can never expose an edge.
ctx.fillStyle = pal.out;
ctx.fillRect(-8, -8, W + 16, H + 16);
pageBackground(pal.out); // ...and the page behind it matches, for the rounded display corners
// stands: the generated crowd, drawn in columns so it can BOUNCE when it cheers. // stands: the generated crowd, drawn in columns so it can BOUNCE when it cheers.
// sit the ad strip flush on the court's back edge (the out-area quad below reaches z=-6); // sit the ad strip flush on the court's back edge (the out-area quad below reaches z=-6);
// pinning to that projected line keeps the boards clear of the lawn in BOTH orientations // pinning to that projected line keeps the boards clear of the lawn in BOTH orientations
@@ -656,7 +662,7 @@ export function render() {
// ground + court (Super Tennis lawn); everything outside the doubles court is OUT // ground + court (Super Tennis lawn); everything outside the doubles court is OUT
const dw = COURT.dblW / 2; const dw = COURT.dblW / 2;
quadXZ(-14, -6, 14, 30, pal.out); // out area quadXZ(-14, -6, 14, 60, pal.out); // out area (past the bottom on any aspect)
quadXZ(-dw - 0.15, -0.15, dw + 0.15, COURT.L + 0.15, '#e8e8dc'); // perimeter border line quadXZ(-dw - 0.15, -0.15, dw + 0.15, COURT.L + 0.15, '#e8e8dc'); // perimeter border line
quadXZ(-dw, 0, dw, COURT.L, pal.lawn); // lawn (doubles court) quadXZ(-dw, 0, dw, COURT.L, pal.lawn); // lawn (doubles court)
for (let z = 0; z < COURT.L; z += 2.97) quadXZ(-dw, z, dw, Math.min(z + 1.49, COURT.L), pal.stripe); // mow stripes for (let z = 0; z < COURT.L; z += 2.97) quadXZ(-dw, z, dw, Math.min(z + 1.49, COURT.L), pal.stripe); // mow stripes