diff --git a/static/scenes/snowdodgeball/index.css b/static/scenes/snowdodgeball/index.css index 94e85aaa2..787e5f463 100644 --- a/static/scenes/snowdodgeball/index.css +++ b/static/scenes/snowdodgeball/index.css @@ -56,9 +56,9 @@ body { .start-screen__title { position: absolute; - top: 80px; + top: clamp(40px, 6vh, 80px); font-family: 'Lobster', sans-serif; - font-size: 80px; + font-size: clamp(40px, 6vw, 80px); font-weight: normal; font-style: normal; color: #2d8659; @@ -75,18 +75,18 @@ body { .instructions-title { font-family: 'Google Sans', sans-serif; - font-size: 42px; + font-size: clamp(28px, 3.2vw, 42px); font-weight: normal; color: #2d8659; - margin-bottom: 30px; + margin-bottom: clamp(15px, 2.2vw, 30px); } .instruction-panels { display: flex; - gap: 20px; + gap: clamp(15px, 1.5vw, 20px); justify-content: center; align-items: flex-start; - margin-bottom: 20px; + margin-bottom: clamp(15px, 1.5vw, 20px); flex-wrap: wrap; max-width: 95vw; } @@ -96,17 +96,17 @@ body { flex-direction: column; align-items: center; background: white; - padding: 20px; - border-radius: 15px; + padding: clamp(12px, 1.5vw, 20px); + border-radius: clamp(10px, 1.2vw, 15px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); flex: 1 1 auto; - min-width: 200px; - max-width: 380px; + min-width: min(200px, 40vw); + max-width: min(380px, 28vw); } .instruction-panel p { font-family: 'Google Sans', sans-serif; - font-size: 18px; + font-size: clamp(14px, 1.3vw, 18px); font-weight: normal; color: #333; margin: 10px 0 0 0; @@ -115,47 +115,21 @@ body { .panel-image { width: 100%; - max-width: 350px; + max-width: min(350px, 26vw); height: auto; - border-radius: 10px; + border-radius: clamp(8px, 0.8vw, 10px); } /* Responsive adjustments */ -@media (max-width: 1400px) { - .instruction-panel { - max-width: 300px; - padding: 15px; - } - - .panel-image { - max-width: 270px; - } -} - -@media (max-width: 1000px) { - .instruction-panel { - max-width: 250px; - padding: 12px; - } - - .panel-image { - max-width: 220px; - } - - .instruction-panels { - gap: 15px; - } -} - @media (max-width: 768px) { .instruction-panels { flex-direction: column; - gap: 15px; + gap: clamp(10px, 2vw, 15px); } .instruction-panel { - max-width: 400px; - width: 90%; + max-width: 85vw; + width: 85vw; } .panel-image { @@ -165,15 +139,15 @@ body { .start-button { position: absolute; - bottom: 100px; - padding: 20px 60px; + bottom: clamp(50px, 8vh, 100px); + padding: clamp(15px, 1.5vw, 20px) clamp(40px, 4.5vw, 60px); font-family: 'Google Sans', sans-serif; - font-size: 32px; + font-size: clamp(24px, 2.4vw, 32px); font-weight: bold; color: white; background: #4a9d5f; border: none; - border-radius: 8px; + border-radius: clamp(6px, 0.6vw, 8px); cursor: pointer; box-shadow: 3px 6px 0 #2d6b3f, 8px 9px 0 rgba(0, 0, 0, 0.16); transform: translate(-3px, -6px); @@ -533,12 +507,11 @@ body { } #arena { - position: relative; + position: absolute; width: 1200px; height: 900px; - max-width: 100%; - max-height: 100%; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); + transform-origin: 0 0; background: radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.8) 1px, transparent 1px), radial-gradient(circle at 60% 70%, rgba(255, 255, 255, 0.6) 1px, transparent 1px), @@ -573,8 +546,8 @@ body { canvas { display: block; - width: 100%; - height: 100%; + width: 1200px; + height: 900px; } /* Elf styling */ diff --git a/static/scenes/snowdodgeball/js/constants.js b/static/scenes/snowdodgeball/js/constants.js index 3429d8c3b..b64ff4268 100644 --- a/static/scenes/snowdodgeball/js/constants.js +++ b/static/scenes/snowdodgeball/js/constants.js @@ -20,7 +20,12 @@ export const Arena = { BORDER_WIDTH: 3, CENTER_LINE_COLOR: 'rgba(0, 0, 0, 0.15)', CENTER_LINE_WIDTH: 2, - OUT_OF_BOUNDS_MARGIN: 50 + OUT_OF_BOUNDS_MARGIN: 50, + // Responsive scaling + PADDING_TOP: 60, + PADDING_TOP_MOBILE: 20, + PADDING_LEFT_PERCENTAGE: 2, + ZOOM_TOUCH_DEVICE: 0.15 }; // ============================================================================= diff --git a/static/scenes/snowdodgeball/js/game.js b/static/scenes/snowdodgeball/js/game.js index 1dbe1ae6d..5ec8c9f2e 100644 --- a/static/scenes/snowdodgeball/js/game.js +++ b/static/scenes/snowdodgeball/js/game.js @@ -9,6 +9,7 @@ import { } from './constants.js'; import { LevelManager } from './level-manager.js'; import { Stats } from './stats.js'; +import isTouchDevice from '../../snowbox/js/utils/isTouchDevice.js'; export class Game { constructor(canvas, api, prepareAnimation) { @@ -58,6 +59,18 @@ export class Game { this.arenaHeight = Arena.HEIGHT; this.arenaX = 0; this.arenaY = 0; + this.ratio = Arena.WIDTH / Arena.HEIGHT; // Aspect ratio 1200/900 = 1.33 + + // Scaling properties + this.scale = 1; + this.paddingTop = Arena.PADDING_TOP; + this.paddingLeft = Arena.PADDING_LEFT_PERCENTAGE; + this.isTouchDevice = isTouchDevice(); + + // Update padding for touch devices + if (this.isTouchDevice) { + this.paddingTop = Arena.PADDING_TOP_MOBILE; + } // Spawn point management this.spawnPoints = []; @@ -74,6 +87,9 @@ export class Game { this.playerGifts = Array.from(document.querySelectorAll('.health-bar--player .gift')); this.opponentGifts = Array.from(document.querySelectorAll('.health-bar--opponent .gift')); + // Get arena DOM element for scaling + this.arenaElement = document.getElementById('arena'); + this.resize(); window.addEventListener('resize', () => this.resize()); @@ -89,7 +105,38 @@ export class Game { resize() { - // Canvas size now matches arena size + // Calculate available space + const maxHeight = window.innerHeight - this.paddingTop * 2; + const maxWidth = window.innerWidth - window.innerWidth * this.paddingLeft / 100; + + // Calculate target dimensions maintaining aspect ratio + const targetedHeight = Math.min( + this.arenaHeight * window.innerWidth / this.arenaWidth, + maxHeight + ); + const targetedWidth = Math.min(targetedHeight * this.ratio, maxWidth); + + // Calculate scale factor + this.scale = targetedWidth / this.arenaWidth; + + // Add extra zoom for touch devices + if (this.isTouchDevice) { + this.scale += Arena.ZOOM_TOUCH_DEVICE; + } + + // Apply CSS transform to scale the entire arena + this.arenaElement.style.position = 'absolute'; + this.arenaElement.style.left = '50%'; + this.arenaElement.style.top = '50%'; + this.arenaElement.style.transformOrigin = '0 0'; + + if (!this.isTouchDevice) { + this.arenaElement.style.transform = `scale(${this.scale.toFixed(3)}) translate(-50%, -50%)`; + } else { + this.arenaElement.style.transform = `scale(${this.scale.toFixed(3)}) translate(-50%, -50%) translateY(${this.paddingTop}px)`; + } + + // Canvas size remains fixed at base dimensions this.canvas.width = this.arenaWidth; this.canvas.height = this.arenaHeight; this.width = this.arenaWidth; @@ -556,8 +603,9 @@ export class Game { const arena = document.getElementById('arena'); const rect = arena.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; + // Adjust click coordinates for difference screen sizes. + const x = (e.clientX - rect.left) / this.scale; + const y = (e.clientY - rect.top) / this.scale; const arenaCenterY = this.arenaHeight / 2; const arenaBounds = { x: 0, y: 0, width: this.arenaWidth, height: this.arenaHeight };