mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-27 19:33:02 +00:00
ui changes
This commit is contained in:
parent
426e936740
commit
daafdeedc2
4 changed files with 73 additions and 10 deletions
|
|
@ -57,15 +57,62 @@ const STATUS_BAR_HEIGHT = StatusBar.currentHeight || 0;
|
||||||
const HERO_HEIGHT = height * 0.85;
|
const HERO_HEIGHT = height * 0.85;
|
||||||
|
|
||||||
// Animated Pagination Dot Component
|
// Animated Pagination Dot Component
|
||||||
const PaginationDot: React.FC<{ isActive: boolean; onPress: () => void }> = React.memo(
|
const PaginationDot: React.FC<{
|
||||||
({ isActive, onPress }) => {
|
isActive: boolean;
|
||||||
|
isNext: boolean;
|
||||||
|
dragProgress: SharedValue<number>;
|
||||||
|
onPress: () => void;
|
||||||
|
}> = React.memo(
|
||||||
|
({ isActive, isNext, dragProgress, onPress }) => {
|
||||||
const animatedStyle = useAnimatedStyle(() => {
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
|
// Base values
|
||||||
|
const activeWidth = 32;
|
||||||
|
const inactiveWidth = 8;
|
||||||
|
const activeOpacity = 0.9;
|
||||||
|
const inactiveOpacity = 0.3;
|
||||||
|
|
||||||
|
// Calculate target width and opacity based on state
|
||||||
|
let targetWidth = isActive ? activeWidth : inactiveWidth;
|
||||||
|
let targetOpacity = isActive ? activeOpacity : inactiveOpacity;
|
||||||
|
|
||||||
|
// If this is the next dot during drag, interpolate between inactive and active
|
||||||
|
if (isNext && dragProgress.value > 0) {
|
||||||
|
targetWidth = interpolate(
|
||||||
|
dragProgress.value,
|
||||||
|
[0, 1],
|
||||||
|
[inactiveWidth, activeWidth],
|
||||||
|
Extrapolation.CLAMP
|
||||||
|
);
|
||||||
|
targetOpacity = interpolate(
|
||||||
|
dragProgress.value,
|
||||||
|
[0, 1],
|
||||||
|
[inactiveOpacity, activeOpacity],
|
||||||
|
Extrapolation.CLAMP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the current active dot during drag, interpolate from active to inactive
|
||||||
|
if (isActive && dragProgress.value > 0) {
|
||||||
|
targetWidth = interpolate(
|
||||||
|
dragProgress.value,
|
||||||
|
[0, 1],
|
||||||
|
[activeWidth, inactiveWidth],
|
||||||
|
Extrapolation.CLAMP
|
||||||
|
);
|
||||||
|
targetOpacity = interpolate(
|
||||||
|
dragProgress.value,
|
||||||
|
[0, 1],
|
||||||
|
[activeOpacity, inactiveOpacity],
|
||||||
|
Extrapolation.CLAMP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width: withTiming(isActive ? 32 : 8, {
|
width: withTiming(targetWidth, {
|
||||||
duration: 300,
|
duration: 300,
|
||||||
easing: Easing.out(Easing.cubic),
|
easing: Easing.out(Easing.cubic),
|
||||||
}),
|
}),
|
||||||
opacity: withTiming(isActive ? 0.9 : 0.3, {
|
opacity: withTiming(targetOpacity, {
|
||||||
duration: 300,
|
duration: 300,
|
||||||
easing: Easing.out(Easing.cubic),
|
easing: Easing.out(Easing.cubic),
|
||||||
}),
|
}),
|
||||||
|
|
@ -890,6 +937,8 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
|
||||||
<PaginationDot
|
<PaginationDot
|
||||||
key={index}
|
key={index}
|
||||||
isActive={index === currentIndex}
|
isActive={index === currentIndex}
|
||||||
|
isNext={index === nextIndex && nextIndex !== currentIndex}
|
||||||
|
dragProgress={dragProgress}
|
||||||
onPress={() => handleDotPress(index)}
|
onPress={() => handleDotPress(index)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ import { logger } from '../../utils/logger';
|
||||||
import { TMDBService } from '../../services/tmdbService';
|
import { TMDBService } from '../../services/tmdbService';
|
||||||
import TrailerService from '../../services/trailerService';
|
import TrailerService from '../../services/trailerService';
|
||||||
import TrailerPlayer from '../video/TrailerPlayer';
|
import TrailerPlayer from '../video/TrailerPlayer';
|
||||||
|
import { HERO_HEIGHT, SCREEN_WIDTH as width, IS_TABLET as isTablet } from '../../constants/dimensions';
|
||||||
|
|
||||||
const { width, height } = Dimensions.get('window');
|
const { height } = Dimensions.get('window');
|
||||||
const isTablet = width >= 768;
|
|
||||||
|
|
||||||
// Ultra-optimized animation constants
|
// Ultra-optimized animation constants
|
||||||
const SCALE_FACTOR = 1.02;
|
const SCALE_FACTOR = 1.02;
|
||||||
|
|
@ -1540,7 +1540,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
||||||
buttonsTranslateY.value = 0;
|
buttonsTranslateY.value = 0;
|
||||||
logoOpacity.value = 1;
|
logoOpacity.value = 1;
|
||||||
heroOpacity.value = 1;
|
heroOpacity.value = 1;
|
||||||
heroHeight.value = height * 0.6;
|
heroHeight.value = HERO_HEIGHT;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('HeroSection', 'Error cleaning up animation values:', error);
|
logger.error('HeroSection', 'Error cleaning up animation values:', error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/constants/dimensions.ts
Normal file
13
src/constants/dimensions.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Dimensions } from 'react-native';
|
||||||
|
|
||||||
|
const { width, height } = Dimensions.get('window');
|
||||||
|
|
||||||
|
// Hero section height - 85% of screen height (matching Apple TV style)
|
||||||
|
export const HERO_HEIGHT = height * 0.70;
|
||||||
|
|
||||||
|
// Screen dimensions
|
||||||
|
export const SCREEN_WIDTH = width;
|
||||||
|
export const SCREEN_HEIGHT = height;
|
||||||
|
|
||||||
|
// Tablet detection
|
||||||
|
export const IS_TABLET = width >= 768;
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
runOnUI,
|
runOnUI,
|
||||||
cancelAnimation,
|
cancelAnimation,
|
||||||
} from 'react-native-reanimated';
|
} from 'react-native-reanimated';
|
||||||
|
import { HERO_HEIGHT } from '../constants/dimensions';
|
||||||
|
|
||||||
const { width, height } = Dimensions.get('window');
|
const { width, height } = Dimensions.get('window');
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) =
|
||||||
// Combined hero animations
|
// Combined hero animations
|
||||||
const heroOpacity = useSharedValue(1);
|
const heroOpacity = useSharedValue(1);
|
||||||
const heroScale = useSharedValue(1); // Start at 1 for Android compatibility
|
const heroScale = useSharedValue(1); // Start at 1 for Android compatibility
|
||||||
const heroHeightValue = useSharedValue(height * 0.55);
|
const heroHeightValue = useSharedValue(HERO_HEIGHT);
|
||||||
|
|
||||||
// Combined UI element animations
|
// Combined UI element animations
|
||||||
const uiElementsOpacity = useSharedValue(1);
|
const uiElementsOpacity = useSharedValue(1);
|
||||||
|
|
@ -154,8 +155,8 @@ export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) =
|
||||||
const rawScrollY = event.contentOffset.y;
|
const rawScrollY = event.contentOffset.y;
|
||||||
scrollY.value = rawScrollY;
|
scrollY.value = rawScrollY;
|
||||||
|
|
||||||
// Single calculation for header threshold
|
// Single calculation for header threshold - show only when hero is fully scrolled
|
||||||
const threshold = height * 0.4 - safeAreaTop;
|
const threshold = HERO_HEIGHT - safeAreaTop;
|
||||||
const progress = rawScrollY > threshold ? 1 : 0;
|
const progress = rawScrollY > threshold ? 1 : 0;
|
||||||
|
|
||||||
// Use single progress value for all header animations
|
// Use single progress value for all header animations
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue