Make video rows flow continuously and smoothly like water

Update the Netflix grid component to use CSS transforms for smooth, continuous scrolling animations instead of incremental index changes, improving the visual experience of video rows.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 2eb1084e-b728-4449-9231-f1665924c8d5
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2eb1084e-b728-4449-9231-f1665924c8d5/QCN70f2
This commit is contained in:
sebastjanartic 2025-08-29 15:05:04 +00:00
parent bc53d99ff0
commit c85660e529

View File

@ -137,17 +137,14 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
const [showLeftButton, setShowLeftButton] = useState(false); const [showLeftButton, setShowLeftButton] = useState(false);
const [showRightButton, setShowRightButton] = useState(true); 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 videosToShow = 5; // Show 5 videos at a time
const videoWidth = 120; // Width + spacing
const scroll = (direction: 'left' | 'right') => { const scroll = (direction: 'left' | 'right') => {
if (direction === 'right') { const speed = direction === 'right' ? videoWidth : -videoWidth;
// Always move forward in the circle - perpetual motion setTranslateX(prev => prev - speed);
setCurrentIndex(prev => prev + 1);
} else {
// For left arrow, also move forward but faster to create illusion of reverse
setCurrentIndex(prev => prev + 1);
}
}; };
const startAutoScroll = (direction: 'left' | 'right') => { const startAutoScroll = (direction: 'left' | 'right') => {
@ -156,29 +153,41 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
clearInterval(scrollIntervalRef.current); clearInterval(scrollIntervalRef.current);
} }
// Both directions move forward - true perpetual motion carousel setIsScrolling(true);
scrollIntervalRef.current = setInterval(() => {
setCurrentIndex(prev => prev + 1); // Continuous smooth flowing animation
}, direction === 'left' ? 600 : 800); // Left moves slightly faster for effect const speed = direction === 'right' ? 2 : -2; // pixels per frame
const animate = () => {
setTranslateX(prev => {
const newValue = prev - speed;
const totalWidth = category.videos.length * videoWidth;
// Reset position for infinite loop
if (direction === 'right' && newValue <= -totalWidth) {
return 0;
} else if (direction === 'left' && newValue >= 0) {
return -totalWidth + videoWidth;
}
return newValue;
});
if (isScrolling) {
scrollIntervalRef.current = requestAnimationFrame(animate);
}
};
animate();
}; };
// Initialize in middle section for smooth infinite scroll // Initialize starting position
useEffect(() => { useEffect(() => {
if (category.videos.length > 0) { if (category.videos.length > 0) {
setCurrentIndex(category.videos.length); // Start in middle section for smooth wrapping setTranslateX(-category.videos.length * videoWidth); // Start in middle section
} }
}, [category.videos.length]); }, [category.videos.length]);
// Handle seamless infinite scroll - only forward movement
useEffect(() => {
const totalVideos = category.videos.length;
if (currentIndex >= totalVideos * 2) {
// When we reach the end, seamlessly jump back to start of middle section
// This creates the illusion of infinite forward movement
setCurrentIndex(currentIndex - totalVideos);
}
}, [currentIndex, category.videos.length]);
// Always show both buttons // Always show both buttons
useEffect(() => { useEffect(() => {
setShowLeftButton(true); setShowLeftButton(true);
@ -186,8 +195,9 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
}, []); }, []);
const stopAutoScroll = () => { const stopAutoScroll = () => {
setIsScrolling(false);
if (scrollIntervalRef.current) { if (scrollIntervalRef.current) {
clearInterval(scrollIntervalRef.current); cancelAnimationFrame(scrollIntervalRef.current);
scrollIntervalRef.current = null; scrollIntervalRef.current = null;
} }
}; };
@ -281,13 +291,14 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
<ChevronRight className="w-5 h-5 text-white" /> <ChevronRight className="w-5 h-5 text-white" />
</button> </button>
{/* Smooth sliding carousel - videos move like geese one by one */} {/* Continuously flowing carousel - videos flow like water */}
<div className="overflow-hidden"> <div className="overflow-hidden">
<div <div
className="flex space-x-2 md:space-x-3 pb-4 px-4 md:px-0 transition-transform duration-500 ease-in-out" className="flex space-x-2 md:space-x-3 pb-4 px-4 md:px-0"
style={{ style={{
transform: `translateX(-${currentIndex * (112 + 8)}px)`, // Mobile: 112px width + 8px spacing transform: `translateX(${translateX}px)`,
willChange: 'transform' willChange: 'transform',
transition: isScrolling ? 'none' : 'transform 0.3s ease-out'
}} }}
> >
{/* Create infinite loop by tripling the videos */} {/* Create infinite loop by tripling the videos */}