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:
parent
bc53d99ff0
commit
c85660e529
@ -137,17 +137,14 @@ 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 + spacing
|
||||
|
||||
const scroll = (direction: 'left' | 'right') => {
|
||||
if (direction === 'right') {
|
||||
// Always move forward in the circle - perpetual motion
|
||||
setCurrentIndex(prev => prev + 1);
|
||||
} else {
|
||||
// For left arrow, also move forward but faster to create illusion of reverse
|
||||
setCurrentIndex(prev => prev + 1);
|
||||
}
|
||||
const speed = direction === 'right' ? videoWidth : -videoWidth;
|
||||
setTranslateX(prev => prev - speed);
|
||||
};
|
||||
|
||||
const startAutoScroll = (direction: 'left' | 'right') => {
|
||||
@ -156,29 +153,41 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
|
||||
clearInterval(scrollIntervalRef.current);
|
||||
}
|
||||
|
||||
// Both directions move forward - true perpetual motion carousel
|
||||
scrollIntervalRef.current = setInterval(() => {
|
||||
setCurrentIndex(prev => prev + 1);
|
||||
}, direction === 'left' ? 600 : 800); // Left moves slightly faster for effect
|
||||
setIsScrolling(true);
|
||||
|
||||
// Continuous smooth flowing animation
|
||||
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(() => {
|
||||
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]);
|
||||
|
||||
// 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
|
||||
useEffect(() => {
|
||||
setShowLeftButton(true);
|
||||
@ -186,8 +195,9 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
|
||||
}, []);
|
||||
|
||||
const stopAutoScroll = () => {
|
||||
setIsScrolling(false);
|
||||
if (scrollIntervalRef.current) {
|
||||
clearInterval(scrollIntervalRef.current);
|
||||
cancelAnimationFrame(scrollIntervalRef.current);
|
||||
scrollIntervalRef.current = null;
|
||||
}
|
||||
};
|
||||
@ -281,13 +291,14 @@ function CategoryRow({ category, onVideoClick }: CategoryRowProps) {
|
||||
<ChevronRight className="w-5 h-5 text-white" />
|
||||
</button>
|
||||
|
||||
{/* Smooth sliding carousel - videos move like geese one by one */}
|
||||
{/* Continuously flowing carousel - videos flow like water */}
|
||||
<div className="overflow-hidden">
|
||||
<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={{
|
||||
transform: `translateX(-${currentIndex * (112 + 8)}px)`, // Mobile: 112px width + 8px spacing
|
||||
willChange: 'transform'
|
||||
transform: `translateX(${translateX}px)`,
|
||||
willChange: 'transform',
|
||||
transition: isScrolling ? 'none' : 'transform 0.3s ease-out'
|
||||
}}
|
||||
>
|
||||
{/* Create infinite loop by tripling the videos */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user