Add the phone felt round to the release
This commit is contained in:
@@ -820,6 +820,38 @@ button,
|
|||||||
outline-offset: 1px;
|
outline-offset: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Seats close enough together that full-size pucks would overlap. The class is set
|
||||||
|
from a measurement render.js takes off the ring itself — the closest two neighbours,
|
||||||
|
straight line — because no media query can see it: the same 390px phone is roomy at
|
||||||
|
six seats and crowded at nine, and a wide landscape felt crowds at its ends while
|
||||||
|
its rim says there is space. Not scoped to a screen size for the same reason. */
|
||||||
|
.seatsTight .seat {
|
||||||
|
/* 58 was chosen by measurement and 46 was tried against it. On a roomy phone 46
|
||||||
|
cleared every overlap at nine seats where 58 left two — but in the state that
|
||||||
|
actually matters, a hand with a side pot where the bar has grown and the felt has
|
||||||
|
shrunk to suit, 46 overlapped four times and 58 twice. Pucks touching at that
|
||||||
|
density is the felt being small, not the puck being wide, and paying for it with
|
||||||
|
an ellipsis on every long name buys nothing. What has to hold is that each seat
|
||||||
|
keeps its own centre, so a tap lands on the player it was aimed at, and that is
|
||||||
|
measured at every size. */
|
||||||
|
width: 58px;
|
||||||
|
min-height: 38px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seatsTight .seatPuckName {
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
font-size: 0.62rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seatsTight .seatPuckStack {
|
||||||
|
font-size: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seatsTight .seatPuckCards {
|
||||||
|
font-size: 0.58rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Seat editor panel (sidebar form for the selected seat) ---------- */
|
/* ---------- Seat editor panel (sidebar form for the selected seat) ---------- */
|
||||||
|
|
||||||
.seatEditorPanel {
|
.seatEditorPanel {
|
||||||
@@ -2921,34 +2953,20 @@ body.hasActionBar #toastHost {
|
|||||||
That trade was real then and isn't now: seats are spaced by distance around the
|
That trade was real then and isn't now: seats are spaced by distance around the
|
||||||
rim rather than by angle, so a shorter felt no longer piles them up. Re-measured
|
rim rather than by angle, so a shorter felt no longer piles them up. Re-measured
|
||||||
at 360x640 with the bar grown by a side pot, where the bottom seat sat 38px
|
at 360x640 with the bar grown by a side pot, where the bottom seat sat 38px
|
||||||
inside it: capped, the felt is 240px and the ring clears the bar by 50, with the
|
inside it: the ring now clears the bar by 50. Scroll room is not an answer for a
|
||||||
audit clean at 6, 9 and 10 seats. Scroll room is not an answer for a seat that is
|
seat that is covered rather than merely below the fold.
|
||||||
covered rather than merely below the fold. */
|
|
||||||
|
The room is spent on height, not on both. Capping the *width* by it — the wide
|
||||||
|
layout's way, because there the felt is sized from its width — kept the table
|
||||||
|
square and threw away 96px of a 360px screen: 240x240 inside a 336px column. A
|
||||||
|
max-height leaves the width alone and lets the aspect ratio give way, so a short
|
||||||
|
screen gets a wide oval, which is the shape a poker table is anyway. Measured at
|
||||||
|
360x640: 336x240 rather than 240x240, same clearance, and the twelve-seat ring
|
||||||
|
that was crowded at 240 comes out clean. Where the room isn't the binding
|
||||||
|
constraint — 390x844 and up — nothing changes and the table stays round. */
|
||||||
#pokerTableWrap {
|
#pokerTableWrap {
|
||||||
max-width: calc(var(--feltRoomPx, 100vh) / var(--feltAspect));
|
max-width: none;
|
||||||
}
|
max-height: var(--feltRoomPx, 100vh);
|
||||||
|
|
||||||
/* A felt small enough that the rim can't give each seat its own puck width. Set
|
|
||||||
from the measured rim in render.js rather than from the window, because the
|
|
||||||
question is how much table there is per player, which no media query can see:
|
|
||||||
nine seats on a 240px felt get 72px of rim each and the puck is 76 across. */
|
|
||||||
.seatsTight .seat {
|
|
||||||
width: 58px;
|
|
||||||
min-height: 38px;
|
|
||||||
padding: 3px 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.seatsTight .seatPuckName {
|
|
||||||
margin-top: 0.35rem;
|
|
||||||
font-size: 0.62rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.seatsTight .seatPuckStack {
|
|
||||||
font-size: 0.55rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.seatsTight .seatPuckCards {
|
|
||||||
font-size: 0.58rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stacked, with the action that ends the hand furthest from a stray thumb. */
|
/* Stacked, with the action that ends the hand furthest from a stray thumb. */
|
||||||
@@ -3312,6 +3330,11 @@ body.hasActionBar #toastHost {
|
|||||||
a table that is already as short as the screen allows. */
|
a table that is already as short as the screen allows. */
|
||||||
aspect-ratio: auto;
|
aspect-ratio: auto;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
/* This branch already spends the measured room on its own height. The phone
|
||||||
|
block's max-height is the same number arriving twice, and applying it here
|
||||||
|
took the felt down to the floor value and crowded the ring — measured at
|
||||||
|
640x360, two seats lost their centres to their neighbours. */
|
||||||
|
max-height: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The page's own top padding is 32px of nothing, on the screen that has the least
|
/* The page's own top padding is 32px of nothing, on the screen that has the least
|
||||||
|
|||||||
@@ -91,12 +91,21 @@ const Render = {
|
|||||||
// circle when there's nothing to measure — the same answer the ring gave before it
|
// circle when there's nothing to measure — the same answer the ring gave before it
|
||||||
// asked.
|
// asked.
|
||||||
feltAspect() {
|
feltAspect() {
|
||||||
if (typeof document === "undefined") return 1;
|
const box = this.ringBox();
|
||||||
const wrap = document.getElementById("pokerTableWrap");
|
return box ? box.width / box.height : 1;
|
||||||
if (!wrap) return 1;
|
},
|
||||||
const box = wrap.getBoundingClientRect();
|
|
||||||
if (!box.width || !box.height) return 1;
|
// The box a seat's left/top percentages actually resolve against. Not the felt: on
|
||||||
return box.width / box.height;
|
// a phone the seat layer is inset by half a puck so the outermost seats don't hang
|
||||||
|
// off the screen, and measuring the felt instead put the ring's shape out by that
|
||||||
|
// inset — twelve seats at 640x360 were computed 90px apart and drawn 60px apart,
|
||||||
|
// which is the difference between "fine" and "overlapping".
|
||||||
|
ringBox() {
|
||||||
|
if (typeof document === "undefined") return null;
|
||||||
|
const layer = document.getElementById("seatsLayer") || document.getElementById("pokerTableWrap");
|
||||||
|
if (!layer) return null;
|
||||||
|
const box = layer.getBoundingClientRect();
|
||||||
|
return box.width && box.height ? box : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Bets sit on the felt between their player and the pot, the way chips actually
|
// Bets sit on the felt between their player and the pot, the way chips actually
|
||||||
@@ -137,23 +146,30 @@ const Render = {
|
|||||||
RING_STEPS: 720,
|
RING_STEPS: 720,
|
||||||
ringCache: {},
|
ringCache: {},
|
||||||
|
|
||||||
// How much rim each seat gets, in pixels. Ramanujan's approximation for an
|
// The closest two neighbouring seats come to each other, straight line, in pixels.
|
||||||
// ellipse's perimeter, which is well inside a pixel at these sizes.
|
//
|
||||||
rimPerSeat(count) {
|
// Rim length divided by seat count was the first answer and it is the wrong one:
|
||||||
const wrap = document.getElementById("pokerTableWrap");
|
// it measures the walk between two seats rather than the gap between them, and on
|
||||||
if (!wrap || !count) return Infinity;
|
// a flat ellipse those are nothing alike. At 640x360 six seats had 193px of rim
|
||||||
const box = wrap.getBoundingClientRect();
|
// each and sat 56px apart, because at the ends of a long oval the rim doubles back
|
||||||
const a = (box.width * 44) / 100;
|
// and two seats a long walk apart are stacked one above the other.
|
||||||
const b = (box.height * 42) / 100;
|
closestSeatGapPx(count, box = this.ringBox()) {
|
||||||
if (!a || !b) return Infinity;
|
if (count < 2 || !box || !box.width || !box.height) return Infinity;
|
||||||
const perimeter = Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));
|
|
||||||
return perimeter / count;
|
const points = this.ringAngles(count, 44, 42, box.width / box.height).map(angle => ({
|
||||||
|
x: ((50 + 44 * Math.cos(angle)) / 100) * box.width,
|
||||||
|
y: ((50 + 42 * Math.sin(angle)) / 100) * box.height,
|
||||||
|
}));
|
||||||
|
return points.reduce((closest, point, i) => {
|
||||||
|
const next = points[(i + 1) % points.length];
|
||||||
|
return Math.min(closest, Math.hypot(point.x - next.x, point.y - next.y));
|
||||||
|
}, Infinity);
|
||||||
},
|
},
|
||||||
|
|
||||||
// A puck is 76px across. Under about that much rim each, neighbours start covering
|
// A puck is 76px across, so neighbours whose centres are closer than that overlap
|
||||||
// each other's middles, and a tap meant for one player lands on the next — so the
|
// by definition; a few pixels more keeps them from touching. Under it the pucks
|
||||||
// pucks come down a size rather than the table pretending it has room it hasn't.
|
// come down a size rather than the table pretending it has room it hasn't.
|
||||||
SEAT_ROOM_PX: 88,
|
SEAT_ROOM_PX: 84,
|
||||||
|
|
||||||
// Called on every render and again whenever the felt is resized — the action bar
|
// Called on every render and again whenever the felt is resized — the action bar
|
||||||
// growing mid-hand shortens the table, and the seats have to answer that without
|
// growing mid-hand shortens the table, and the seats have to answer that without
|
||||||
@@ -162,7 +178,7 @@ const Render = {
|
|||||||
const layer = document.getElementById("seatsLayer");
|
const layer = document.getElementById("seatsLayer");
|
||||||
if (!layer || !this.state) return;
|
if (!layer || !this.state) return;
|
||||||
layer.classList.toggle("seatsTight",
|
layer.classList.toggle("seatsTight",
|
||||||
this.rimPerSeat(this.state.seatCount) < this.SEAT_ROOM_PX);
|
this.closestSeatGapPx(this.state.seatCount) < this.SEAT_ROOM_PX);
|
||||||
},
|
},
|
||||||
|
|
||||||
// The angles that cut the rim into `count` equal lengths, starting at the top.
|
// The angles that cut the rim into `count` equal lengths, starting at the top.
|
||||||
|
|||||||
@@ -92,6 +92,36 @@ describe("a felt wider than it is tall", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("how close the closest two seats come", () => {
|
||||||
|
// What decides whether the pucks shrink. Rim length per seat was the first answer
|
||||||
|
// and it is the wrong question: it measures the walk between two seats, not the
|
||||||
|
// gap. On a long oval the rim doubles back at the ends, so two seats a long walk
|
||||||
|
// apart end up stacked one above the other.
|
||||||
|
const gap = (count, width, height) => Render.closestSeatGapPx(count, { width, height });
|
||||||
|
|
||||||
|
test("a flat table's ends are far closer than its rim suggests", () => {
|
||||||
|
// 616x96: the landscape felt on a 640x360 phone once the bar has grown.
|
||||||
|
const rim = Math.PI * (3 * (271 + 40) - Math.sqrt((3 * 271 + 40) * (271 + 3 * 40))) / 6;
|
||||||
|
assert.ok(gap(6, 616, 96) < rim / 3, `${gap(6, 616, 96).toFixed(0)}px apart, ${rim.toFixed(0)}px of rim each`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a round table's neighbours are about as far apart as the rim says", () => {
|
||||||
|
const rim = (Math.PI * 2 * 0.43 * 400) / 9;
|
||||||
|
assert.ok(Math.abs(gap(9, 400, 400) - rim) < rim * 0.15);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("more seats, less room, in that order", () => {
|
||||||
|
assert.ok(gap(12, 400, 400) < gap(9, 400, 400));
|
||||||
|
assert.ok(gap(9, 400, 400) < gap(6, 400, 400));
|
||||||
|
assert.ok(gap(6, 400, 400) < gap(6, 800, 800), "and a bigger table gives more");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("nothing to measure between fewer than two seats", () => {
|
||||||
|
assert.equal(gap(1, 400, 400), Infinity);
|
||||||
|
assert.equal(gap(9, 0, 0), Infinity);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("the shape of the ring", () => {
|
describe("the shape of the ring", () => {
|
||||||
test("is a mirror image left to right", () => {
|
test("is a mirror image left to right", () => {
|
||||||
const seats = ringPixels(10, 601, 126);
|
const seats = ringPixels(10, 601, 126);
|
||||||
|
|||||||
Reference in New Issue
Block a user