Refactor animation durations across multiple components for improved performance and consistency

This update modifies the animation durations in various components, including CatalogSection, ContinueWatchingSection, FeaturedContent, and ThisWeekSection, reducing the fade-in durations to enhance the user experience. Additionally, adjustments were made to the CastSection, MetadataDetails, and SeriesContent components to streamline animations. The changes aim to create a more cohesive and responsive interface throughout the application.
This commit is contained in:
tapframe 2025-06-21 18:36:20 +05:30
parent e020c2f4d9
commit 4a6f349cdb
15 changed files with 153 additions and 115 deletions

View file

@ -76,7 +76,7 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
return (
<Animated.View
style={styles.catalogContainer}
entering={FadeIn.duration(400).delay(50)}
entering={FadeIn.duration(300).delay(50)}
>
<View style={styles.catalogHeader}>
<View style={styles.titleContainer}>

View file

@ -272,7 +272,7 @@ const ContinueWatchingSection = React.forwardRef<ContinueWatchingRef>((props, re
}
return (
<Animated.View entering={FadeIn.duration(400).delay(250)} style={styles.container}>
<Animated.View entering={FadeIn.duration(300).delay(150)} style={styles.container}>
<View style={styles.header}>
<View style={styles.titleContainer}>
<Text style={[styles.title, { color: currentTheme.colors.text }]}>Continue Watching</Text>

View file

@ -382,7 +382,7 @@ const FeaturedContent = ({ featuredContent, isSaved, handleSaveToLibrary }: Feat
return (
<Animated.View
entering={FadeIn.duration(800).easing(Easing.out(Easing.cubic))}
entering={FadeIn.duration(400).easing(Easing.out(Easing.cubic))}
>
<TouchableOpacity
activeOpacity={0.95}

View file

@ -189,7 +189,7 @@ export const ThisWeekSection = () => {
return (
<Animated.View
entering={FadeInRight.delay(index * 150).duration(600)}
entering={FadeInRight.delay(index * 50).duration(300)}
style={styles.episodeItemContainer}
>
<TouchableOpacity
@ -261,7 +261,7 @@ export const ThisWeekSection = () => {
};
return (
<Animated.View entering={FadeIn.duration(400)} style={styles.container}>
<Animated.View entering={FadeIn.duration(300)} style={styles.container}>
<View style={styles.header}>
<View style={styles.titleContainer}>
<Text style={[styles.title, { color: currentTheme.colors.text }]}>This Week</Text>

View file

@ -42,7 +42,7 @@ export const CastSection: React.FC<CastSectionProps> = ({
return (
<Animated.View
style={styles.castSection}
entering={FadeIn.duration(500).delay(300)}
entering={FadeIn.duration(300).delay(150)}
layout={Layout}
>
<View style={styles.sectionHeader}>
@ -56,7 +56,7 @@ export const CastSection: React.FC<CastSectionProps> = ({
keyExtractor={(item) => item.id.toString()}
renderItem={({ item, index }) => (
<Animated.View
entering={FadeIn.duration(500).delay(100 + index * 50)}
entering={FadeIn.duration(300).delay(50 + index * 30)}
layout={Layout}
>
<TouchableOpacity
@ -75,7 +75,7 @@ export const CastSection: React.FC<CastSectionProps> = ({
transition={200}
/>
) : (
<View style={[styles.castImagePlaceholder, { backgroundColor: currentTheme.colors.cardBackground }]}>
<View style={[styles.castImagePlaceholder, { backgroundColor: currentTheme.colors.darkBackground }]}>
<Text style={[styles.placeholderText, { color: currentTheme.colors.textMuted }]}>
{item.name.split(' ').reduce((prev: string, current: string) => prev + current[0], '').substring(0, 2)}
</Text>

View file

@ -60,7 +60,7 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
{/* Creator/Director Info */}
<Animated.View
entering={FadeIn.duration(500).delay(200)}
entering={FadeIn.duration(300).delay(100)}
style={styles.creatorContainer}
>
{metadata.directors && metadata.directors.length > 0 && (
@ -81,7 +81,7 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
{metadata.description && (
<Animated.View
style={styles.descriptionContainer}
layout={Layout.duration(300).easing(Easing.inOut(Easing.ease))}
entering={FadeIn.duration(300)}
>
<TouchableOpacity
onPress={() => setIsFullDescriptionOpen(!isFullDescriptionOpen)}

View file

@ -535,13 +535,13 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
return (
<View style={styles.container}>
<Animated.View
entering={FadeIn.duration(500).delay(100)}
entering={FadeIn.duration(300).delay(50)}
>
{renderSeasonSelector()}
</Animated.View>
<Animated.View
entering={FadeIn.duration(500).delay(200)}
entering={FadeIn.duration(300).delay(100)}
>
<Text style={[styles.sectionTitle, { color: currentTheme.colors.highEmphasis }]}>
{episodes.length} {episodes.length === 1 ? 'Episode' : 'Episodes'}
@ -562,7 +562,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
{currentSeasonEpisodes.map((episode, index) => (
<Animated.View
key={episode.id}
entering={FadeIn.duration(400).delay(300 + index * 50)}
entering={FadeIn.duration(300).delay(100 + index * 30)}
style={[
styles.episodeCardWrapperHorizontal,
isTablet && styles.episodeCardWrapperHorizontalTablet
@ -586,7 +586,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
{currentSeasonEpisodes.map((episode, index) => (
<Animated.View
key={episode.id}
entering={FadeIn.duration(400).delay(300 + index * 50)}
entering={FadeIn.duration(300).delay(100 + index * 30)}
>
{renderVerticalEpisodeCard(episode)}
</Animated.View>
@ -596,7 +596,7 @@ export const SeriesContent: React.FC<SeriesContentProps> = ({
currentSeasonEpisodes.map((episode, index) => (
<Animated.View
key={episode.id}
entering={FadeIn.duration(400).delay(300 + index * 50)}
entering={FadeIn.duration(300).delay(100 + index * 30)}
>
{renderVerticalEpisodeCard(episode)}
</Animated.View>

View file

@ -19,8 +19,6 @@ import Animated, {
withDelay,
withSequence,
runOnJS,
BounceIn,
ZoomIn
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
import { styles } from '../utils/playerStyles';
@ -227,7 +225,7 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
</Text>
</Animated.View>
<Animated.View entering={BounceIn.duration(400).delay(200)}>
<Animated.View entering={FadeIn.duration(300).delay(200)}>
<TouchableOpacity
style={{
width: 44,
@ -267,8 +265,8 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
{vlcAudioTracks.length > 0 ? vlcAudioTracks.map((track, index) => (
<Animated.View
key={track.id}
entering={FadeInDown.duration(300).delay(150 + (index * 50))}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(50 + index * 30)}
exiting={FadeOut.duration(150)}
style={{
marginBottom: 16,
width: '100%',
@ -323,7 +321,7 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
{selectedAudioTrack === track.id && (
<Animated.View
entering={BounceIn.duration(300)}
entering={FadeIn.duration(300)}
style={{
flexDirection: 'row',
alignItems: 'center',
@ -388,7 +386,7 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
: 'rgba(255, 255, 255, 0.1)',
}}>
{selectedAudioTrack === track.id ? (
<Animated.View entering={ZoomIn.duration(200)}>
<Animated.View entering={FadeIn.duration(200)}>
<MaterialIcons name="check-circle" size={24} color="#F97316" />
</Animated.View>
) : (
@ -400,7 +398,7 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
</Animated.View>
)) : (
<Animated.View
entering={FadeInDown.duration(300).delay(150)}
entering={FadeIn.duration(300).delay(150)}
style={{
backgroundColor: 'rgba(255, 255, 255, 0.02)',
borderRadius: 20,

View file

@ -19,8 +19,6 @@ import Animated, {
withDelay,
withSequence,
runOnJS,
BounceIn,
ZoomIn
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
import { styles } from '../utils/playerStyles';
@ -62,7 +60,7 @@ const QualityIndicator = ({ quality }: { quality: string | null }) => {
return (
<Animated.View
entering={ZoomIn.duration(200).delay(100)}
entering={FadeIn.duration(200).delay(100)}
style={{
backgroundColor: `${color}20`,
borderColor: `${color}60`,
@ -107,7 +105,7 @@ const StreamMetaBadge = ({
delay?: number;
}) => (
<Animated.View
entering={FadeInUp.duration(200).delay(delay)}
entering={FadeIn.duration(200).delay(delay)}
style={{
backgroundColor: bgColor,
borderColor: `${color}40`,
@ -279,7 +277,7 @@ const SourcesModal: React.FC<SourcesModalProps> = ({
}}
>
<Animated.View
entering={FadeInDown.duration(300).delay(100)}
entering={FadeIn.duration(300).delay(100)}
style={{ flex: 1 }}
>
<Text style={{
@ -304,7 +302,7 @@ const SourcesModal: React.FC<SourcesModalProps> = ({
</Text>
</Animated.View>
<Animated.View entering={BounceIn.duration(400).delay(200)}>
<Animated.View entering={FadeIn.duration(300).delay(200)}>
<TouchableOpacity
style={{
width: 44,
@ -343,8 +341,8 @@ const SourcesModal: React.FC<SourcesModalProps> = ({
{sortedProviders.map(([providerId, { streams, addonName }], providerIndex) => (
<Animated.View
key={providerId}
entering={FadeInDown.duration(400).delay(150 + (providerIndex * 80))}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(50 + providerIndex * 30)}
exiting={FadeOut.duration(150)}
style={{
marginBottom: streams.length > 0 ? 32 : 0,
width: '100%',
@ -426,8 +424,8 @@ const SourcesModal: React.FC<SourcesModalProps> = ({
return (
<Animated.View
key={`${stream.url}-${index}`}
entering={FadeInDown.duration(300).delay((providerIndex * 80) + (index * 40))}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(100 + index * 50)}
exiting={FadeOut.duration(150)}
style={{ width: '100%' }}
>
<TouchableOpacity
@ -482,7 +480,7 @@ const SourcesModal: React.FC<SourcesModalProps> = ({
{isSelected && (
<Animated.View
entering={BounceIn.duration(300)}
entering={FadeIn.duration(300)}
style={{
flexDirection: 'row',
alignItems: 'center',
@ -632,7 +630,7 @@ const SourcesModal: React.FC<SourcesModalProps> = ({
shadowRadius: 4,
}}>
{isSelected ? (
<Animated.View entering={ZoomIn.duration(200)}>
<Animated.View entering={FadeIn.duration(200)}>
<MaterialIcons name="check-circle" size={24} color="#E50914" />
</Animated.View>
) : (

View file

@ -19,8 +19,6 @@ import Animated, {
withDelay,
withSequence,
runOnJS,
BounceIn,
ZoomIn
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
import { styles } from '../utils/playerStyles';
@ -281,7 +279,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
</Text>
</Animated.View>
<Animated.View entering={BounceIn.duration(400).delay(200)}>
<Animated.View entering={FadeIn.duration(300).delay(200)}>
<TouchableOpacity
style={{
width: 44,
@ -321,8 +319,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{/* External Subtitles Section */}
<Animated.View
entering={FadeInDown.duration(400).delay(150)}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(150)}
exiting={FadeOut.duration(150)}
style={{
marginBottom: 32,
}}
@ -372,8 +370,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{/* Custom subtitles option */}
{customSubtitles.length > 0 && (
<Animated.View
entering={FadeInDown.duration(300).delay(200)}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(200)}
exiting={FadeOut.duration(150)}
style={{ marginBottom: 16 }}
>
<TouchableOpacity
@ -423,7 +421,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{useCustomSubtitles && (
<Animated.View
entering={BounceIn.duration(300)}
entering={FadeIn.duration(300)}
style={{
flexDirection: 'row',
alignItems: 'center',
@ -486,7 +484,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
: 'rgba(255, 255, 255, 0.1)',
}}>
{useCustomSubtitles ? (
<Animated.View entering={ZoomIn.duration(200)}>
<Animated.View entering={FadeIn.duration(300)}>
<MaterialIcons name="check-circle" size={24} color="#4CAF50" />
</Animated.View>
) : (
@ -500,8 +498,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{/* Search for external subtitles */}
<Animated.View
entering={FadeInDown.duration(300).delay(250)}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(250)}
exiting={FadeOut.duration(150)}
>
<TouchableOpacity
style={{
@ -549,8 +547,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{/* Subtitle Size Controls */}
{useCustomSubtitles && (
<Animated.View
entering={FadeInDown.duration(400).delay(200)}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(200)}
exiting={FadeOut.duration(150)}
style={{
marginBottom: 32,
}}
@ -598,7 +596,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
</View>
<Animated.View
entering={FadeInDown.duration(300).delay(300)}
entering={FadeIn.duration(200).delay(300)}
exiting={FadeOut.duration(150)}
style={{
backgroundColor: 'rgba(255, 255, 255, 0.03)',
borderRadius: 20,
@ -698,8 +697,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{vlcTextTracks.length > 0 ? vlcTextTracks.map((track, index) => (
<Animated.View
key={track.id}
entering={FadeInDown.duration(300).delay(400 + (index * 50))}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(50 + index * 30)}
exiting={FadeOut.duration(150)}
style={{ marginBottom: 16 }}
>
<TouchableOpacity
@ -749,7 +748,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{(selectedTextTrack === track.id && !useCustomSubtitles) && (
<Animated.View
entering={BounceIn.duration(300)}
entering={FadeIn.duration(300)}
style={{
flexDirection: 'row',
alignItems: 'center',
@ -812,7 +811,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
: 'rgba(255, 255, 255, 0.1)',
}}>
{(selectedTextTrack === track.id && !useCustomSubtitles) ? (
<Animated.View entering={ZoomIn.duration(200)}>
<Animated.View entering={FadeIn.duration(300)}>
<MaterialIcons name="check-circle" size={24} color="#FF9800" />
</Animated.View>
) : (
@ -824,7 +823,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
</Animated.View>
)) : (
<Animated.View
entering={FadeInDown.duration(300).delay(400)}
entering={FadeIn.duration(200).delay(400)}
exiting={FadeOut.duration(150)}
style={{
backgroundColor: 'rgba(255, 255, 255, 0.02)',
borderRadius: 16,
@ -972,7 +972,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
</Text>
</Animated.View>
<Animated.View entering={BounceIn.duration(400).delay(200)}>
<Animated.View entering={FadeIn.duration(300).delay(200)}>
<TouchableOpacity
style={{
width: 44,
@ -1011,8 +1011,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
{availableSubtitles.length > 0 ? availableSubtitles.map((subtitle, index) => (
<Animated.View
key={subtitle.id}
entering={FadeInDown.duration(300).delay(150 + (index * 50))}
layout={Layout.springify()}
entering={FadeIn.duration(200).delay(100 + index * 50)}
exiting={FadeOut.duration(150)}
style={{ marginBottom: 16 }}
>
<TouchableOpacity
@ -1098,7 +1098,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
</Animated.View>
)) : (
<Animated.View
entering={FadeInDown.duration(300).delay(150)}
entering={FadeIn.duration(200).delay(150)}
style={{
backgroundColor: 'rgba(255, 255, 255, 0.02)',
borderRadius: 20,

View file

@ -7,6 +7,7 @@ import {
Easing,
useAnimatedScrollHandler,
runOnUI,
cancelAnimation,
} from 'react-native-reanimated';
const { width, height } = Dimensions.get('window');
@ -57,73 +58,115 @@ export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) =
// Ultra-fast entrance sequence - batch animations for better performance
useEffect(() => {
// Batch all entrance animations to run simultaneously
// Batch all entrance animations to run simultaneously with safety
const enterAnimations = () => {
'worklet';
// Start with slightly reduced values and animate to full visibility
screenOpacity.value = withTiming(1, {
duration: 250,
easing: easings.fast
});
heroOpacity.value = withTiming(1, {
duration: 300,
easing: easings.fast
});
heroScale.value = withSpring(1, ultraFastSpring);
uiElementsOpacity.value = withTiming(1, {
duration: 400,
easing: easings.natural
});
uiElementsTranslateY.value = withSpring(0, fastSpring);
contentOpacity.value = withTiming(1, {
duration: 350,
easing: easings.fast
});
try {
// Start with slightly reduced values and animate to full visibility
screenOpacity.value = withTiming(1, {
duration: 250,
easing: easings.fast
});
heroOpacity.value = withTiming(1, {
duration: 300,
easing: easings.fast
});
heroScale.value = withSpring(1, ultraFastSpring);
uiElementsOpacity.value = withTiming(1, {
duration: 400,
easing: easings.natural
});
uiElementsTranslateY.value = withSpring(0, fastSpring);
contentOpacity.value = withTiming(1, {
duration: 350,
easing: easings.fast
});
} catch (error) {
// Silently handle any animation errors
console.warn('Animation error in enterAnimations:', error);
}
};
// Use runOnUI for better performance
runOnUI(enterAnimations)();
// Use runOnUI for better performance with error handling
try {
runOnUI(enterAnimations)();
} catch (error) {
console.warn('Failed to run enter animations:', error);
}
}, []);
// Optimized watch progress animation
// Optimized watch progress animation with safety
useEffect(() => {
const hasProgress = watchProgress && watchProgress.duration > 0;
const updateProgress = () => {
'worklet';
progressOpacity.value = withTiming(hasProgress ? 1 : 0, {
duration: hasProgress ? 200 : 150,
easing: easings.fast
});
try {
progressOpacity.value = withTiming(hasProgress ? 1 : 0, {
duration: hasProgress ? 200 : 150,
easing: easings.fast
});
} catch (error) {
console.warn('Animation error in updateProgress:', error);
}
};
runOnUI(updateProgress)();
try {
runOnUI(updateProgress)();
} catch (error) {
console.warn('Failed to run progress animation:', error);
}
}, [watchProgress]);
// Ultra-optimized scroll handler with minimal calculations
// Cleanup function to cancel animations
useEffect(() => {
return () => {
try {
cancelAnimation(screenOpacity);
cancelAnimation(contentOpacity);
cancelAnimation(heroOpacity);
cancelAnimation(heroScale);
cancelAnimation(uiElementsOpacity);
cancelAnimation(uiElementsTranslateY);
cancelAnimation(progressOpacity);
cancelAnimation(scrollY);
cancelAnimation(headerProgress);
cancelAnimation(staticHeaderElementsY);
} catch (error) {
console.warn('Error canceling animations:', error);
}
};
}, []);
// Ultra-optimized scroll handler with minimal calculations and safety
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
'worklet';
const rawScrollY = event.contentOffset.y;
scrollY.value = rawScrollY;
try {
const rawScrollY = event.contentOffset.y;
scrollY.value = rawScrollY;
// Single calculation for header threshold
const threshold = height * 0.4 - safeAreaTop;
const progress = rawScrollY > threshold ? 1 : 0;
// Use single progress value for all header animations
if (headerProgress.value !== progress) {
headerProgress.value = withTiming(progress, {
duration: progress ? 200 : 150,
easing: easings.ultraFast
});
// Single calculation for header threshold
const threshold = height * 0.4 - safeAreaTop;
const progress = rawScrollY > threshold ? 1 : 0;
// Use single progress value for all header animations
if (headerProgress.value !== progress) {
headerProgress.value = withTiming(progress, {
duration: progress ? 200 : 150,
easing: easings.ultraFast
});
}
} catch (error) {
console.warn('Animation error in scroll handler:', error);
}
},
});

View file

@ -463,7 +463,7 @@ const HomeScreen = () => {
/>
);
case 'thisWeek':
return <Animated.View entering={FadeIn.duration(400).delay(150)}><ThisWeekSection /></Animated.View>;
return <Animated.View entering={FadeIn.duration(300).delay(100)}><ThisWeekSection /></Animated.View>;
case 'continueWatching':
return <ContinueWatchingSection ref={continueWatchingRef} />;
case 'catalog':

View file

@ -35,7 +35,6 @@ import Animated, {
interpolate,
withSpring,
withDelay,
ZoomIn
} from 'react-native-reanimated';
import { RootStackParamList } from '../navigation/AppNavigator';
import { logger } from '../utils/logger';
@ -445,7 +444,7 @@ const SearchScreen = () => {
onPress={() => {
navigation.navigate('Metadata', { id: item.id, type: item.type });
}}
entering={FadeIn.duration(500).delay(index * 100)}
entering={FadeIn.duration(300).delay(index * 50)}
activeOpacity={0.7}
>
<View style={[styles.horizontalItemPosterContainer, {
@ -650,7 +649,7 @@ const SearchScreen = () => {
{seriesResults.length > 0 && (
<Animated.View
style={styles.carouselContainer}
entering={FadeIn.duration(300).delay(100)}
entering={FadeIn.duration(300).delay(50)}
>
<Text style={[styles.carouselTitle, { color: currentTheme.colors.white }]}>
TV Shows ({seriesResults.length})

View file

@ -481,7 +481,7 @@ const ShowRatingsScreen = ({ route }: Props) => {
<Animated.View
key={`s${season.season_number}`}
style={styles.ratingColumn}
entering={SlideInRight.delay(season.season_number * 50).duration(200)}
entering={FadeIn.delay(season.season_number * 20).duration(200)}
>
<Text style={[styles.headerText, { color: colors.white }]}>S{season.season_number}</Text>
</Animated.View>
@ -507,7 +507,7 @@ const ShowRatingsScreen = ({ route }: Props) => {
<Animated.View
key={`s${season.season_number}e${episodeIndex + 1}`}
style={styles.ratingColumn}
entering={SlideInRight.delay((season.season_number + episodeIndex) * 10).duration(200)}
entering={FadeIn.delay((season.season_number + episodeIndex) * 5).duration(200)}
>
{season.episodes[episodeIndex] &&
<RatingCell

View file

@ -165,7 +165,7 @@ const StreamCard = ({ stream, onPress, index, isLoading, statusMessage, theme, i
return (
<Animated.View
entering={FadeInDown.duration(200).delay(enterDelay)}
layout={Layout.duration(200)}
exiting={FadeOut.duration(150)}
>
<TouchableOpacity
style={[
@ -268,7 +268,7 @@ const ProviderFilter = memo(({
const renderItem = useCallback(({ item, index }: { item: { id: string; name: string }; index: number }) => (
<Animated.View
entering={FadeIn.duration(300).delay(100 + index * 40)}
layout={Layout.springify()}
exiting={FadeOut.duration(150)}
>
<TouchableOpacity
key={item.id}
@ -1076,7 +1076,7 @@ export const StreamsScreen = () => {
return (
<Animated.View
entering={FadeIn.duration(400)}
layout={Layout.springify()}
exiting={FadeOut.duration(150)}
style={styles.sectionHeaderContainer}
>
<View style={styles.sectionHeaderContent}>
@ -1196,11 +1196,11 @@ export const StreamsScreen = () => {
{type === 'series' && currentEpisode && (
<Animated.View style={[styles.streamsHeroContainer, heroStyle]}>
<Animated.View
entering={FadeIn.duration(600).springify()}
entering={FadeIn.duration(300)}
style={StyleSheet.absoluteFill}
>
<Animated.View
entering={FadeIn.duration(800).delay(100).springify().withInitialValues({
entering={FadeIn.duration(400).delay(100).withInitialValues({
transform: [{ scale: 1.05 }]
})}
style={StyleSheet.absoluteFill}