Ensure correct card overlap on mobile by using actual rendered dimensions

This commit is contained in:
chris 2026-05-25 01:12:57 -04:00
parent 5c8357f47b
commit 542b131cd8

View File

@ -141,51 +141,47 @@
function renderBoard() {
if (!isActive) return;
const boardRect = gameBoard.getBoundingClientRect();
const layoutRect = pyramidLayout.getBoundingClientRect();
// If the board isn't visible yet or dimensions aren't ready, wait for the next frame
if (boardRect.width === 0 || layoutRect.width === 0) {
// If the board isn't visible yet or dimensions aren't ready, wait
if (layoutRect.width === 0) {
requestAnimationFrame(renderBoard);
return;
}
const rootStyle = getComputedStyle(document.documentElement);
let cardWidth = parseFloat(rootStyle.getPropertyValue('--card-width'));
let horizontalGap = parseFloat(rootStyle.getPropertyValue('--gap'));
// Use the first available card to get the REAL pixel width/height calculated by CSS
const firstCardId = Object.keys(cardElements)[0];
if (!firstCardId) return;
const sampleCard = cardElements[firstCardId];
const cardRect = sampleCard.getBoundingClientRect();
const cardWidth = cardRect.width;
const cardHeight = cardRect.height;
if (isNaN(cardWidth) || cardWidth === 0) cardWidth = 80;
if (isNaN(horizontalGap)) horizontalGap = 10;
const cardHeight = cardWidth * 1.4;
const rowOverlap = 0.45;
const boardTop = boardRect.top;
const boardLeft = boardRect.left;
// Calculate gap based on 1.5vw or fallback (matching style.css)
let horizontalGap = (window.innerWidth * 1.5) / 100;
if (window.innerWidth > 600) horizontalGap = (window.innerWidth * 1.2) / 100; // Desktop gap
const verticalStep = cardHeight * 0.45; // Row overlap ratio
// Position Pyramid Cards
let index = 0;
for (let row = 0; row < 7; row++) {
// Calculate total width of this row to center it
const numCards = row + 1;
const totalRowWidth = numCards * cardWidth + (numCards - 1) * horizontalGap;
const startX = (layoutRect.width - totalRowWidth) / 2;
const y = row * (cardHeight * rowOverlap);
const y = row * verticalStep;
for (let i = 0; i <= row; i++) {
const card = pyramid[index];
if (card) {
const el = cardElements[card.id];
// Absolute position relative to the gameBoard
const finalTop = y + (layoutRect.top - boardTop);
const finalLeft = startX + i * (cardWidth + horizontalGap) + (layoutRect.left - boardLeft);
el.style.top = `${finalTop}px`;
el.style.left = `${finalLeft}px`;
// Position relative to the pyramid-layout container
// Note: pyramid-layout has position: relative in pyramid.css
el.style.top = `${y}px`;
el.style.left = `${startX + i * (cardWidth + horizontalGap)}px`;
el.style.zIndex = 100 + index;
el.classList.add('is-flipped');
// Mark if it's currently playable for visual feedback
const exposed = isExposed(card.id);
el.classList.toggle('is-exposed', exposed);
}
@ -193,41 +189,38 @@
}
}
// Position Stock
// Position Stock, Waste, Discard (these are in a separate container at the bottom)
// We'll use a slightly different approach for these to ensure they stay in their piles
const sRect = stockEl.getBoundingClientRect();
const wRect = wasteEl.getBoundingClientRect();
const dRect = discardEl.getBoundingClientRect();
const bRect = gameBoard.getBoundingClientRect();
stock.forEach((card, i) => {
const el = cardElements[card.id];
el.style.top = `${sRect.top - boardTop + PILE_BORDER_WIDTH}px`;
el.style.left = `${sRect.left - boardLeft + PILE_BORDER_WIDTH}px`;
el.style.top = `${sRect.top - bRect.top + PILE_BORDER_WIDTH}px`;
el.style.left = `${sRect.left - bRect.left + PILE_BORDER_WIDTH}px`;
el.style.zIndex = 10 + i;
el.classList.remove('is-flipped');
el.classList.remove('is-selected');
el.classList.remove('is-exposed');
el.classList.remove('is-flipped', 'is-selected', 'is-exposed');
});
// Position Waste
const wRect = wasteEl.getBoundingClientRect();
waste.forEach((card, i) => {
const el = cardElements[card.id];
const isTop = (i === waste.length - 1);
el.style.top = `${wRect.top - boardTop + PILE_BORDER_WIDTH}px`;
el.style.left = `${wRect.left - boardLeft + PILE_BORDER_WIDTH}px`;
// Base z-index for waste is 500, +i to stack correctly
el.style.zIndex = 500 + i;
el.style.top = `${wRect.top - bRect.top + PILE_BORDER_WIDTH}px`;
el.style.left = `${wRect.left - bRect.left + PILE_BORDER_WIDTH}px`;
el.style.zIndex = 500 + i;
el.classList.add('is-flipped');
el.classList.toggle('is-exposed', isTop);
});
// Position Discard
const dRect = discardEl.getBoundingClientRect();
discard.forEach((card, i) => {
const el = cardElements[card.id];
el.style.top = `${dRect.top - boardTop + PILE_BORDER_WIDTH}px`;
el.style.left = `${dRect.left - boardLeft + PILE_BORDER_WIDTH}px`;
el.style.top = `${dRect.top - bRect.top + PILE_BORDER_WIDTH}px`;
el.style.left = `${dRect.left - bRect.left + PILE_BORDER_WIDTH}px`;
el.style.zIndex = 10 + i;
el.classList.add('is-flipped');
el.classList.remove('is-selected');
el.classList.remove('is-exposed');
el.classList.remove('is-selected', 'is-exposed');
});
checkWin();