mirror of
https://github.com/JorySeverijnse/ui-fixer-supreme.git
synced 2025-12-06 13:36:57 +00:00
Improve mobile UI fixes
Addressed mobile-specific issues: - Stabilized Matrix Rain startup on phones - Adjusted mobile game layout and controls to render correctly - Auto-collapse mobile sidebar when navigating between routes X-Lovable-Edit-ID: edt-f21d53ab-1980-44e2-b1ea-ac60805a8999
This commit is contained in:
commit
6ff0bbb8ef
@ -29,8 +29,13 @@ const MatrixRain = ({ color = '#00FF00' }: MatrixRainProps) => {
|
||||
const alphabet = katakana + latin + nums;
|
||||
|
||||
const init = () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
// On mobile, use visualViewport for accurate dimensions
|
||||
const isMobile = window.innerWidth < 768;
|
||||
const width = isMobile && window.visualViewport ? window.visualViewport.width : window.innerWidth;
|
||||
const height = isMobile && window.visualViewport ? window.visualViewport.height : window.innerHeight;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
columns = Math.floor(canvas.width / fontSize);
|
||||
rainDrops = [];
|
||||
for (let x = 0; x < columns; x++) {
|
||||
@ -38,7 +43,13 @@ const MatrixRain = ({ color = '#00FF00' }: MatrixRainProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Delay init slightly on mobile to ensure viewport is ready
|
||||
const isMobile = window.innerWidth < 768;
|
||||
if (isMobile) {
|
||||
setTimeout(init, 100);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
const draw = () => {
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.04)';
|
||||
@ -62,6 +73,7 @@ const MatrixRain = ({ color = '#00FF00' }: MatrixRainProps) => {
|
||||
|
||||
const handleResize = () => init();
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('orientationchange', () => setTimeout(init, 100));
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { cn } from '@/lib/utils';
|
||||
@ -22,6 +22,11 @@ const Sidebar = () => {
|
||||
const { playSound } = useSettings();
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
// Auto-collapse sidebar on mobile when route changes
|
||||
useEffect(() => {
|
||||
setIsExpanded(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
const currentPage = navItems.find(item => item.path === location.pathname)?.label || 'Menu';
|
||||
|
||||
const toggleMenu = () => {
|
||||
|
||||
@ -50,8 +50,8 @@ const Breakout = () => {
|
||||
if (typeof window === 'undefined') return { width: 480, height: 580 };
|
||||
const isMobile = window.innerWidth < 768;
|
||||
if (isMobile) {
|
||||
const maxWidth = window.innerWidth - 32;
|
||||
const maxHeight = window.innerHeight - 280;
|
||||
const maxWidth = window.innerWidth - 24;
|
||||
const maxHeight = window.innerHeight - 220;
|
||||
const aspectRatio = 480 / 580;
|
||||
let width = maxWidth;
|
||||
let height = width / aspectRatio;
|
||||
@ -269,7 +269,7 @@ const Breakout = () => {
|
||||
)}
|
||||
|
||||
{isMobile && (
|
||||
<div className="mt-4 flex flex-col items-center gap-2 w-full">
|
||||
<div className="mt-2 flex flex-col items-center gap-1 w-full flex-shrink-0">
|
||||
<div className="flex gap-4 text-center">
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-primary">{score.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">HIGH</p><p className="font-minecraft text-sm text-primary">{highScore.toLocaleString()}</p></div>
|
||||
@ -277,10 +277,10 @@ const Breakout = () => {
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">♥</p><p className="font-minecraft text-sm text-primary">{lives}</p></div>
|
||||
</div>
|
||||
{gameStarted && !gameOver && (
|
||||
<div className="flex gap-4 mt-2">
|
||||
<GameTouchButton onAction={moveLeft} className="p-4 px-8 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg" interval={30}>←</GameTouchButton>
|
||||
<button onClick={() => setIsPaused(p => !p)} className="p-4 px-6 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={moveRight} className="p-4 px-8 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg" interval={30}>→</GameTouchButton>
|
||||
<div className="flex gap-4 mt-1">
|
||||
<GameTouchButton onAction={moveLeft} className="p-3 px-6 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg" interval={30}>←</GameTouchButton>
|
||||
<button onClick={() => setIsPaused(p => !p)} className="p-3 px-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={moveRight} className="p-3 px-6 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg" interval={30}>→</GameTouchButton>
|
||||
</div>
|
||||
)}
|
||||
{(!gameStarted || gameOver) && (
|
||||
|
||||
@ -73,9 +73,9 @@ const Pacman = () => {
|
||||
if (typeof window === 'undefined') return 20;
|
||||
const isMobile = window.innerWidth < 768;
|
||||
if (isMobile) {
|
||||
const maxWidth = window.innerWidth - 40;
|
||||
const maxHeight = window.innerHeight - 300;
|
||||
return Math.min(Math.floor(maxWidth / GRID_WIDTH), Math.floor(maxHeight / GRID_HEIGHT), 16);
|
||||
const maxWidth = window.innerWidth - 32;
|
||||
const maxHeight = window.innerHeight - 260;
|
||||
return Math.min(Math.floor(maxWidth / GRID_WIDTH), Math.floor(maxHeight / GRID_HEIGHT), 14);
|
||||
}
|
||||
return isFullscreen ? 26 : 20;
|
||||
}, [isFullscreen]);
|
||||
@ -342,22 +342,22 @@ const Pacman = () => {
|
||||
)}
|
||||
|
||||
{isMobile && (
|
||||
<div className="mt-4 flex flex-col items-center gap-2 w-full">
|
||||
<div className="mt-2 flex flex-col items-center gap-1 w-full flex-shrink-0">
|
||||
<div className="flex gap-4 text-center">
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-primary">{score.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">HIGH</p><p className="font-minecraft text-sm text-primary">{highScore.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">LVL</p><p className="font-minecraft text-sm text-primary">{level}</p></div>
|
||||
</div>
|
||||
{gameStarted && !gameOver && !gameComplete && (
|
||||
<div className="grid grid-cols-3 gap-1 mt-2">
|
||||
<div className="grid grid-cols-3 gap-1 mt-1">
|
||||
<div />
|
||||
<GameTouchButton onAction={() => setNextDirection('up')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↑</GameTouchButton>
|
||||
<GameTouchButton onAction={() => setNextDirection('up')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↑</GameTouchButton>
|
||||
<div />
|
||||
<GameTouchButton onAction={() => setNextDirection('left')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">←</GameTouchButton>
|
||||
<button onClick={() => setIsPaused(p => !p)} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={() => setNextDirection('right')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">→</GameTouchButton>
|
||||
<GameTouchButton onAction={() => setNextDirection('left')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">←</GameTouchButton>
|
||||
<button onClick={() => setIsPaused(p => !p)} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={() => setNextDirection('right')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">→</GameTouchButton>
|
||||
<div />
|
||||
<GameTouchButton onAction={() => setNextDirection('down')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↓</GameTouchButton>
|
||||
<GameTouchButton onAction={() => setNextDirection('down')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↓</GameTouchButton>
|
||||
<div />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -39,9 +39,9 @@ const Snake = () => {
|
||||
if (typeof window === 'undefined') return 24;
|
||||
const isMobile = window.innerWidth < 768;
|
||||
if (isMobile) {
|
||||
const maxWidth = window.innerWidth - 40;
|
||||
const maxHeight = window.innerHeight - 300;
|
||||
return Math.min(Math.floor(maxWidth / GRID_SIZE), Math.floor(maxHeight / GRID_SIZE), 18);
|
||||
const maxWidth = window.innerWidth - 32;
|
||||
const maxHeight = window.innerHeight - 260;
|
||||
return Math.min(Math.floor(maxWidth / GRID_SIZE), Math.floor(maxHeight / GRID_SIZE), 16);
|
||||
}
|
||||
return isFullscreen ? 28 : 24;
|
||||
}, [isFullscreen]);
|
||||
@ -303,22 +303,22 @@ const Snake = () => {
|
||||
|
||||
{/* Mobile Controls */}
|
||||
{isMobile && (
|
||||
<div className="mt-4 flex flex-col items-center gap-2 w-full">
|
||||
<div className="mt-2 flex flex-col items-center gap-1 w-full flex-shrink-0">
|
||||
<div className="flex gap-4 text-center">
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-primary">{score.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">HIGH</p><p className="font-minecraft text-sm text-primary">{highScore.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">LEN</p><p className="font-minecraft text-sm text-primary">{snake.length}</p></div>
|
||||
</div>
|
||||
{gameStarted && !gameOver && !gameComplete && (
|
||||
<div className="grid grid-cols-3 gap-1 mt-2">
|
||||
<div className="grid grid-cols-3 gap-1 mt-1">
|
||||
<div />
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('up')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↑</GameTouchButton>
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('up')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↑</GameTouchButton>
|
||||
<div />
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('left')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">←</GameTouchButton>
|
||||
<button onClick={() => setIsPaused(p => !p)} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('right')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">→</GameTouchButton>
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('left')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">←</GameTouchButton>
|
||||
<button onClick={() => setIsPaused(p => !p)} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('right')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">→</GameTouchButton>
|
||||
<div />
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('down')} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↓</GameTouchButton>
|
||||
<GameTouchButton onAction={() => directionQueueRef.current.push('down')} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↓</GameTouchButton>
|
||||
<div />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -76,9 +76,10 @@ const Tetris = () => {
|
||||
if (typeof window === 'undefined') return 24;
|
||||
const isMobile = window.innerWidth < 768;
|
||||
if (isMobile) {
|
||||
const maxWidth = window.innerWidth - 40;
|
||||
const maxHeight = window.innerHeight - 320;
|
||||
return Math.min(Math.floor(maxWidth / BOARD_WIDTH), Math.floor(maxHeight / BOARD_HEIGHT), 22);
|
||||
const maxWidth = window.innerWidth - 32;
|
||||
// Reserve more space for controls on mobile
|
||||
const maxHeight = window.innerHeight - 280;
|
||||
return Math.min(Math.floor(maxWidth / BOARD_WIDTH), Math.floor(maxHeight / BOARD_HEIGHT), 18);
|
||||
}
|
||||
return isFullscreen ? 30 : 24;
|
||||
}, [isFullscreen]);
|
||||
@ -316,22 +317,22 @@ const Tetris = () => {
|
||||
</div>
|
||||
)}
|
||||
{isMobile && (
|
||||
<div className="mt-4 flex flex-col items-center gap-2 w-full">
|
||||
<div className="mt-2 flex flex-col items-center gap-1 w-full flex-shrink-0">
|
||||
<div className="flex gap-4 text-center">
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-primary">{score.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">HIGH</p><p className="font-minecraft text-sm text-primary">{highScore.toLocaleString()}</p></div>
|
||||
<div><p className="font-pixel text-[8px] text-foreground/60">LINES</p><p className="font-minecraft text-sm text-primary">{lines}</p></div>
|
||||
</div>
|
||||
{gameStarted && !gameOver && !gameComplete && (
|
||||
<div className="grid grid-cols-3 gap-1 mt-2">
|
||||
<div className="grid grid-cols-3 gap-1 mt-1">
|
||||
<div />
|
||||
<GameTouchButton onAction={rotatePiece} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg" interval={200}>↑</GameTouchButton>
|
||||
<GameTouchButton onAction={rotatePiece} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg" interval={200}>↑</GameTouchButton>
|
||||
<div />
|
||||
<GameTouchButton onAction={moveLeft} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">←</GameTouchButton>
|
||||
<GameTouchButton onAction={hardDrop} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px]" interval={500}>DROP</GameTouchButton>
|
||||
<GameTouchButton onAction={moveRight} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">→</GameTouchButton>
|
||||
<button onClick={togglePause} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={moveDown} className="p-4 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↓</GameTouchButton>
|
||||
<GameTouchButton onAction={moveLeft} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">←</GameTouchButton>
|
||||
<GameTouchButton onAction={hardDrop} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px]" interval={500}>DROP</GameTouchButton>
|
||||
<GameTouchButton onAction={moveRight} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">→</GameTouchButton>
|
||||
<button onClick={togglePause} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-[10px] select-none">{isPaused ? '▶' : '❚❚'}</button>
|
||||
<GameTouchButton onAction={moveDown} className="p-3 border border-primary/50 active:bg-primary/40 text-primary font-pixel text-lg">↓</GameTouchButton>
|
||||
<div />
|
||||
</div>
|
||||
)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user