diff --git a/client/src/components/netflix-grid.tsx b/client/src/components/netflix-grid.tsx index 7ec0f50..4866da5 100644 --- a/client/src/components/netflix-grid.tsx +++ b/client/src/components/netflix-grid.tsx @@ -137,35 +137,55 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) { const [showLeftButton, setShowLeftButton] = useState(false); const [showRightButton, setShowRightButton] = useState(true); - const [currentIndex, setCurrentIndex] = useState(0); + const [translateX, setTranslateX] = useState(0); + const [isScrolling, setIsScrolling] = useState(false); const videosToShow = 5; // Show 5 videos at a time + const videoWidth = 120; // Width including spacing const scroll = (direction: 'left' | 'right') => { - const totalVideos = category.videos.length; - if (direction === 'right') { - // Move to next video in circular fashion: 1→2→3→...→10→1→2→... - setCurrentIndex(prev => (prev + 1) % totalVideos); - } else { - // Move to previous video in circular fashion: 1→10→9→...→2→1→10→... - setCurrentIndex(prev => prev === 0 ? totalVideos - 1 : prev - 1); - } + const step = direction === 'right' ? -videoWidth : videoWidth; + setTranslateX(prev => prev + step); }; const startAutoScroll = (direction: 'left' | 'right') => { - // Clear any existing interval + // Clear any existing animation if (scrollIntervalRef.current) { - clearInterval(scrollIntervalRef.current); + cancelAnimationFrame(scrollIntervalRef.current); } - // Start continuous circular scrolling - scrollIntervalRef.current = setInterval(() => { - scroll(direction); - }, 600); // Auto scroll every 600ms + setIsScrolling(true); + + // Continuous smooth flow - videos flow like a stream + const speed = direction === 'right' ? -2 : 2; // Pixels per frame + + const animate = () => { + setTranslateX(prev => { + const newX = prev + speed; + const totalWidth = category.videos.length * videoWidth; + + // Infinite loop - seamless reset + if (direction === 'right' && newX <= -totalWidth * 2) { + return -totalWidth; // Reset to middle section + } else if (direction === 'left' && newX >= 0) { + return -totalWidth; // Reset to middle section + } + + return newX; + }); + + if (isScrolling) { + scrollIntervalRef.current = requestAnimationFrame(animate); + } + }; + + animate(); }; - // Always start with video 1 visible at first position + // Initialize in middle section for infinite scroll useEffect(() => { - setCurrentIndex(0); // Start with video 1 at first position + if (category.videos.length > 0) { + setTranslateX(-category.videos.length * videoWidth); // Start in middle section + } }, [category.videos.length]); // Always show both buttons @@ -175,8 +195,9 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) { }, []); const stopAutoScroll = () => { + setIsScrolling(false); if (scrollIntervalRef.current) { - clearInterval(scrollIntervalRef.current); + cancelAnimationFrame(scrollIntervalRef.current); scrollIntervalRef.current = null; } }; @@ -270,34 +291,42 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) { - {/* Circular carousel - videos rotate in circle */} -
- {/* Show 5 videos in circular fashion */} - {Array.from({ length: videosToShow }, (_, index) => { - const videoIndex = (currentIndex + index) % category.videos.length; - const video = category.videos[videoIndex]; - return ( -
- {/* Top 10 Number overlay for first category */} - {category.title.includes("Top 10") && ( -
- {videoIndex + 1} -
- )} - -
- ); - })} + {/* Continuous flowing carousel - videos flow across entire width */} +
+
+ {/* Triple the videos for seamless infinite flow */} + {[...category.videos, ...category.videos, ...category.videos].map((video, index) => { + const actualIndex = index % category.videos.length; + return ( +
+ {/* Top 10 Number overlay for first category */} + {category.title.includes("Top 10") && ( +
+ {actualIndex + 1} +
+ )} + +
+ ); + })} +