Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 476c2e7

Browse files
committed
update
1 parent 18830f6 commit 476c2e7

File tree

1 file changed

+70
-4
lines changed

1 file changed

+70
-4
lines changed

‎src/components/Testimonial.jsx

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
import React, { useRef, useEffect } from 'react';
1+
import React, { useRef, useEffect,useState } from 'react';
22

33
const Testimonial = () => {
44
const scrollRef = useRef(null);
5+
const [isPaused, setIsPaused] = useState(false);
6+
const [currentIndex, setCurrentIndex] = useState(0);
57

68
useEffect(() => {
79
const scrollContainer = scrollRef.current;
810
let isDown = false;
911
let startX;
1012
let scrollLeft;
13+
let autoScrollInterval;
14+
let pauseTimeout;
1115

1216
const handleMouseDown = (e) => {
1317
isDown = true;
18+
setIsPaused(true);
1419
scrollContainer.classList.add('active');
1520
startX = e.pageX - scrollContainer.offsetLeft;
1621
scrollLeft = scrollContainer.scrollLeft;
@@ -19,11 +24,17 @@ const Testimonial = () => {
1924
const handleMouseLeave = () => {
2025
isDown = false;
2126
scrollContainer.classList.remove('active');
27+
// Resume auto-scroll after 2 seconds of no interaction
28+
clearTimeout(pauseTimeout);
29+
pauseTimeout = setTimeout(() => setIsPaused(false), 2000);
2230
};
2331

2432
const handleMouseUp = () => {
2533
isDown = false;
2634
scrollContainer.classList.remove('active');
35+
// Resume auto-scroll after 2 seconds of no interaction
36+
clearTimeout(pauseTimeout);
37+
pauseTimeout = setTimeout(() => setIsPaused(false), 2000);
2738
};
2839

2940
const handleMouseMove = (e) => {
@@ -34,11 +45,43 @@ const Testimonial = () => {
3445
scrollContainer.scrollLeft = scrollLeft - walk;
3546
};
3647

48+
const handleScroll = () => {
49+
if (!scrollContainer) return;
50+
setIsPaused(true);
51+
clearTimeout(pauseTimeout);
52+
pauseTimeout = setTimeout(() => setIsPaused(false), 2000);
53+
};
54+
55+
const autoScroll = () => {
56+
if (!scrollContainer || isPaused) return;
57+
58+
const cardWidth = 400; // Width of each testimonial card
59+
const gap = 32; // Gap between cards (8 * 4 = 32px from gap-8)
60+
const totalWidth = scrollContainer.scrollWidth;
61+
62+
setCurrentIndex((prevIndex) => {
63+
const nextIndex = (prevIndex + 1) % 4; // 4 is the number of testimonials
64+
const scrollPosition = (cardWidth + gap) * nextIndex;
65+
66+
scrollContainer.scrollTo({
67+
left: scrollPosition,
68+
behavior: 'smooth'
69+
});
70+
71+
return nextIndex;
72+
});
73+
};
74+
75+
// Set up event listeners
3776
if (scrollContainer) {
3877
scrollContainer.addEventListener('mousedown', handleMouseDown);
3978
scrollContainer.addEventListener('mouseleave', handleMouseLeave);
4079
scrollContainer.addEventListener('mouseup', handleMouseUp);
4180
scrollContainer.addEventListener('mousemove', handleMouseMove);
81+
scrollContainer.addEventListener('scroll', handleScroll);
82+
83+
// Start auto-scrolling
84+
autoScrollInterval = setInterval(autoScroll, 5000); // Scroll every 5 seconds
4285
}
4386

4487
return () => {
@@ -47,9 +90,12 @@ const Testimonial = () => {
4790
scrollContainer.removeEventListener('mouseleave', handleMouseLeave);
4891
scrollContainer.removeEventListener('mouseup', handleMouseUp);
4992
scrollContainer.removeEventListener('mousemove', handleMouseMove);
93+
scrollContainer.removeEventListener('scroll', handleScroll);
5094
}
95+
clearInterval(autoScrollInterval);
96+
clearTimeout(pauseTimeout);
5197
};
52-
}, []);
98+
}, [isPaused]);
5399

54100
return (
55101
<section className="py-16 bg-gradient-to-r from-gray-50 to-gray-100 overflow-hidden">
@@ -167,11 +213,31 @@ const Testimonial = () => {
167213
</div>
168214
</div>
169215

170-
{/* Scroll Indicator */}
171-
<div className="mt-8 flex justify-center">
216+
{/* Scroll Indicator and Navigation Dots */}
217+
<div className="mt-8 flex flex-col items-center gap-4">
172218
<div className="text-sm text-gray-500">
173219
← Scroll or drag to see more testimonials →
174220
</div>
221+
<div className="flex gap-2">
222+
{[0, 1, 2, 3].map((index) => (
223+
<button
224+
key={index}
225+
className={`w-2 h-2 rounded-full transition-all duration-300 ${
226+
currentIndex === index ? 'bg-gray-800 w-4' : 'bg-gray-300'
227+
}`}
228+
onClick={() => {
229+
setIsPaused(true);
230+
setCurrentIndex(index);
231+
scrollRef.current?.scrollTo({
232+
left: index * (400 + 32),
233+
behavior: 'smooth'
234+
});
235+
setTimeout(() => setIsPaused(false), 2000);
236+
}}
237+
aria-label={`Go to testimonial ${index + 1}`}
238+
/>
239+
))}
240+
</div>
175241
</div>
176242
</div>
177243

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /