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