feedback1 ui: folding the shop in prep moves the own board to the official shop-collapsed camera (GitHub #5)
While the own prep board is shown and the shop bar is folded, the game asks for the prep camera with shop: false
(left_prepare_camera_param / the boss prepare params) and the view keeps the bench above the folded shop's HUD band:
the higher of the folded tab and the corner buttons' hit areas (hudBands(kind, size, { shop: false }), cornerBand).
Unfolding returns to the shop camera. Never over the pen, a teammate's board or a battle; deferred while a piece is
dragged or its direction is chosen (gameLogic prepCameraFor / foldCamera). Below 768 px of width the corner stays on
one row while the bar is folded. Tile px at row 10: 1920x1080 109.8 -> 130.7, 844x390 35.9 -> 44.3,
756x366 31.4 -> 40.1, every bench pad fully pressable.
This commit is contained in:
1 parent
842e1e13c4
commit
82c54897c7
8 files changed
+394
-33
No files matched your search
+61
-17
@@ -43,41 +43,85 @@ export function hudPadding(kind, size) {
|
||||
* 2.15rem measured) and the shop bar's top edge above the viewport's bottom (css/screens/game-shop.css .shopbar
|
||||
* bottom .2rem + .shopbar__row padding .1rem ×2 + card height 2.24rem, plus its 2 px + 1 px borders). The shop bar
|
||||
* sits on the viewport's bottom edge even on a notched phone (css/devices.css, DESIGN §18.1).
|
||||
* The folded shop (public issue #5): the tab's top edge (.shopbar-tab bottom .2rem + padding .08rem ×2 + its .44rem
|
||||
* button, plus its 2 px + 1 px borders; it sits inside the HUD layer, above the bottom safe-area inset) and the corner
|
||||
* buttons' top edge (css/screens/game.css .gm__corner bottom .24rem + a .56rem row — the fallback when the corner
|
||||
* cannot be measured: on phones its buttons grow to 34 px and below 768 CSS px of width they wrap into two rows,
|
||||
* css/devices.css).
|
||||
*/
|
||||
export const HUD_REM = Object.freeze({ bondStripBottom: 2.16, shopBarTop: 2.64, shopBarBorderPx: 3 });
|
||||
export const HUD_REM = Object.freeze({
|
||||
bondStripBottom: 2.16, shopBarTop: 2.64, shopBarBorderPx: 3, shopTabTop: 0.8, shopTabBorderPx: 3, cornerTop: 0.8,
|
||||
});
|
||||
|
||||
/**
|
||||
* CSS px of HUD along the top edge (top bar + bond strip) and the bottom edge (the shop bar) of the viewport during
|
||||
* prep — the own board ('prep') or the Final Assault half ('bossPrep'); null for every other camera. The prep camera
|
||||
* keeps the bench / temp rows and the field's back row clear of them (render/projection.js clearHud; user playtest
|
||||
* #5 item 9: the rem floor of 40 px makes the HUD relatively taller on phones in landscape and the shop bar covered
|
||||
* the bench). The prep camera is the shop camera whether or not the bar is collapsed, so the band assumes the bar —
|
||||
* also for an eliminated player's own board (no shop bar: the band only costs size there, while a camera following
|
||||
* the bar's presence would have to re-frame whenever it appears, e.g. when the private state arrives after the prep
|
||||
* camera was set). Scouting a teammate's board uses the 'normal' camera: no band. An armed shop card (two-tap buy,
|
||||
* css/screens/game-shop.css .scard.is-armed) rises 4 px above the bar's top on a phone and covers the bench pads'
|
||||
* near corners by ≈ 3 px while it stays armed — less than under the unchanged official camera at 1920×1080 (13 px
|
||||
* above the bar, ≈ 11 px over the pads).
|
||||
* CSS px of HUD along the top edge (top bar + bond strip) and the bottom edge of the viewport during prep — the own
|
||||
* board ('prep') or the Final Assault half ('bossPrep'); null for every other camera. The prep camera keeps the bench /
|
||||
* temp rows and the field's back row clear of them (render/projection.js clearHud; user playtest #5 item 9: the rem
|
||||
* floor of 40 px makes the HUD relatively taller on phones in landscape and the shop bar covered the bench).
|
||||
* The bottom band is the shop bar's — also for an eliminated player's own board (no shop bar: the band only costs size
|
||||
* there, while a camera following the bar's presence would have to re-frame whenever it appears, e.g. when the private
|
||||
* state arrives after the prep camera was set) — unless `opts.shop === false`: the player folded the shop (收起) and the
|
||||
* own prep board moved to the official shop-collapsed camera (public issue #5: the board did not grow; screens/game.js
|
||||
* asks for it only while a folded bar is shown). Its band is the higher of the folded tab (bottom right) and the corner
|
||||
* buttons' hit areas (交流 / ⚙ / 📖 / ⛶, bottom left, measured by `cornerBand`: on a narrow phone two rows of 34 px buttons
|
||||
* with 44 px touch areas) — under the official collapsed camera the bench's left pads reach under the corner, and with
|
||||
* the tab alone it would cover bench pad 0 at the user's 756×366 Android and the near edge of pads 0–2 at 844×390; both
|
||||
* are 1-D bands like the bar's, so every bench pad stays fully pressable. Scouting a
|
||||
* teammate's board uses the 'normal' camera: no band. An armed shop card (two-tap buy, css/screens/game-shop.css
|
||||
* .scard.is-armed) rises 4 px above the bar's top on a phone and covers the bench pads' near corners by ≈ 3 px while it
|
||||
* stays armed — less than under the unchanged official camera at 1920×1080 (13 px above the bar, ≈ 11 px over the pads).
|
||||
* @param {string} kind
|
||||
* @param {{ width: number, height: number }} size
|
||||
* @param {{ shop?: boolean }} [opts] `shop: false` = the folded shop's band
|
||||
* @returns {{ top: number, bottom: number }|null}
|
||||
*/
|
||||
export function hudBands(kind, size) {
|
||||
export function hudBands(kind, size, opts) {
|
||||
if (kind !== 'prep' && kind !== 'bossPrep') return null;
|
||||
const folded = !!opts && opts.shop === false;
|
||||
const h = size?.height || 1080;
|
||||
let rem = 100;
|
||||
let safeTop = 0;
|
||||
let safeBottom = 0;
|
||||
let corner = 0;
|
||||
try {
|
||||
rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 100;
|
||||
// the HUD layer starts below the top safe-area inset (css/devices.css .gm__hud)
|
||||
safeTop = Math.max(0, document.querySelector('.gm__hud')?.getBoundingClientRect().top || 0);
|
||||
// the HUD layer starts below the top safe-area inset and ends above the bottom one (css/devices.css .gm__hud)
|
||||
const hud = document.querySelector('.gm__hud')?.getBoundingClientRect();
|
||||
safeTop = Math.max(0, hud?.top || 0);
|
||||
if (folded) {
|
||||
if (hud && hud.bottom > 0) safeBottom = Math.max(0, h - hud.bottom);
|
||||
corner = cornerBand(h);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
const h = size?.height || 1080;
|
||||
const bottom = folded
|
||||
? Math.max(safeBottom + rem * HUD_REM.shopTabTop + HUD_REM.shopTabBorderPx, corner || safeBottom + rem * HUD_REM.cornerTop)
|
||||
: rem * HUD_REM.shopBarTop + HUD_REM.shopBarBorderPx;
|
||||
return {
|
||||
top: Math.min(h * 0.4, safeTop + rem * HUD_REM.bondStripBottom),
|
||||
bottom: Math.min(h * 0.4, rem * HUD_REM.shopBarTop + HUD_REM.shopBarBorderPx),
|
||||
bottom: Math.min(h * 0.4, bottom),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS px from the viewport's bottom edge (`h` high) up to the top of the corner buttons' hit areas (交流 / ⚙ / 📖 / ⛶,
|
||||
* .gm__corner), 0 when they are not on the page. On a touch screen every one of them takes taps from an invisible area
|
||||
* of at least --tap-min (44 px) centred on it (css/devices.css touch targets), 5 px above a 34 px phone button — a bench
|
||||
* pad edge there is not pressable.
|
||||
* @param {number} h
|
||||
*/
|
||||
function cornerBand(h) {
|
||||
const btns = document.querySelectorAll('.gm__corner .gm__gear, .gm__corner .ewheel__btn');
|
||||
if (!btns || !btns.length) return 0;
|
||||
const root = document.documentElement;
|
||||
const tap = root?.classList?.contains('sp-coarse') ? parseFloat(getComputedStyle(root).getPropertyValue('--tap-min')) || 0 : 0;
|
||||
let top = Infinity;
|
||||
for (const b of btns) {
|
||||
const r = b.getBoundingClientRect();
|
||||
if (r && r.height > 0) top = Math.min(top, r.top + r.height / 2 - Math.max(r.height, tap) / 2);
|
||||
}
|
||||
return top < Infinity ? Math.max(0, h - top) : 0;
|
||||
}
|
||||
|
||||
function renderPref() {
|
||||
try {
|
||||
const q = new URLSearchParams(globalThis.location?.search || '').get('render');
|
||||
|
||||
Reference in new issue
Block a user