1
- import React , { useState , useRef , TouchEvent } from 'react' ;
1
+ import React , { useState , useRef , TouchEvent , useEffect , useCallback } from 'react' ;
2
2
3
3
interface AppProps {
4
4
originalImageSrc : string ;
@@ -7,107 +7,214 @@ interface AppProps {
7
7
sliderColor : string ;
8
8
}
9
9
10
- function ImageComparisonSlider ( {
11
- originalImageSrc,
12
- upscaledImageSrc,
13
- sliderPosition,
14
- sliderColor,
15
- } : AppProps ) {
16
- const [ imageRevealFraction , setImageRevealFraction ] = useState ( sliderPosition ) ;
17
- const imageContainer = useRef < HTMLDivElement > ( null ) ;
18
-
19
- const slide = ( xPosition : number ) : void => {
20
- const containerBoundingRect = imageContainer . current ?. getBoundingClientRect ( ) ;
21
-
22
- if ( containerBoundingRect ) {
23
- setImageRevealFraction ( ( ) => {
24
- if ( xPosition < containerBoundingRect . left ) {
25
- return 0 ;
26
- } else if ( xPosition > containerBoundingRect . right ) {
27
- return 1 ;
28
- }
29
- return ( xPosition - containerBoundingRect . left ) / containerBoundingRect . width ;
30
- } ) ;
31
- }
32
- } ;
33
-
34
- const handleTouchMove = ( event : TouchEvent < HTMLDivElement > ) : void => {
35
- slide ( event . touches . item ( 0 ) ?. clientX || 0 ) ;
36
- } ;
37
-
38
- const handleMouseDown = ( ) : void => {
39
- window . onmousemove = handleMouseMoveMove ;
40
- window . onmouseup = handleMouseUp ;
41
- } ;
42
-
43
- const handleMouseMoveMove = ( event : MouseEvent ) : void => {
44
- slide ( event . clientX ) ;
45
- } ;
46
-
47
- const handleMouseUp = ( ) : void => {
48
- window . onmousemove = null ;
49
- window . onmouseup = null ;
50
- } ;
51
-
52
- return (
53
- < div className = 'px4' >
54
- < div
55
- ref = { imageContainer }
56
- className = 'max-w-lg w-full mx-auto mt-32 relative select-none group'
57
- >
58
- < img src = { originalImageSrc } alt = 'Original' className = 'pointer-events-none' />
59
- < img
60
- style = { {
61
- filter : 'grayscale(100%)' ,
62
- clipPath : `polygon(0 0, ${ imageRevealFraction * 100 } % 0, ${
63
- imageRevealFraction * 100
64
- } % 100%, 0% 100%)`,
65
- } }
66
- src = { upscaledImageSrc }
67
- alt = 'Upscaled'
68
- className = 'absolute inset-0 pointer-events-none'
69
- />
10
+ const ImageComparisonSlider = React . memo (
11
+ ( { originalImageSrc, upscaledImageSrc, sliderPosition, sliderColor } : AppProps ) => {
12
+ const [ imageRevealFraction , setImageRevealFraction ] = useState ( sliderPosition ) ;
13
+ const imageContainer = useRef < HTMLDivElement > ( null ) ;
70
14
15
+ const slide = useCallback ( ( xPosition : number ) : void => {
16
+ const containerBoundingRect = imageContainer . current ?. getBoundingClientRect ( ) ;
17
+
18
+ if ( containerBoundingRect ) {
19
+ setImageRevealFraction ( ( ) => {
20
+ if ( xPosition < containerBoundingRect . left ) {
21
+ return 0 ;
22
+ } else if ( xPosition > containerBoundingRect . right ) {
23
+ return 1 ;
24
+ }
25
+ return ( xPosition - containerBoundingRect . left ) / containerBoundingRect . width ;
26
+ } ) ;
27
+ }
28
+ } , [ ] ) ;
29
+
30
+ const handleTouchMove = useCallback (
31
+ ( event : TouchEvent < HTMLDivElement > ) : void => {
32
+ slide ( event . touches . item ( 0 ) ?. clientX || 0 ) ;
33
+ } ,
34
+ [ slide ]
35
+ ) ;
36
+
37
+ const handleMouseMoveMove = useCallback (
38
+ ( event : MouseEvent ) : void => {
39
+ slide ( event . clientX ) ;
40
+ } ,
41
+ [ slide ]
42
+ ) ;
43
+
44
+ const handleMouseUp = useCallback ( ( ) : void => {
45
+ window . onmousemove = null ;
46
+ window . onmouseup = null ;
47
+ } , [ ] ) ;
48
+
49
+ const handleMouseDown = useCallback ( ( ) : void => {
50
+ window . onmousemove = handleMouseMoveMove ;
51
+ window . onmouseup = handleMouseUp ;
52
+ } , [ handleMouseMoveMove , handleMouseUp ] ) ;
53
+
54
+ useEffect ( ( ) => {
55
+ return ( ) => {
56
+ window . onmousemove = null ;
57
+ window . onmouseup = null ;
58
+ } ;
59
+ } , [ ] ) ;
60
+
61
+ return (
62
+ < div className = 'px4' >
71
63
< div
72
- style = { { left : ` ${ imageRevealFraction * 100 } %` } }
73
- className = 'absolute inset-0 group-hover:opacity-0 group-hover:opacity-100 transition-opacity duration-300 opacity-0 '
64
+ ref = { imageContainer }
65
+ className = 'max-w-lg w-full mx-auto mt-32 relative select-none group '
74
66
>
75
- < div className = 'relative h-full' >
76
- < div
77
- className = 'absolute inset-0'
78
- style = { {
79
- backgroundColor : sliderColor ,
80
- width : '0.5px' ,
81
- marginLeft : '-1px' ,
82
- opacity : '50' ,
83
- } }
84
- > </ div >
85
- < div
86
- style = { { touchAction : 'none' } }
87
- onMouseDown = { handleMouseDown }
88
- onTouchMove = { handleTouchMove }
89
- className = 'h-12 w-12 -ml-6 -mt-6 rounded-full bg-white absolute top-1/2'
90
- >
91
- < svg
92
- xmlns = 'http://www.w3.org/2000/svg'
93
- fill = 'none'
94
- viewBox = '0 0 24 24'
95
- strokeWidth = { 1.5 }
96
- stroke = 'currentColor'
97
- className = 'w-6 h-6 text-gray-800 rotate-90 cursor-pointer absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2'
67
+ < img src = { originalImageSrc } alt = 'Original' className = 'pointer-events-none' />
68
+ < img
69
+ style = { {
70
+ filter : 'grayscale(100%)' ,
71
+ clipPath : `polygon(0 0, ${ imageRevealFraction * 100 } % 0, ${
72
+ imageRevealFraction * 100
73
+ } % 100%, 0% 100%)`,
74
+ } }
75
+ src = { upscaledImageSrc }
76
+ alt = 'Upscaled'
77
+ className = 'absolute inset-0 pointer-events-none'
78
+ />
79
+
80
+ < div
81
+ style = { { left : `${ imageRevealFraction * 100 } %` } }
82
+ className = 'absolute inset-0 group-hover:opacity-0 group-hover:opacity-100 transition-opacity duration-300 opacity-0'
83
+ >
84
+ < div className = 'relative h-full' >
85
+ < div
86
+ className = 'absolute inset-0'
87
+ style = { {
88
+ backgroundColor : sliderColor ,
89
+ width : '0.5px' ,
90
+ marginLeft : '-1px' ,
91
+ opacity : '50' ,
92
+ } }
93
+ > </ div >
94
+ < div
95
+ style = { { touchAction : 'none' } }
96
+ onMouseDown = { handleMouseDown }
97
+ onTouchMove = { handleTouchMove }
98
+ className = 'h-12 w-12 -ml-6 -mt-6 rounded-full bg-white absolute top-1/2'
98
99
>
99
- < path
100
- strokeLinecap = 'round'
101
- strokeLinejoin = 'round'
102
- d = 'M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9'
103
- />
104
- </ svg >
100
+ < svg
101
+ xmlns = 'http://www.w3.org/2000/svg'
102
+ fill = 'none'
103
+ viewBox = '0 0 24 24'
104
+ strokeWidth = { 1.5 }
105
+ stroke = 'currentColor'
106
+ className = 'w-6 h-6 text-gray-800 rotate-90 cursor-pointer absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2'
107
+ >
108
+ < path
109
+ strokeLinecap = 'round'
110
+ strokeLinejoin = 'round'
111
+ d = 'M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9'
112
+ />
113
+ //{ ' ' }
114
+ </ svg >
115
+ </ div >
105
116
</ div >
106
117
</ div >
107
118
</ div >
108
119
</ div >
109
- </ div >
110
- ) ;
111
- }
120
+ ) ;
121
+ }
122
+ ) ;
112
123
113
124
export default ImageComparisonSlider ;
125
+
126
+ // function ImageComparisonSlider({ originalImageSrc, upscaledImageSrc, sliderPosition, sliderColor }: AppProps) {
127
+ // const [imageRevealFraction, setImageRevealFraction] = useState(sliderPosition);
128
+ // const imageContainer = useRef<HTMLDivElement>(null);
129
+
130
+ // const slide = (xPosition: number): void => {
131
+ // const containerBoundingRect = imageContainer.current?.getBoundingClientRect();
132
+
133
+ // if (containerBoundingRect) {
134
+ // setImageRevealFraction(() => {
135
+ // if (xPosition < containerBoundingRect.left) {
136
+ // return 0;
137
+ // } else if (xPosition > containerBoundingRect.right) {
138
+ // return 1;
139
+ // }
140
+ // return (xPosition - containerBoundingRect.left) / containerBoundingRect.width;
141
+ // });
142
+ // }
143
+ // };
144
+
145
+ // const handleTouchMove = (event: TouchEvent<HTMLDivElement>): void => {
146
+ // slide(event.touches.item(0)?.clientX || 0);
147
+ // };
148
+
149
+ // const handleMouseDown = (): void => {
150
+ // window.onmousemove = handleMouseMoveMove;
151
+ // window.onmouseup = handleMouseUp;
152
+ // };
153
+
154
+ // const handleMouseMoveMove = (event: MouseEvent): void => {
155
+ // slide(event.clientX);
156
+ // };
157
+
158
+ // const handleMouseUp = (): void => {
159
+ // window.onmousemove = null;
160
+ // window.onmouseup = null;
161
+ // };
162
+
163
+ // return (
164
+ // <div className='px4'>
165
+ // <div
166
+ // ref={imageContainer}
167
+ // className='max-w-lg w-full mx-auto mt-32 relative select-none group'
168
+ // >
169
+ // <img src={originalImageSrc} alt='Original' className='pointer-events-none' />
170
+ // <img
171
+ // style={{
172
+ // filter: 'grayscale(100%)',
173
+ // clipPath: `polygon(0 0, ${imageRevealFraction * 100}% 0, ${imageRevealFraction * 100}% 100%, 0% 100%)`,
174
+ // }}
175
+ // src={upscaledImageSrc}
176
+ // alt='Upscaled'
177
+ // className='absolute inset-0 pointer-events-none'
178
+ // />
179
+
180
+ // <div
181
+ // style={{ left: `${imageRevealFraction * 100}%` }}
182
+ // className='absolute inset-0 group-hover:opacity-0 group-hover:opacity-100 transition-opacity duration-300 opacity-0'
183
+ // >
184
+ // <div className='relative h-full'>
185
+ // <div
186
+ // className='absolute inset-0'
187
+ // style={{
188
+ // backgroundColor: sliderColor,
189
+ // width: '0.5px',
190
+ // marginLeft: '-1px',
191
+ // opacity: '50',
192
+ // }}
193
+ // ></div>
194
+ // <div
195
+ // style={{ touchAction: 'none' }}
196
+ // onMouseDown={handleMouseDown}
197
+ // onTouchMove={handleTouchMove}
198
+ // className='h-12 w-12 -ml-6 -mt-6 rounded-full bg-white absolute top-1/2'
199
+ // >
200
+ // <svg
201
+ // xmlns='http://www.w3.org/2000/svg'
202
+ // fill='none'
203
+ // viewBox='0 0 24 24'
204
+ // strokeWidth={1.5}
205
+ // stroke='currentColor'
206
+ // className='w-6 h-6 text-gray-800 rotate-90 cursor-pointer absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2'
207
+ // >
208
+ // <path
209
+ // strokeLinecap='round'
210
+ // strokeLinejoin='round'
211
+ // d='M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9'
212
+ // />
213
+ // </svg>
214
+ // </div>
215
+ // </div>
216
+ // </div>
217
+ // </div>
218
+ // </div>
219
+ // );
220
+ // }
0 commit comments