optimzed perf
This commit is contained in:
parent
bf15c5fb45
commit
3effdee5c0
1 changed files with 58 additions and 49 deletions
|
|
@ -832,7 +832,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
const titleCardTranslateY = useSharedValue(0);
|
||||
const genreOpacity = useSharedValue(1);
|
||||
|
||||
// Performance optimization: Cache theme colors
|
||||
// Ultra-optimized theme colors with stable references
|
||||
const themeColors = useMemo(() => ({
|
||||
black: currentTheme.colors.black,
|
||||
darkBackground: currentTheme.colors.darkBackground,
|
||||
|
|
@ -840,6 +840,15 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
text: currentTheme.colors.text
|
||||
}), [currentTheme.colors.black, currentTheme.colors.darkBackground, currentTheme.colors.highEmphasis, currentTheme.colors.text]);
|
||||
|
||||
// Pre-calculated style objects for better performance
|
||||
const staticStyles = useMemo(() => ({
|
||||
heroWrapper: styles.heroWrapper,
|
||||
heroSection: styles.heroSection,
|
||||
absoluteFill: styles.absoluteFill,
|
||||
thumbnailContainer: styles.thumbnailContainer,
|
||||
thumbnailImage: styles.thumbnailImage,
|
||||
}), []);
|
||||
|
||||
// Handle trailer preload completion
|
||||
const handleTrailerPreloaded = useCallback(() => {
|
||||
setTrailerPreloaded(true);
|
||||
|
|
@ -1153,34 +1162,30 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
opacity: watchProgressOpacity.value,
|
||||
}), []);
|
||||
|
||||
// Enhanced backdrop with smooth loading animation and dynamic parallax effect
|
||||
// Ultra-optimized backdrop with cached calculations and minimal worklet overhead
|
||||
const backdropImageStyle = useAnimatedStyle(() => {
|
||||
'worklet';
|
||||
const scrollYValue = scrollY.value;
|
||||
|
||||
// Default zoom factor
|
||||
const defaultZoom = 1.1; // 10% zoom by default
|
||||
// Pre-calculated constants for better performance
|
||||
const DEFAULT_ZOOM = 1.1;
|
||||
const SCROLL_UP_MULTIPLIER = 0.002;
|
||||
const SCROLL_DOWN_MULTIPLIER = 0.0001;
|
||||
const MAX_SCALE = 1.4;
|
||||
const PARALLAX_FACTOR = 0.3;
|
||||
|
||||
// Dynamic scale based on scroll direction and position
|
||||
let scale = defaultZoom;
|
||||
if (scrollYValue < 0) {
|
||||
// Scrolling up - zoom in to fill blank area
|
||||
scale = defaultZoom + Math.abs(scrollYValue) * 0.002; // More aggressive zoom when scrolling up
|
||||
} else {
|
||||
// Scrolling down - subtle scale effect
|
||||
scale = defaultZoom + scrollYValue * 0.0001;
|
||||
}
|
||||
// Optimized scale calculation with minimal branching
|
||||
const scrollUpScale = DEFAULT_ZOOM + Math.abs(scrollYValue) * SCROLL_UP_MULTIPLIER;
|
||||
const scrollDownScale = DEFAULT_ZOOM + scrollYValue * SCROLL_DOWN_MULTIPLIER;
|
||||
const scale = Math.min(scrollYValue < 0 ? scrollUpScale : scrollDownScale, MAX_SCALE);
|
||||
|
||||
// Cap the scale to prevent excessive zoom
|
||||
scale = Math.min(scale, 1.4); // Allow up to 40% zoom (including default)
|
||||
|
||||
// Parallax effect - move image slower than scroll
|
||||
const parallaxOffset = scrollYValue * 0.3; // 30% of scroll speed
|
||||
// Single parallax calculation
|
||||
const parallaxOffset = scrollYValue * PARALLAX_FACTOR;
|
||||
|
||||
return {
|
||||
opacity: imageOpacity.value * imageLoadOpacity.value,
|
||||
transform: [
|
||||
{ scale: scale },
|
||||
{ scale },
|
||||
{ translateY: parallaxOffset }
|
||||
],
|
||||
};
|
||||
|
|
@ -1209,29 +1214,29 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
opacity: genreOpacity.value
|
||||
}), []);
|
||||
|
||||
// Trailer parallax effect - moves slower than scroll for depth with dynamic zoom
|
||||
// Ultra-optimized trailer parallax with cached calculations
|
||||
const trailerParallaxStyle = useAnimatedStyle(() => {
|
||||
'worklet';
|
||||
const scrollYValue = scrollY.value;
|
||||
|
||||
// Dynamic scale for trailer based on scroll direction
|
||||
let scale = 1;
|
||||
if (scrollYValue < 0) {
|
||||
// Scrolling up - zoom in to fill blank area
|
||||
scale = 1 + Math.abs(scrollYValue) * 0.0015; // Slightly less aggressive than background
|
||||
} else {
|
||||
// Scrolling down - subtle scale effect
|
||||
scale = 1 + scrollYValue * 0.0001;
|
||||
}
|
||||
// Pre-calculated constants for better performance
|
||||
const DEFAULT_ZOOM = 1.0;
|
||||
const SCROLL_UP_MULTIPLIER = 0.0015;
|
||||
const SCROLL_DOWN_MULTIPLIER = 0.0001;
|
||||
const MAX_SCALE = 1.25;
|
||||
const PARALLAX_FACTOR = 0.2;
|
||||
|
||||
// Cap the scale to prevent excessive zoom
|
||||
scale = Math.min(scale, 1.25); // Allow up to 25% zoom for trailer
|
||||
// Optimized scale calculation with minimal branching
|
||||
const scrollUpScale = DEFAULT_ZOOM + Math.abs(scrollYValue) * SCROLL_UP_MULTIPLIER;
|
||||
const scrollDownScale = DEFAULT_ZOOM + scrollYValue * SCROLL_DOWN_MULTIPLIER;
|
||||
const scale = Math.min(scrollYValue < 0 ? scrollUpScale : scrollDownScale, MAX_SCALE);
|
||||
|
||||
const parallaxOffset = scrollYValue * 0.2; // 20% of scroll speed for trailer
|
||||
// Single parallax calculation
|
||||
const parallaxOffset = scrollYValue * PARALLAX_FACTOR;
|
||||
|
||||
return {
|
||||
transform: [
|
||||
{ scale: scale },
|
||||
{ scale },
|
||||
{ translateY: parallaxOffset }
|
||||
],
|
||||
};
|
||||
|
|
@ -1384,27 +1389,31 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
}
|
||||
}, [isFocused, setTrailerPlaying]);
|
||||
|
||||
// Pause/resume trailer based on scroll with hysteresis and guard
|
||||
// Ultra-optimized scroll-based pause/resume with cached calculations
|
||||
useDerivedValue(() => {
|
||||
'worklet';
|
||||
try {
|
||||
if (!scrollGuardEnabledSV.value || isFocusedSV.value === 0) return;
|
||||
const pauseThreshold = heroHeight.value * 0.7; // pause when beyond 70%
|
||||
const resumeThreshold = heroHeight.value * 0.4; // resume when back within 40%
|
||||
|
||||
|
||||
// Pre-calculate thresholds for better performance
|
||||
const pauseThreshold = heroHeight.value * 0.7;
|
||||
const resumeThreshold = heroHeight.value * 0.4;
|
||||
const y = scrollY.value;
|
||||
const isPlaying = isPlayingSV.value === 1;
|
||||
const isPausedByScroll = pausedByScrollSV.value === 1;
|
||||
|
||||
if (y > pauseThreshold && isPlayingSV.value === 1 && pausedByScrollSV.value === 0) {
|
||||
// Optimized pause/resume logic with minimal branching
|
||||
if (y > pauseThreshold && isPlaying && !isPausedByScroll) {
|
||||
pausedByScrollSV.value = 1;
|
||||
runOnJS(setTrailerPlaying)(false);
|
||||
isPlayingSV.value = 0;
|
||||
} else if (y < resumeThreshold && pausedByScrollSV.value === 1) {
|
||||
} else if (y < resumeThreshold && isPausedByScroll) {
|
||||
pausedByScrollSV.value = 0;
|
||||
runOnJS(setTrailerPlaying)(true);
|
||||
isPlayingSV.value = 1;
|
||||
}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
// Silent error handling for performance
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1456,21 +1465,21 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
|
||||
|
||||
return (
|
||||
<View style={styles.heroWrapper}>
|
||||
<Animated.View style={[styles.heroSection, heroAnimatedStyle]}>
|
||||
<View style={staticStyles.heroWrapper}>
|
||||
<Animated.View style={[staticStyles.heroSection, heroAnimatedStyle]}>
|
||||
{/* Optimized Background */}
|
||||
<View style={[styles.absoluteFill, { backgroundColor: themeColors.black }]} />
|
||||
<View style={[staticStyles.absoluteFill, { backgroundColor: themeColors.black }]} />
|
||||
|
||||
{/* Shimmer loading effect removed */}
|
||||
|
||||
{/* Background thumbnail image - always rendered when available with parallax */}
|
||||
{shouldLoadSecondaryData && imageSource && !loadingBanner && (
|
||||
<Animated.View style={[styles.thumbnailContainer, {
|
||||
<Animated.View style={[staticStyles.thumbnailContainer, {
|
||||
opacity: thumbnailOpacity
|
||||
}]}>
|
||||
<Animated.Image
|
||||
source={{ uri: imageSource }}
|
||||
style={[styles.thumbnailImage, backdropImageStyle]}
|
||||
style={[staticStyles.thumbnailImage, backdropImageStyle]}
|
||||
resizeMode="cover"
|
||||
onError={handleImageError}
|
||||
onLoad={handleImageLoad}
|
||||
|
|
@ -1480,13 +1489,13 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
|
||||
{/* Hidden preload trailer player - loads in background */}
|
||||
{shouldLoadSecondaryData && settings?.showTrailers && trailerUrl && !trailerLoading && !trailerError && !trailerPreloaded && (
|
||||
<View style={[styles.absoluteFill, { opacity: 0, pointerEvents: 'none' }]}>
|
||||
<View style={[staticStyles.absoluteFill, { opacity: 0, pointerEvents: 'none' }]}>
|
||||
<TrailerPlayer
|
||||
key={`preload-${trailerUrl}`}
|
||||
trailerUrl={trailerUrl}
|
||||
autoPlay={false}
|
||||
muted={true}
|
||||
style={styles.absoluteFill}
|
||||
style={staticStyles.absoluteFill}
|
||||
hideLoadingSpinner={true}
|
||||
onLoad={handleTrailerPreloaded}
|
||||
onError={handleTrailerError}
|
||||
|
|
@ -1496,7 +1505,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
|
||||
{/* Visible trailer player - rendered on top with fade transition and parallax */}
|
||||
{shouldLoadSecondaryData && settings?.showTrailers && trailerUrl && !trailerLoading && !trailerError && trailerPreloaded && (
|
||||
<Animated.View style={[styles.absoluteFill, {
|
||||
<Animated.View style={[staticStyles.absoluteFill, {
|
||||
opacity: trailerOpacity
|
||||
}, trailerParallaxStyle]}>
|
||||
<TrailerPlayer
|
||||
|
|
@ -1505,7 +1514,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
trailerUrl={trailerUrl}
|
||||
autoPlay={globalTrailerPlaying}
|
||||
muted={trailerMuted}
|
||||
style={styles.absoluteFill}
|
||||
style={staticStyles.absoluteFill}
|
||||
hideLoadingSpinner={true}
|
||||
hideControls={true}
|
||||
onFullscreenToggle={handleFullscreenToggle}
|
||||
|
|
|
|||
Loading…
Reference in a new issue