This commit is contained in:
gpt-engineer-app[bot]
2026-02-02 08:49:30 +00:00
parent f4b2916693
commit 147b8cb43f
6 changed files with 147 additions and 14 deletions
+22 -3
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { createPortal } from 'react-dom';
import { motion } from 'framer-motion';
import { useSettings } from '@/contexts/SettingsContext';
import { useAchievements } from '@/contexts/AchievementsContext';
@@ -8,6 +9,7 @@ import { Maximize2, Minimize2 } from 'lucide-react';
import { useBrowserFullscreen } from '@/hooks/useGameDimensions';
import GameTouchButton from '@/components/GameTouchButton';
import { MobileControlsDock, MobileControlsSpacer } from '@/components/MobileControlsDock';
import MatrixCursor from '@/components/MatrixCursor';
const PADDLE_HEIGHT = 14;
const BRICK_ROWS = 6;
@@ -132,6 +134,15 @@ const Breakout = () => {
if (gameStarted && isMobile && !isFullscreen) { setIsFullscreen(true); enterFullscreen(); }
}, [gameStarted, isMobile, isFullscreen, enterFullscreen]);
// Prevent page scroll while in fullscreen mode (portal overlays the viewport)
useEffect(() => {
if (typeof document === 'undefined') return;
if (!isFullscreen) return;
const prevOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => { document.body.style.overflow = prevOverflow; };
}, [isFullscreen]);
const toggleFullscreen = async () => {
if (!isFullscreen) { setIsFullscreen(true); await enterFullscreen(); }
else { setIsFullscreen(false); await exitFullscreen(); }
@@ -183,7 +194,8 @@ const Breakout = () => {
}, []);
const resetBall = useCallback((ballRefToReset: React.MutableRefObject<{x: number, y: number, dx: number, dy: number}>, paddleRefToReset: React.MutableRefObject<number>, lvl: number) => {
const speed = 2.5 + lvl * 0.3;
// Slower base speed for classic Breakout feel (was 2.5)
const speed = 1.8 + lvl * 0.2;
ballRefToReset.current = { x: canvasSize.width / 2, y: canvasSize.height - 60, dx: (Math.random() > 0.5 ? 1 : -1) * speed, dy: -speed };
paddleRefToReset.current = canvasSize.width / 2 - paddleWidth / 2;
}, [canvasSize.width, canvasSize.height, paddleWidth]);
@@ -538,9 +550,11 @@ const Breakout = () => {
if (showGlitchCrash) return <GlitchCrash onComplete={() => window.location.reload()} />;
return (
const gameUi = (
<motion.div ref={gameRef} tabIndex={0} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }}
className={`flex flex-col outline-none ${isFullscreen ? 'fixed inset-0 z-50 bg-background p-4' : 'h-full'}`}>
className={`flex flex-col outline-none ${isFullscreen ? 'fixed inset-0 z-[100] bg-background p-4 w-screen h-screen' : 'h-full'}`}>
{/* Render cursor inside portal for fullscreen */}
{isFullscreen && <MatrixCursor />}
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-4">
<Link to="/games" className="font-pixel text-sm text-foreground/50 hover:text-primary transition-colors">{'<'} Back</Link>
@@ -712,6 +726,11 @@ const Breakout = () => {
)}
</motion.div>
);
// Portal to body for true fullscreen, escaping MainLayout transforms
return isFullscreen && typeof document !== 'undefined'
? createPortal(gameUi, document.body)
: gameUi;
};
export default Breakout;