homescreen entrance animation improvement

This commit is contained in:
tapframe 2025-08-13 15:11:36 +05:30
parent ebf9a71430
commit 71a9042dc4
5 changed files with 36 additions and 18 deletions

View file

@ -7,6 +7,7 @@ import { LinearGradient } from 'expo-linear-gradient';
import { CatalogContent, StreamingContent } from '../../services/catalogService';
import { useTheme } from '../../contexts/ThemeContext';
import ContentItem from './ContentItem';
import Animated, { FadeIn } from 'react-native-reanimated';
import { RootStackParamList } from '../../navigation/AppNavigator';
interface CatalogSectionProps {
@ -76,7 +77,7 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
const keyExtractor = useCallback((item: StreamingContent) => `${item.id}-${item.type}`, []);
return (
<View style={styles.catalogContainer}>
<Animated.View style={styles.catalogContainer} entering={FadeIn.duration(350)}>
<View style={styles.catalogHeader}>
<View style={styles.titleContainer}>
<Text style={[styles.catalogTitle, { color: currentTheme.colors.text }]} numberOfLines={1}>{catalog.name}</Text>
@ -109,7 +110,7 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
onEndReached={() => {}}
scrollEventThrottle={16}
/>
</View>
</Animated.View>
);
};

View file

@ -11,7 +11,7 @@ import {
ActivityIndicator
} from 'react-native';
import { FlashList } from '@shopify/flash-list';
import Animated from 'react-native-reanimated';
import Animated, { FadeIn } from 'react-native-reanimated';
import { useNavigation } from '@react-navigation/native';
import { NavigationProp } from '@react-navigation/native';
import { RootStackParamList } from '../../navigation/AppNavigator';
@ -509,7 +509,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
}
return (
<View style={styles.container}>
<Animated.View style={styles.container} entering={FadeIn.duration(350)}>
<View style={styles.header}>
<View style={styles.titleContainer}>
<Text style={[styles.title, { color: currentTheme.colors.text }]}>Continue Watching</Text>
@ -632,7 +632,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
onEndReachedThreshold={0.7}
onEndReached={() => {}}
/>
</View>
</Animated.View>
);
});

View file

@ -20,7 +20,7 @@ import { tmdbService } from '../../services/tmdbService';
import { useLibrary } from '../../hooks/useLibrary';
import { RootStackParamList } from '../../navigation/AppNavigator';
import { parseISO, isThisWeek, format, isAfter, isBefore } from 'date-fns';
import Animated from 'react-native-reanimated';
import Animated, { FadeIn } from 'react-native-reanimated';
import { useCalendarData } from '../../hooks/useCalendarData';
const { width } = Dimensions.get('window');
@ -179,7 +179,7 @@ export const ThisWeekSection = React.memo(() => {
};
return (
<View style={styles.container}>
<Animated.View style={styles.container} entering={FadeIn.duration(350)}>
<View style={styles.header}>
<View style={styles.titleContainer}>
<Text style={[styles.title, { color: currentTheme.colors.text }]}>This Week</Text>
@ -203,7 +203,7 @@ export const ThisWeekSection = React.memo(() => {
snapToAlignment="start"
ItemSeparatorComponent={() => <View style={{ width: 16 }} />}
/>
</View>
</Animated.View>
);
});

View file

@ -654,7 +654,6 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
data={currentSeasonEpisodes}
renderItem={({ item: episode, index }) => (
<Animated.View
key={episode.id}
entering={FadeIn.duration(300).delay(100 + index * 30)}
style={[
styles.episodeCardWrapperHorizontal,
@ -668,6 +667,14 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={isTablet ? styles.episodeListContentHorizontalTablet : styles.episodeListContentHorizontal}
estimatedItemSize={(isTablet ? width * 0.4 : width * 0.75) + (isTablet ? 20 : 16)}
overrideItemLayout={(layout, _item, _index) => {
const cardWidth = isTablet ? width * 0.4 : width * 0.75;
const margin = isTablet ? 20 : 16;
layout.size = cardWidth + margin;
layout.span = 1;
}}
removeClippedSubviews
/>
) : (
// Vertical Layout (Traditional)
@ -676,7 +683,6 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
data={currentSeasonEpisodes}
renderItem={({ item: episode, index }) => (
<Animated.View
key={episode.id}
entering={FadeIn.duration(300).delay(100 + index * 30)}
>
{renderVerticalEpisodeCard(episode)}
@ -684,6 +690,14 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
)}
keyExtractor={episode => episode.id.toString()}
contentContainerStyle={isTablet ? styles.episodeListContentVerticalTablet : styles.episodeListContentVertical}
estimatedItemSize={isTablet ? 160 + 16 : 120 + 16}
overrideItemLayout={(layout, _item, _index) => {
// height along main axis for vertical list
const itemHeight = (isTablet ? 160 : 120) + 16; // card height + marginBottom
layout.size = itemHeight;
layout.span = 1;
}}
removeClippedSubviews
/>
)
)}

View file

@ -621,19 +621,22 @@ const HomeScreen = () => {
const memoizedThisWeekSection = useMemo(() => <ThisWeekSection />, []);
const memoizedContinueWatchingSection = useMemo(() => <ContinueWatchingSection ref={continueWatchingRef} />, []);
const renderListItem = useCallback(({ item }: { item: HomeScreenListItem }) => {
const renderListItem = useCallback(({ item, index }: { item: HomeScreenListItem, index: number }) => {
const wrapper = (child: React.ReactNode) => (
<Animated.View entering={FadeIn.duration(350).delay(Math.min(index * 70, 700))}>
{child}
</Animated.View>
);
switch (item.type) {
// featured is rendered via ListHeaderComponent to avoid remounts
case 'thisWeek':
return memoizedThisWeekSection;
return wrapper(memoizedThisWeekSection);
case 'continueWatching':
return memoizedContinueWatchingSection;
return wrapper(memoizedContinueWatchingSection);
case 'catalog':
return (
<CatalogSection catalog={item.catalog} />
);
return wrapper(<CatalogSection catalog={item.catalog} />);
case 'placeholder':
return (
return wrapper(
<View style={styles.catalogPlaceholder}>
<View style={styles.placeholderHeader}>
<View style={[styles.placeholderTitle, { backgroundColor: currentTheme.colors.elevation1 }]} />
@ -669,7 +672,7 @@ const HomeScreen = () => {
</Animated.View>
);
case 'welcome':
return <FirstTimeWelcome />;
return wrapper(<FirstTimeWelcome />);
default:
return null;
}