diff --git a/src/pages/HomePage/sections/Testimonials.tsx b/src/pages/HomePage/sections/Testimonials.tsx index d5f91f5..c15f520 100644 --- a/src/pages/HomePage/sections/Testimonials.tsx +++ b/src/pages/HomePage/sections/Testimonials.tsx @@ -5,15 +5,67 @@ import TextAnimation from "@/components/ui/TextAnimation"; import ImageOrVideo from "@/components/ui/ImageOrVideo"; import ScrollReveal from "@/components/ui/ScrollReveal"; import { BadgeCheck } from "lucide-react"; -import { useRef, useState } from "react"; +import { useRef, useState, useEffect } from "react"; const DraggableMarqueeRow = ({ items, duration, reverse = false }: { items: any[], duration: string, reverse?: boolean }) => { const containerRef = useRef(null); + const trackRef = useRef(null); + const isInitialized = useRef(false); + const [isHovered, setIsHovered] = useState(false); const [isDragging, setIsDragging] = useState(false); const [startX, setStartX] = useState(0); const [scrollLeft, setScrollLeft] = useState(0); + useEffect(() => { + let animationFrameId: number; + let lastTime = performance.now(); + + const durationMs = parseFloat(duration) * 1000; + + const animate = (time: number) => { + const deltaTime = time - lastTime; + lastTime = time; + + if (containerRef.current && trackRef.current) { + const container = containerRef.current as any; + const track = trackRef.current as any; + const singleCopyWidth = track.scrollWidth / 4; + + if (singleCopyWidth > 0) { + if (!isInitialized.current) { + if (reverse) { + container.scrollLeft = singleCopyWidth * 2; + } + isInitialized.current = true; + } + + if (!isHovered && !isDragging) { + const speed = singleCopyWidth / durationMs; + const delta = speed * deltaTime; + + if (reverse) { + container.scrollLeft -= delta; + if (container.scrollLeft <= 0) { + container.scrollLeft += singleCopyWidth * 2; + } + } else { + container.scrollLeft += delta; + if (container.scrollLeft >= singleCopyWidth * 2) { + container.scrollLeft -= singleCopyWidth * 2; + } + } + } + } + } + + animationFrameId = requestAnimationFrame(animate); + }; + + animationFrameId = requestAnimationFrame(animate); + return () => cancelAnimationFrame(animationFrameId); + }, [isHovered, isDragging, duration, reverse]); + const handleMouseDown = (e: React.MouseEvent) => { if (!containerRef.current) return; setIsDragging(true); @@ -31,11 +83,27 @@ const DraggableMarqueeRow = ({ items, duration, reverse = false }: { items: any[ }; const handleMouseMove = (e: React.MouseEvent) => { - if (!isDragging || !containerRef.current) return; + if (!isDragging || !containerRef.current || !trackRef.current) return; e.preventDefault(); const x = e.pageX - (containerRef.current as any).offsetLeft; const walk = (x - startX) * 2; - (containerRef.current as any).scrollLeft = scrollLeft - walk; + + let newScrollLeft = scrollLeft - walk; + const singleCopyWidth = (trackRef.current as any).scrollWidth / 4; + + if (singleCopyWidth > 0) { + if (newScrollLeft <= 0) { + newScrollLeft += singleCopyWidth * 2; + setStartX(e.pageX - (containerRef.current as any).offsetLeft); + setScrollLeft(newScrollLeft + walk); + } else if (newScrollLeft >= singleCopyWidth * 2) { + newScrollLeft -= singleCopyWidth * 2; + setStartX(e.pageX - (containerRef.current as any).offsetLeft); + setScrollLeft(newScrollLeft + walk); + } + } + + (containerRef.current as any).scrollLeft = newScrollLeft; }; return ( @@ -47,11 +115,11 @@ const DraggableMarqueeRow = ({ items, duration, reverse = false }: { items: any[ onMouseDown={handleMouseDown} onMouseUp={handleMouseUp} onMouseMove={handleMouseMove} - style={{ overflowX: isHovered ? 'auto' : 'hidden', scrollbarWidth: 'none', msOverflowStyle: 'none' }} + style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }} >
{[...items, ...items, ...items, ...items].map((item, i) => (