mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
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:
parent
7ae46313a5
commit
d5cfe8a563
8 changed files with 265 additions and 8 deletions
|
|
@ -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>>();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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
82
src/utils/posterUtils.ts
Normal 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);
|
||||
};
|
||||
Loading…
Reference in a new issue