All files / utils mobileLayoutHelpers.ts

91.66% Statements 22/24
92.85% Branches 13/14
100% Functions 1/1
91.66% Lines 22/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108                                                                                                        64x 64x 64x                   64x 5x 59x 37x 22x 2x 20x 6x     14x       64x     64x 64x 64x     64x           64x 64x     64x 64x   64x                
/**
 * Mobile Layout Helpers
 * 
 * Shared utilities for calculating mobile area bounds with consistent aspect ratios
 * and device-specific sizing. Used by both combat and training layout hooks.
 * 
 * @module utils/mobileLayoutHelpers
 * @category Layout
 * @korean 모바일레이아웃도우미
 */
 
/**
 * Calculate mobile area bounds with 4:3 aspect ratio
 * 
 * Implements consistent mobile area sizing logic shared between combat and training screens.
 * Adapts to different device resolutions while maintaining a 4:3 aspect ratio.
 * Enhanced with extra-small device support (<380px) for low-end mobile devices.
 * 
 * @param width - Screen width in pixels
 * @param height - Screen height in pixels
 * @param topClearance - Minimum space to reserve at top (for HUD/header)
 * @param bottomClearance - Minimum space to reserve at bottom (for controls)
 * @param yOffset - Y position offset (typically header height + padding)
 * @returns Mobile area bounds with position, dimensions, and scale factor
 * 
 * @example
 * ```typescript
 * const bounds = calculateMobileAreaBounds(375, 667, 80, 120, 100);
 * // Returns: { x: ~37, y: 100, width: 300, height: 225, scale: 0.3125 }
 * 
 * const boundsSmall = calculateMobileAreaBounds(320, 568, 75, 110, 90);
 * // Returns: { x: ~25, y: 90, width: 270, height: 202, scale: 0.28125 }
 * ```
 * 
 * @public
 * @korean 모바일영역경계계산
 */
export function calculateMobileAreaBounds(
  width: number,
  height: number,
  topClearance: number,
  bottomClearance: number,
  yOffset: number
): {
  readonly x: number;
  readonly y: number;
  readonly width: number;
  readonly height: number;
  readonly scale: number;
} {
  // Calculate available space for the area
  // Extra-small devices (<380px) use tighter margins for more screen real estate
  const horizontalMargin = width < 380 ? 30 : 40; // 15px vs 20px per side
  const availableHeight = height - topClearance - bottomClearance;
  const availableWidth = width - horizontalMargin;
 
  // Determine max width based on device resolution
  // Device-specific sizing with extra-small support:
  // - 4K/QHD+ (≥1440px): up to 800px
  // - 2K (1200-1439px): up to 600px
  // - Large phones (768-1199px): up to 500px
  // - Standard phones (380-767px): up to 400px
  // - Extra-small phones (<380px): up to 320px
  let maxMobileWidth: number;
  if (width >= 1440) {
    maxMobileWidth = Math.min(availableWidth, 800);
  } else if (width >= 1200) {
    maxMobileWidth = Math.min(availableWidth, 600);
  } else if (width >= 768) {
    maxMobileWidth = Math.min(availableWidth, 500);
  } else if (width >= 380) {
    maxMobileWidth = Math.min(availableWidth, 400);
  } else {
    // Extra-small devices (iPhone SE, old Android, budget phones)
    maxMobileWidth = Math.min(availableWidth, 320);
  }
  
  // Extra-small devices also get reduced max height for better fit
  const maxMobileHeight = Math.min(availableHeight, width < 380 ? 240 : 800);
 
  // Maintain 4:3 aspect ratio (width:height = 4:3)
  const aspectRatio = 4 / 3;
  let areaWidth = maxMobileWidth;
  let areaHeight = areaWidth / aspectRatio;
 
  // If height exceeds available, recalculate based on height constraint
  Iif (areaHeight > maxMobileHeight) {
    areaHeight = maxMobileHeight;
    areaWidth = areaHeight * aspectRatio;
  }
 
  // Ensure minimum size for usability
  areaWidth = Math.min(Math.max(areaWidth, 300), availableWidth);
  areaHeight = Math.min(Math.max(areaHeight, 225), maxMobileHeight);
 
  // Calculate 3D scale factor (mobile area is smaller than desktop reference)
  const desktopWidth = 960; // 80% of 1200px reference
  const scale = areaWidth / desktopWidth;
 
  return {
    x: (width - areaWidth) / 2, // Centered horizontally
    y: yOffset,
    width: areaWidth,
    height: areaHeight,
    scale,
  };
}