hero changes

This commit is contained in:
tapframe 2025-11-08 11:54:43 +05:30
parent 2c524020af
commit ea7f6bf7d7

View file

@ -128,6 +128,7 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
// Animation values // Animation values
const dragProgress = useSharedValue(0); const dragProgress = useSharedValue(0);
const dragDirection = useSharedValue(0); // -1 for left, 1 for right
const logoOpacity = useSharedValue(1); const logoOpacity = useSharedValue(1);
const [nextIndex, setNextIndex] = useState(currentIndex); const [nextIndex, setNextIndex] = useState(currentIndex);
const thumbnailOpacity = useSharedValue(1); const thumbnailOpacity = useSharedValue(1);
@ -337,13 +338,14 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
// Reset drag progress and animate logo when index changes // Reset drag progress and animate logo when index changes
useEffect(() => { useEffect(() => {
// Instant reset - no extra fade animation
dragProgress.value = 0; dragProgress.value = 0;
setNextIndex(currentIndex); setNextIndex(currentIndex);
// Fade out and fade in logo/title // Quick logo fade
logoOpacity.value = 0; logoOpacity.value = 0;
logoOpacity.value = withDelay( logoOpacity.value = withDelay(
200, 150,
withTiming(1, { withTiming(1, {
duration: 400, duration: 400,
easing: Easing.out(Easing.cubic), easing: Easing.out(Easing.cubic),
@ -375,18 +377,26 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
const panGesture = useMemo( const panGesture = useMemo(
() => () =>
Gesture.Pan() Gesture.Pan()
.activeOffsetX([-10, 10]) // Only activate on horizontal movement .activeOffsetX([-5, 5]) // Smaller activation area - more sensitive
.failOffsetY([-10, 10]) // Fail if vertical movement is detected .failOffsetY([-15, 15]) // Fail if vertical movement is detected
.onStart(() => { .onStart(() => {
// Determine which direction and set preview // Determine which direction and set preview
runOnJS(updateInteractionTime)(); runOnJS(updateInteractionTime)();
}) })
.onUpdate((event) => { .onUpdate((event) => {
const translationX = event.translationX; const translationX = event.translationX;
const progress = Math.abs(translationX) / width; // Use smaller width multiplier for easier drag
const progress = Math.abs(translationX) / (width * 0.6);
// Update drag progress (0 to 1) // Update drag progress (0 to 1) with eased curve
dragProgress.value = Math.min(progress, 1); dragProgress.value = Math.min(progress, 1);
// Track drag direction: positive = right (previous), negative = left (next)
if (translationX > 0) {
dragDirection.value = 1; // Swiping right - show previous
} else if (translationX < 0) {
dragDirection.value = -1; // Swiping left - show next
}
// Determine preview index based on direction // Determine preview index based on direction
if (translationX > 0) { if (translationX > 0) {
@ -402,55 +412,57 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
.onEnd((event) => { .onEnd((event) => {
const velocity = event.velocityX; const velocity = event.velocityX;
const translationX = event.translationX; const translationX = event.translationX;
const swipeThreshold = width * 0.25; const swipeThreshold = width * 0.15; // Smaller threshold - easier to swipe
if (Math.abs(translationX) > swipeThreshold || Math.abs(velocity) > 800) { if (Math.abs(translationX) > swipeThreshold || Math.abs(velocity) > 600) {
// Complete the swipe // Complete the swipe - instant navigation
if (translationX > 0) { if (translationX > 0) {
runOnJS(goToPrevious)(); runOnJS(goToPrevious)();
} else { } else {
runOnJS(goToNext)(); runOnJS(goToNext)();
} }
} else { } else {
// Cancel the swipe - animate back // Cancel the swipe - animate back with ease
dragProgress.value = withTiming(0, { dragProgress.value = withTiming(0, {
duration: 300, duration: 250,
easing: Easing.out(Easing.cubic), easing: Easing.bezier(0.4, 0.0, 0.2, 1), // Material design ease curve
}); });
} }
}), }),
[goToPrevious, goToNext, updateInteractionTime, setPreviewIndex, currentIndex, items.length] [goToPrevious, goToNext, updateInteractionTime, setPreviewIndex, currentIndex, items.length]
); );
// Animated styles for current and next images // Animated styles for next image only - smooth crossfade + slide during drag
const currentImageStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(
dragProgress.value,
[0, 1],
[1, 0],
Extrapolate.CLAMP
),
};
});
const nextImageStyle = useAnimatedStyle(() => { const nextImageStyle = useAnimatedStyle(() => {
// Enhanced 4-point curve for smoother crossfade
const opacity = interpolate(
dragProgress.value,
[0, 0.3, 0.7, 1],
[0, 0.4, 0.8, 1],
Extrapolate.CLAMP
);
// Smoother slide effect with ease-out curve
const slideDistance = 20; // Subtle 20px movement
const slideProgress = interpolate(
dragProgress.value,
[0, 0.4, 0.8, 1], // 4-point for smoother acceleration
[-slideDistance * dragDirection.value, -slideDistance * 0.5 * dragDirection.value, -slideDistance * 0.15 * dragDirection.value, 0],
Extrapolate.CLAMP
);
return { return {
opacity: interpolate( opacity,
dragProgress.value, transform: [{ translateX: slideProgress }],
[0, 1],
[0, 1],
Extrapolate.CLAMP
),
}; };
}); });
// Animated style for logo/title only - fades during drag // Animated style for logo/title only - fades during drag with smoother curve
const logoAnimatedStyle = useAnimatedStyle(() => { const logoAnimatedStyle = useAnimatedStyle(() => {
const dragFade = interpolate( const dragFade = interpolate(
dragProgress.value, dragProgress.value,
[0, 0.3], [0, 0.2, 0.4],
[1, 0], [1, 0.5, 0],
Extrapolate.CLAMP Extrapolate.CLAMP
); );
@ -506,10 +518,8 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
> >
{/* Background Images with Crossfade */} {/* Background Images with Crossfade */}
<View style={styles.backgroundContainer}> <View style={styles.backgroundContainer}>
{/* Current Image - Thumbnail with fade */} {/* Current Image - Always visible as base */}
<Animated.View style={[StyleSheet.absoluteFillObject, currentImageStyle, { <View style={styles.imageWrapper}>
opacity: thumbnailOpacity
}]}>
<FastImage <FastImage
source={{ source={{
uri: bannerUrl, uri: bannerUrl,
@ -520,11 +530,11 @@ const AppleTVHero: React.FC<AppleTVHeroProps> = ({
resizeMode={FastImage.resizeMode.cover} resizeMode={FastImage.resizeMode.cover}
onLoad={() => setBannerLoaded((prev) => ({ ...prev, [currentIndex]: true }))} onLoad={() => setBannerLoaded((prev) => ({ ...prev, [currentIndex]: true }))}
/> />
</Animated.View> </View>
{/* Next/Preview Image */} {/* Next/Preview Image - Animated overlay during drag */}
{nextIndex !== currentIndex && ( {nextIndex !== currentIndex && (
<Animated.View style={[StyleSheet.absoluteFillObject, nextImageStyle]}> <Animated.View style={[styles.imageWrapperAbsolute, nextImageStyle]}>
<FastImage <FastImage
source={{ source={{
uri: nextBannerUrl, uri: nextBannerUrl,
@ -791,6 +801,20 @@ const styles = StyleSheet.create({
bottom: 0, bottom: 0,
zIndex: 1, zIndex: 1,
}, },
imageWrapper: {
position: 'absolute',
top: 0,
left: -50, // Extend 50px to left
right: -50, // Extend 50px to right
bottom: 0,
},
imageWrapperAbsolute: {
position: 'absolute',
top: 0,
left: -50, // Extend 50px to left
right: -50, // Extend 50px to right
bottom: 0,
},
backgroundImage: { backgroundImage: {
width: '100%', width: '100%',
height: '100%', height: '100%',