1
- import { useEffect , useState } from "react" ;
1
+ import { useEffect , useState , useRef , useCallback } from "react" ;
2
2
3
3
import { BsChevronLeft , BsChevronRight } from "react-icons/bs" ;
4
4
import { RxDotFilled } from "react-icons/rx" ;
5
5
6
6
import person01 from "../../assets/Images/person01.png" ;
7
7
import person02 from "../../assets/Images/person02.png" ;
8
8
import person03 from "../../assets/Images/person03.png" ;
9
- import testimonialbg from "../../assets/Images/testimonialbg.png " ;
9
+ import testimonialbg from "../../assets/Images/testimonialbg.jpg " ;
10
10
11
11
const slides = [
12
12
{
@@ -31,20 +31,58 @@ const slides = [
31
31
32
32
const Testimonial = ( ) => {
33
33
const [ slide , setSlide ] = useState ( 0 ) ;
34
+ const [ mousePosition , setMousePosition ] = useState ( { x : 0 , y : 0 } ) ;
35
+ const testimonialRef = useRef ( null ) ;
36
+
37
+ const handleRightSlide = useCallback ( ( ) => {
38
+ const isLastSlide = slide === slides . length - 1 ;
39
+ const nextSlide = isLastSlide ? 0 : slide + 1 ;
40
+ setSlide ( nextSlide ) ;
41
+ } , [ slide ] ) ;
34
42
35
43
useEffect ( ( ) => {
36
44
const interval = setInterval ( ( ) => {
37
45
handleRightSlide ( ) ;
38
46
} , 3000 ) ;
39
47
40
48
return ( ) => clearInterval ( interval ) ;
41
- } , [ slide ] ) ;
49
+ } , [ handleRightSlide ] ) ;
50
+
51
+ useEffect ( ( ) => {
52
+ const handleMouseMove = ( e ) => {
53
+ if ( testimonialRef . current ) {
54
+ const rect = testimonialRef . current . getBoundingClientRect ( ) ;
55
+ // Calculate mouse position relative to the component
56
+ const x = e . clientX - rect . left ;
57
+ const y = e . clientY - rect . top ;
58
+ setMousePosition ( { x, y } ) ;
59
+ }
60
+ } ;
61
+
62
+ window . addEventListener ( "mousemove" , handleMouseMove ) ;
63
+ return ( ) => {
64
+ window . removeEventListener ( "mousemove" , handleMouseMove ) ;
65
+ } ;
66
+ } , [ ] ) ;
67
+
68
+ // Calculate parallax values
69
+ const calculateParallax = ( strength = 20 ) => {
70
+ if ( ! testimonialRef . current ) return { x : 0 , y : 0 } ;
71
+
72
+ const rect = testimonialRef . current . getBoundingClientRect ( ) ;
73
+ const centerX = rect . width / 2 ;
74
+ const centerY = rect . height / 2 ;
75
+
76
+ // Calculate offset from center (normalized between -1 and 1)
77
+ const offsetX = ( mousePosition . x - centerX ) / centerX ;
78
+ const offsetY = ( mousePosition . y - centerY ) / centerY ;
79
+
80
+ return {
81
+ x : offsetX * strength ,
82
+ y : offsetY * strength ,
83
+ } ;
84
+ } ;
42
85
43
- function handleRightSlide ( ) {
44
- const isLastSlide = slide === slides . length - 1 ;
45
- const nextSlide = isLastSlide ? 0 : slide + 1 ;
46
- setSlide ( nextSlide ) ;
47
- }
48
86
function handleLeftSlide ( ) {
49
87
const isFirstSlide = slide === 0 ;
50
88
const newSlide = isFirstSlide ? slides . length - 1 : slide - 1 ;
@@ -54,40 +92,74 @@ const Testimonial = () => {
54
92
function goToSlide ( slideIndex ) {
55
93
setSlide ( slideIndex ) ;
56
94
}
95
+
96
+ const parallax = calculateParallax ( ) ;
97
+
57
98
return (
58
99
< div
59
- className = " testimonial max-w-[1440px] h-[780px] m-auto py-16 px-4 relative group"
100
+ ref = { testimonialRef }
101
+ className = "testimonial max-w-[1440px] h-[700px] m-auto py-16 px-4 relative group overflow-hidden"
60
102
style = { { backgroundImage : `url(${ testimonialbg } )` } }
61
103
>
62
- < h2 className = "text-4xl font-bold tracking-wider text-textWhite mb-[2rem] text-center font-monsterrat" >
104
+ < h2
105
+ className = "text-4xl font-bold tracking-wider text-textWhite mb-[2rem] text-center font-monsterrat"
106
+ style = { {
107
+ transform : `translate(${ parallax . x * - 0.5 } px, ${ parallax . y * - 0.5 } px)` ,
108
+ transition : "transform 0.1s ease-out" ,
109
+ } }
110
+ >
63
111
Testimonials
64
112
</ h2 >
65
- < div className = "flex justify-center items-center gap-4 sm:gap-20" >
66
- < div className = "flex justify-center items-center w-40 h-40 sm:w-40 sm:h-40 rounded-full overflow-hidden py-3 bg-center bg-cover duration-500" >
113
+ < div
114
+ className = "flex justify-center items-center gap-4 sm:gap-20"
115
+ style = { {
116
+ transform : `translate(${ parallax . x * - 1 } px, ${ parallax . y * - 1 } px)` ,
117
+ transition : "transform 0.1s ease-out" ,
118
+ } }
119
+ >
120
+ < div
121
+ className = "flex justify-center items-center w-40 h-40 sm:w-40 sm:h-40 rounded-full overflow-hidden py-3 bg-center bg-cover duration-500"
122
+ style = { {
123
+ transform : `translate(${ parallax . x * 2 } px, ${ parallax . y * 1.2 } px)` ,
124
+ transition : "transform 0.1s ease-out" ,
125
+ } }
126
+ >
67
127
< img
68
128
src = { slides [ slide - 1 < 0 ? slides . length - 1 : slide - 1 ] . url }
69
129
alt = "image"
70
- style = { { marginBottom : '0' } }
71
- /*
72
- Warning: A 60px margin is applied to all images in home.css,
73
- causing layout issues. Remove if unnecessary,
74
- or use overrides for specific components.
75
- That's what I did overrided specific components becouse I don't know it that home.css code is needed or not.
76
- */
77
-
130
+ style = { { marginBottom : "0" } }
78
131
/>
79
132
</ div >
80
- < div className = "flex justify-center items-center w-56 h-56 sm:w-96 sm:h-96 rounded-full overflow-hidden py-3 bg-center bg-cover duration-500 hover:scale-110 cursor-pointer" >
133
+ < div
134
+ className = "flex justify-center items-center w-56 h-56 sm:w-96 sm:h-96 rounded-full overflow-hidden py-3 bg-center bg-cover duration-500 hover:scale-110 cursor-pointer"
135
+ style = { {
136
+ transform : `translate(${ parallax . x * - 1.5 } px, ${ parallax . y * - 1.5 } px)` ,
137
+ transition : "transform 0.1s ease-out, scale 0.3s ease" ,
138
+ } }
139
+ >
81
140
< img src = { slides [ slide ] . url } alt = "image" />
82
141
</ div >
83
- < div className = "flex justify-center items-center w-40 h-40 sm:w-40 sm:h-40 rounded-full overflow-hidden py-3 bg-center bg-cover duration-500" >
84
- < img src = { slides [ slide + 1 > slides . length - 1 ? 0 : slide + 1 ] . url } alt = "image"
85
- style = { { marginBottom : '0' } }
86
- />
142
+ < div
143
+ className = "flex justify-center items-center w-40 h-40 sm:w-40 sm:h-40 rounded-full overflow-hidden py-3 bg-center bg-cover duration-500"
144
+ style = { {
145
+ transform : `translate(${ parallax . x * 2 } px, ${ parallax . y * 1.2 } px)` ,
146
+ transition : "transform 0.1s ease-out" ,
147
+ } }
148
+ >
149
+ < img
150
+ src = { slides [ slide + 1 > slides . length - 1 ? 0 : slide + 1 ] . url }
151
+ alt = "image"
152
+ style = { { marginBottom : "0" } }
153
+ />
87
154
</ div >
88
-
89
155
</ div >
90
- < div className = "flex flex-col mx-auto w-[70%] justify-center text-center text-textWhite font-medium" >
156
+ < div
157
+ className = "flex flex-col mx-auto w-[70%] justify-center text-center text-textWhite font-medium"
158
+ style = { {
159
+ transform : `translate(${ parallax . x * - 0.8 } px, ${ parallax . y * - 0.8 } px)` ,
160
+ transition : "transform 0.1s ease-out" ,
161
+ } }
162
+ >
91
163
< h2 > { slides [ slide ] . name } </ h2 >
92
164
< p className = "py-4" > { slides [ slide ] . paragraph } </ p >
93
165
</ div >
@@ -104,7 +176,13 @@ const Testimonial = () => {
104
176
>
105
177
< BsChevronRight size = { 30 } />
106
178
</ div >
107
- < div className = "flex justify-center items-center py-2" >
179
+ < div
180
+ className = "flex justify-center items-center py-2"
181
+ style = { {
182
+ transform : `translate(${ parallax . x * - 0.3 } px, ${ parallax . y * - 0.3 } px)` ,
183
+ transition : "transform 0.1s ease-out" ,
184
+ } }
185
+ >
108
186
{ slides . map ( ( singleSlide , slideIndex ) => (
109
187
< div
110
188
key = { slideIndex }
0 commit comments