Implement dynamic poster layout calculation across multiple components for improved responsiveness

This update introduces a new function to calculate poster layout dynamically based on screen width in several components, including CatalogSection, ContentItem, ContinueWatchingSection, MoreLikeThisSection, CatalogScreen, HomeScreen, and homeStyles. The function ensures that poster widths and the number of columns adapt to different screen sizes, enhancing the overall user experience and visual consistency across the application.
This commit is contained in:
tapframe 2025-06-18 18:25:19 +05:30
parent 7ae46313a5
commit d5cfe8a563
8 changed files with 265 additions and 8 deletions

View file

@ -14,7 +14,32 @@ interface CatalogSectionProps {
}
const { width } = Dimensions.get('window');
const POSTER_WIDTH = (width - 50) / 3;
// Dynamic poster calculation based on screen width
const calculatePosterLayout = (screenWidth: number) => {
const MIN_POSTER_WIDTH = 110; // Minimum poster width for readability
const MAX_POSTER_WIDTH = 140; // Maximum poster width to prevent oversized posters
const HORIZONTAL_PADDING = 50; // Total horizontal padding/margins
// Calculate how many posters can fit
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / MIN_POSTER_WIDTH);
// Limit to reasonable number of columns (3-6)
const numColumns = Math.min(Math.max(maxColumns, 3), 6);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, MAX_POSTER_WIDTH);
return {
numColumns,
posterWidth,
spacing: 12 // Space between posters
};
};
const posterLayout = calculatePosterLayout(width);
const POSTER_WIDTH = posterLayout.posterWidth;
const CatalogSection = ({ catalog }: CatalogSectionProps) => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();

View file

