THE PHONE: the game was unplayable on one, and an audit says so
Moving the station card off the ticket last commit only fixed desktop. An automated occlusion sweep — enter every station, elementFromPoint at the card's centre, check who is actually painted there — confirmed all 13 cards clear at 1280px, and then found the same collision alive and well at 375px, where a 240px card and a 272px ticket cannot both sit at the top of a 375px screen. Three real problems, all of them "nobody has opened this on a phone since the touch controls shipped": 1. THE PANELS. Both were still desktop width. Narrowed to 50vw/41vw on small screens with the type scaled down, and the ticket capped at 46vh and scrollable so a chatty customer cannot shove the card off the bottom of the world. 2. THE CAMERA. Every scene is composed left-to-right — the spice rack, the coal bed, the drawer — and a perspective camera holds its VERTICAL fov, so a portrait phone silently crops the sides off all of it. On a 375x812 screen the outer spice jars were simply not on the display, and you cannot drag a jar you cannot see. Narrow viewports now widen the fov to hold the horizontal extent instead — capped at 74 degrees, because the honest 16:9 lock wants 112 and that is not a camera, it is a peephole (tried it; the bench went small and distant in the middle of an empty floor). Desktop is untouched at 42. 3. THE TICKET COVERED THE BENCH. Even framed correctly, the ask sat on top of three of the six spice jars. It now folds on a tap to the day, the customer and the demo button, with 'tap to read' underneath, and unfolds again on the next order because you have not read that one. Verified at 375x812: no occlusion at any station, no horizontal overflow, outer jars at 0.75 NDC and reachable, ticket 376px → 97px folded. Desktop back to fov 42 and unchanged: grill 9.4, pan 8.0, eggs 10.0, chips 10.0, curry 10.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0e092e2551
commit
00e3f1694e
@ -147,6 +147,25 @@ export class App {
|
||||
const h = Math.max(1, window.innerHeight);
|
||||
this.renderer.setSize(w, h, false);
|
||||
this.camera.aspect = w / h;
|
||||
// Every scene is composed left-to-right — the spice rack, the coal bed, the
|
||||
// drawer of cutlery. A perspective camera holds its VERTICAL fov, so a
|
||||
// portrait phone silently crops the sides off all of it: on a 375x812
|
||||
// screen the outer spice jars were simply not on the display, and you
|
||||
// cannot drag a jar you cannot see. So below 1:1 we widen the vertical fov
|
||||
// to hold the HORIZONTAL extent the scenes were framed for instead.
|
||||
const DESIGN_ASPECT = 16 / 9;
|
||||
const BASE_FOV = 42;
|
||||
// Capped, because holding the FULL 16:9 width on a 0.46 phone needs 112
|
||||
// degrees, and that is not a camera, it is a peephole — the bench went
|
||||
// small and distant in the middle of an empty floor. 74 gets the outer
|
||||
// props back on screen while still looking like a lens.
|
||||
const MAX_FOV = 74;
|
||||
if (this.camera.aspect < DESIGN_ASPECT) {
|
||||
const halfW = Math.tan((BASE_FOV * Math.PI) / 360) * DESIGN_ASPECT;
|
||||
this.camera.fov = Math.min(MAX_FOV, (Math.atan(halfW / this.camera.aspect) * 360) / Math.PI);
|
||||
} else {
|
||||
this.camera.fov = BASE_FOV;
|
||||
}
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.view?.resize?.(w, h);
|
||||
}
|
||||
|
||||
@ -185,6 +185,13 @@ export class Game {
|
||||
|
||||
this.ticket = new Panel('ticket');
|
||||
this.ticketEl = el('div', 'ticket-body', this.ticket.root);
|
||||
// On a phone the ticket covers half the bench — and you cannot drag a
|
||||
// spice jar you cannot reach. Tap it to fold it down to the day and the
|
||||
// customer; tap again to read the whole ask. Desktop ignores this.
|
||||
this.ticket.root.addEventListener('click', (e) => {
|
||||
if ((e.target as HTMLElement).tagName === 'BUTTON') return; // the demo button still works
|
||||
if (window.matchMedia('(max-width: 600px)').matches) this.ticket.root.classList.toggle('folded');
|
||||
});
|
||||
this.tempPanel = new Panel('temp-toggle');
|
||||
this.tempPanel.hide();
|
||||
|
||||
@ -1298,6 +1305,7 @@ export class Game {
|
||||
}
|
||||
|
||||
private renderTicket(): void {
|
||||
this.ticket.root.classList.remove('folded');
|
||||
const o = this.order;
|
||||
this.ticketEl.innerHTML = '';
|
||||
el('div', 'ticket-day', this.ticketEl, `DAY ${o.day}`);
|
||||
|
||||
@ -895,3 +895,51 @@ body:has(#ui > .title) #touchpad { display: none; }
|
||||
background: #f0e2c0;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
/* Small screens: the ask and the live words have to SHARE 375px, and both
|
||||
were still desktop width — the card landed straight back under the ticket
|
||||
on every phone. Narrow both, shrink the type, and cap the ticket's height
|
||||
so a chatty customer cannot push the card off the bottom of the world. */
|
||||
@media (max-width: 600px) {
|
||||
.ticket {
|
||||
width: 50vw;
|
||||
padding: 10px 11px 9px;
|
||||
font-size: 11px;
|
||||
max-height: 46vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ticket-face {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
top: 7px;
|
||||
right: 8px;
|
||||
}
|
||||
.slicer-card {
|
||||
width: 41vw;
|
||||
top: 12px;
|
||||
right: 10px;
|
||||
padding: 10px 11px;
|
||||
}
|
||||
.slicer-ask { font-size: 11px; }
|
||||
.slicer-thick,
|
||||
.slicer-wobble { font-size: 10px; }
|
||||
.drawer-hint { font-size: 9px; }
|
||||
.taste-axis { width: 34px; font-size: 8px; }
|
||||
}
|
||||
|
||||
/* Folded ticket (phones): the day, who is asking, and the demo button. The
|
||||
rest is one tap away, and the bench underneath is reachable again. */
|
||||
@media (max-width: 600px) {
|
||||
.ticket { cursor: pointer; }
|
||||
.ticket.folded { max-height: none; }
|
||||
.ticket.folded .ticket-body > *:not(.ticket-day):not(.ticket-who):not(.ticket-face):not(.watch-btn):not(.ticket-critic) {
|
||||
display: none;
|
||||
}
|
||||
.ticket.folded::after {
|
||||
content: 'tap to read';
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font: 9px ui-monospace, monospace;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user