Fix mobile view: next piece display, game board scaling, and visibility issues

This commit is contained in:
cmclark00 2025-03-25 16:49:10 -04:00
parent 6285492580
commit 40700d1636
2 changed files with 112 additions and 21 deletions

117
script.js
View file

@ -381,11 +381,11 @@ class Firework {
// The Piece class // The Piece class
class Piece { class Piece {
constructor(tetromino, color) { constructor(tetromino, tetrominoN, color) {
this.tetromino = tetromino; this.tetromino = tetromino;
this.color = color; this.color = color;
this.tetrominoN = 0; // Default rotation state this.tetrominoN = tetrominoN || 0; // Rotation state
this.activeTetromino = this.tetromino[this.tetrominoN]; this.activeTetromino = this.tetromino[this.tetrominoN];
this.shadowTetromino = this.activeTetromino; // For shadow calculation this.shadowTetromino = this.activeTetromino; // For shadow calculation
@ -1510,8 +1510,11 @@ function drawBoard() {
// Generate random piece // Generate random piece
function randomPiece() { function randomPiece() {
let r = Math.floor(Math.random() * PIECES.length); let randomN = Math.floor(Math.random() * PIECES.length);
return new Piece(PIECES[r], COLORS[r]); let randomTetromino = PIECES[randomN];
let randomIndex = Math.floor(Math.random() * randomTetromino.length);
return new Piece(randomTetromino, randomIndex, COLORS[randomN]);
} }
// Play piece movement sounds // Play piece movement sounds
@ -1620,7 +1623,26 @@ function draw() {
// Drop the piece - called by interval // Drop the piece - called by interval
function dropPiece() { function dropPiece() {
if (gameOver || paused) return; if (gameOver || paused) return;
p.moveDown();
let now = Date.now();
let delta = now - dropStart;
// Drop speed depends on level
let speed = 1000 * (1 - (level - 1) * 0.1);
speed = Math.max(speed, 100); // Don't allow too fast drops (minimum 100ms)
if (delta > speed) {
p.moveDown();
dropStart = now;
// If we can't move down and we're still at the top, game over
if (p.collision(0, 1) && p.y < 1) {
gameOver = true;
showGameOver();
}
}
if (!gameOver) requestAnimationFrame(draw);
} }
// Show game over modal // Show game over modal
@ -1752,9 +1774,11 @@ function init() {
// Draw the board // Draw the board
drawBoard(); drawBoard();
// Generate pieces // Generate initial pieces
p = randomPiece(); p = randomPiece();
nextPiece = randomPiece(); nextPiece = randomPiece();
// Draw initial next piece
nextPiece.drawNextPiece(); nextPiece.drawNextPiece();
// Set up game interval // Set up game interval
@ -1836,13 +1860,13 @@ function handleResize() {
// For portrait: maximize width, maintain aspect ratio // For portrait: maximize width, maintain aspect ratio
if (isPortrait) { if (isPortrait) {
// Use 95% of viewport width // Use 90% of viewport width
const targetWidth = viewportWidth * 0.95; const targetWidth = viewportWidth * 0.9;
const targetHeight = (targetWidth / COLS) * ROWS; const targetHeight = (targetWidth / COLS) * ROWS;
// If height is too tall, scale down // If height is too tall, scale down
if (targetHeight > availableHeight * 0.9) { if (targetHeight > availableHeight * 0.8) {
const scaleFactor = (availableHeight * 0.9) / targetHeight; const scaleFactor = (availableHeight * 0.8) / targetHeight;
canvas.style.width = `${targetWidth * scaleFactor}px`; canvas.style.width = `${targetWidth * scaleFactor}px`;
canvas.style.height = `${targetHeight * scaleFactor}px`; canvas.style.height = `${targetHeight * scaleFactor}px`;
} else { } else {
@ -1852,8 +1876,8 @@ function handleResize() {
} }
// For landscape: maximize height, maintain aspect ratio // For landscape: maximize height, maintain aspect ratio
else { else {
// Use 80% of available height // Use 75% of available height
const targetHeight = availableHeight * 0.8; const targetHeight = availableHeight * 0.75;
const targetWidth = (targetHeight / ROWS) * COLS; const targetWidth = (targetHeight / ROWS) * COLS;
// If width is too wide, scale down // If width is too wide, scale down
@ -1867,11 +1891,12 @@ function handleResize() {
} }
} }
// Scale next piece preview to match // Force a redraw of the nextPiece preview to fix rendering issues
if (nextPieceCanvas) { if (nextPiece) {
const scale = parseInt(canvas.style.width) / (COLS * BLOCK_SIZE); setTimeout(() => {
nextPieceCanvas.style.transform = `scale(${scale})`; nextPieceCtx.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height);
nextPieceCanvas.style.transformOrigin = 'top left'; nextPiece.drawNextPiece();
}, 100);
} }
// Show touch controls mode // Show touch controls mode
@ -2303,6 +2328,64 @@ function createTouchControlButtons() {
}, 5000); }, 5000);
} }
// Set active piece to next piece and create new next piece
function getNextPiece() {
// Current piece becomes the next piece
p = nextPiece;
// Generate a new next piece
nextPiece = randomPiece();
// Draw the next piece in preview
nextPiece.drawNextPiece();
// Force redraw for mobile to ensure it renders
if (isMobile || forceMobileControls) {
setTimeout(() => {
nextPieceCtx.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height);
nextPiece.drawNextPiece();
}, 50);
}
}
// Lock the piece in the board and get the next piece
function lockPiece() {
// Add piece to board
for (let r = 0; r < p.activeTetromino.length; r++) {
for (let c = 0; c < p.activeTetromino[r].length; c++) {
// Skip empty squares
if (!p.activeTetromino[r][c]) {
continue;
}
// Game over when piece is locked at the top
if (p.y + r < 0) {
gameOver = true;
showGameOver();
return;
}
// Lock piece
board[p.y + r][p.x + c] = p.color;
}
}
// Check for completed rows
checkRows();
// Update the score
updateScore();
// Get next piece
getNextPiece();
// Reset drop timer
dropStart = Date.now();
// Play drop sound
playSound(dropSound);
}
// Start the game // Start the game
window.onload = function() { window.onload = function() {
init(); init();

View file

@ -516,15 +516,20 @@ input[type=range]::-moz-range-thumb {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin: 0 auto; margin: 0 auto;
position: relative;
margin-bottom: 20px; /* Add bottom margin to make room for next piece */
} }
.mobile-mode #next-piece-preview { .mobile-mode #next-piece-preview {
position: absolute; position: absolute;
top: -50px; /* Position above the game board */ top: auto;
bottom: -70px; /* Position below the game board */
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
margin: 0; margin: 0;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);
z-index: 10;
border-color: rgba(0, 255, 255, 0.5);
} }
.mobile-mode .score-container { .mobile-mode .score-container {
@ -589,10 +594,12 @@ input[type=range]::-moz-range-thumb {
@media (orientation: portrait) { @media (orientation: portrait) {
.mobile-mode .game-container { .mobile-mode .game-container {
padding-top: 40px; padding-top: 40px;
padding-bottom: 80px; /* Add bottom padding for next piece */
} }
.mobile-mode canvas#tetris { .mobile-mode canvas#tetris {
width: 90vw; width: 90vw;
max-height: 75vh; /* Ensure game board doesn't take too much height */
height: auto; height: auto;
object-fit: contain; object-fit: contain;
} }
@ -623,13 +630,14 @@ input[type=range]::-moz-range-thumb {
.mobile-mode #next-piece-preview { .mobile-mode #next-piece-preview {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: auto;
left: 100%; left: 100%;
margin-left: 10px;
transform: none; transform: none;
margin-left: 10px;
} }
.mobile-mode canvas#tetris { .mobile-mode canvas#tetris {
height: 80vh; height: 75vh; /* Slightly reduced to ensure full visibility */
width: auto; width: auto;
object-fit: contain; object-fit: contain;
} }