@ -12,7 +12,32 @@ interface ContentItemProps {
}
const { width } = Dimensions.get('window');
const POSTER_WIDTH = (width - 50) / 3;
// Dynamic poster calculation based on screen width
const calculatePosterLayout = (screenWidth: number) => {
const MIN_POSTER_WIDTH = 110; // Minimum poster width for readability
const MAX_POSTER_WIDTH = 140; // Maximum poster width to prevent oversized posters
const HORIZONTAL_PADDING = 50; // Total horizontal padding/margins
// Calculate how many posters can fit
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / MIN_POSTER_WIDTH);
// Limit to reasonable number of columns (3-6)
const numColumns = Math.min(Math.max(maxColumns, 3), 6);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, MAX_POSTER_WIDTH);
return {
numColumns,
posterWidth,
spacing: 12 // Space between posters
};
};
const posterLayout = calculatePosterLayout(width);
const POSTER_WIDTH = posterLayout.posterWidth;
const ContentItem = ({ item: initialItem, onPress }: ContentItemProps) => {
const [menuVisible, setMenuVisible] = useState(false);

View file

@ -33,8 +33,32 @@ interface ContinueWatchingRef {
refresh: () => Promise<boolean>;
}
// Dynamic poster calculation based on screen width for Continue Watching section
const calculatePosterLayout = (screenWidth: number) => {
const MIN_POSTER_WIDTH = 120; // Slightly larger for continue watching items
const MAX_POSTER_WIDTH = 160; // Maximum poster width for this section
const HORIZONTAL_PADDING = 40; // Total horizontal padding/margins
// Calculate how many posters can fit (fewer items for continue watching)
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / MIN_POSTER_WIDTH);
// Limit to reasonable number of columns (2-5 for continue watching)
const numColumns = Math.min(Math.max(maxColumns, 2), 5);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, MAX_POSTER_WIDTH);
return {
numColumns,
posterWidth,
spacing: 12 // Space between posters
};
};
const { width } = Dimensions.get('window');
const POSTER_WIDTH = (width - 40) / 2.7;
const posterLayout = calculatePosterLayout(width);
const POSTER_WIDTH = posterLayout.posterWidth;
// Create a proper imperative handle with React.forwardRef and updated type
const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, ref) => {

View file

@ -19,7 +19,32 @@ import { TMDBService } from '../../services/tmdbService';
import { catalogService } from '../../services/catalogService';
const { width } = Dimensions.get('window');
const POSTER_WIDTH = (width - 48) / 3.5; // Adjust number for desired items visible
// Dynamic poster calculation based on screen width for More Like This section
const calculatePosterLayout = (screenWidth: number) => {
const MIN_POSTER_WIDTH = 100; // Slightly smaller for more items in this section
const MAX_POSTER_WIDTH = 130; // Maximum poster width
const HORIZONTAL_PADDING = 48; // Total horizontal padding/margins
// Calculate how many posters can fit (aim for slightly more items than main sections)
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / MIN_POSTER_WIDTH);
// Limit to reasonable number of columns (3-7 for this section)
const numColumns = Math.min(Math.max(maxColumns, 3), 7);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, MAX_POSTER_WIDTH);
return {
numColumns,
posterWidth,
spacing: 12 // Space between posters
};
};
const posterLayout = calculatePosterLayout(width);
const POSTER_WIDTH = posterLayout.posterWidth;
const POSTER_HEIGHT = POSTER_WIDTH * 1.5;
interface MoreLikeThisSectionProps {

View file

@ -41,9 +41,35 @@ const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
// Screen dimensions and grid layout
const { width } = Dimensions.get('window');
const NUM_COLUMNS = 3;
// Dynamic column calculation based on screen width
const calculateCatalogLayout = (screenWidth: number) => {
const MIN_ITEM_WIDTH = 110; // Minimum item width for readability
const MAX_ITEM_WIDTH = 150; // Maximum item width
const HORIZONTAL_PADDING = SPACING.lg * 2; // Total horizontal padding
const ITEM_MARGIN_TOTAL = SPACING.sm * 2; // Total margin per item
// Calculate how many columns can fit
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / (MIN_ITEM_WIDTH + ITEM_MARGIN_TOTAL));
// Limit to reasonable number of columns (2-6)
const numColumns = Math.min(Math.max(maxColumns, 2), 6);
// Calculate actual item width
const totalMargins = ITEM_MARGIN_TOTAL * numColumns;
const itemWidth = Math.min((availableWidth - totalMargins) / numColumns, MAX_ITEM_WIDTH);
return {
numColumns,
itemWidth
};
};
const catalogLayout = calculateCatalogLayout(width);
const NUM_COLUMNS = catalogLayout.numColumns;
const ITEM_MARGIN = SPACING.sm;
const ITEM_WIDTH = (width - (SPACING.lg * 2) - (ITEM_MARGIN * 2 * NUM_COLUMNS)) / NUM_COLUMNS;
const ITEM_WIDTH = catalogLayout.itemWidth;
// Create a styles creator function that accepts the theme colors
const createStyles = (colors: any) => StyleSheet.create({

View file

@ -679,7 +679,32 @@ const HomeScreen = () => {
};
const { width, height } = Dimensions.get('window');
const POSTER_WIDTH = (width - 50) / 3;
// Dynamic poster calculation based on screen width
const calculatePosterLayout = (screenWidth: number) => {
const MIN_POSTER_WIDTH = 110; // Minimum poster width for readability
const MAX_POSTER_WIDTH = 140; // Maximum poster width to prevent oversized posters
const HORIZONTAL_PADDING = 50; // Total horizontal padding/margins
// Calculate how many posters can fit
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / MIN_POSTER_WIDTH);
// Limit to reasonable number of columns (3-6)
const numColumns = Math.min(Math.max(maxColumns, 3), 6);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, MAX_POSTER_WIDTH);
return {
numColumns,
posterWidth,
spacing: 12 // Space between posters
};
};
const posterLayout = calculatePosterLayout(width);
const POSTER_WIDTH = posterLayout.posterWidth;
const styles = StyleSheet.create<any>({
container: {

View file

@ -1,7 +1,32 @@
import { StyleSheet, Dimensions, Platform } from 'react-native';
const { width, height } = Dimensions.get('window');
export const POSTER_WIDTH = (width - 50) / 3;
// Dynamic poster calculation based on screen width
const calculatePosterLayout = (screenWidth: number) => {
const MIN_POSTER_WIDTH = 110; // Minimum poster width for readability
const MAX_POSTER_WIDTH = 140; // Maximum poster width to prevent oversized posters
const HORIZONTAL_PADDING = 50; // Total horizontal padding/margins
// Calculate how many posters can fit
const availableWidth = screenWidth - HORIZONTAL_PADDING;
const maxColumns = Math.floor(availableWidth / MIN_POSTER_WIDTH);
// Limit to reasonable number of columns (3-6)
const numColumns = Math.min(Math.max(maxColumns, 3), 6);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, MAX_POSTER_WIDTH);
return {
numColumns,
posterWidth,
spacing: 12 // Space between posters
};
};
const posterLayout = calculatePosterLayout(width);
export const POSTER_WIDTH = posterLayout.posterWidth;
export const POSTER_HEIGHT = POSTER_WIDTH * 1.5;
export const HORIZONTAL_PADDING = 16;

82
src/utils/posterUtils.ts Normal file
View file

@ -0,0 +1,82 @@
import { Dimensions } from 'react-native';
export interface PosterLayoutConfig {
minPosterWidth: number;
maxPosterWidth: number;
horizontalPadding: number;
minColumns: number;
maxColumns: number;
spacing: number;
}
export interface PosterLayout {
numColumns: number;
posterWidth: number;
spacing: number;
}
// Default configuration for main home sections
export const DEFAULT_POSTER_CONFIG: PosterLayoutConfig = {
minPosterWidth: 110,
maxPosterWidth: 140,
horizontalPadding: 50,
minColumns: 3,
maxColumns: 6,
spacing: 12
};
// Configuration for More Like This section (smaller posters, more items)
export const MORE_LIKE_THIS_CONFIG: PosterLayoutConfig = {
minPosterWidth: 100,
maxPosterWidth: 130,
horizontalPadding: 48,
minColumns: 3,
maxColumns: 7,
spacing: 12
};
// Configuration for Continue Watching section (larger posters, fewer items)
export const CONTINUE_WATCHING_CONFIG: PosterLayoutConfig = {
minPosterWidth: 120,
maxPosterWidth: 160,
horizontalPadding: 40,
minColumns: 2,
maxColumns: 5,
spacing: 12
};
export const calculatePosterLayout = (
screenWidth: number,
config: PosterLayoutConfig = DEFAULT_POSTER_CONFIG
): PosterLayout => {
const {
minPosterWidth,
maxPosterWidth,
horizontalPadding,
minColumns,
maxColumns,
spacing
} = config;
// Calculate how many posters can fit
const availableWidth = screenWidth - horizontalPadding;
const maxColumnsBasedOnWidth = Math.floor(availableWidth / minPosterWidth);
// Limit to reasonable number of columns
const numColumns = Math.min(Math.max(maxColumnsBasedOnWidth, minColumns), maxColumns);
// Calculate actual poster width
const posterWidth = Math.min(availableWidth / numColumns, maxPosterWidth);
return {
numColumns,
posterWidth,
spacing
};
};
// Helper function to get current screen dimensions
export const getCurrentPosterLayout = (config?: PosterLayoutConfig): PosterLayout => {
const { width } = Dimensions.get('window');
return calculatePosterLayout(width, config);
};