@@ -6,11 +6,12 @@ const ImageCropView = ({
66 containerWidth = 800 ,
77 containerHeight = 400 ,
88 isEditing,
9+ zoom = 1 ,
910} ) => {
10- const [ position , setPosition ] = useState ( { y : 0 } ) ;
11+ const [ position , setPosition ] = useState ( { x : 0 , y : 0 } ) ;
1112 const [ imageSize , setImageSize ] = useState ( { width : 0 , height : 0 } ) ;
1213 const [ isDragging , setIsDragging ] = useState ( false ) ;
13- const [ startY , setStartY ] = useState ( 0 ) ;
14+ const [ startPos , setStartPos ] = useState ( { x : 0 , y : 0 } ) ;
1415 const imageRef = useRef ( null ) ;
1516 const containerRef = useRef ( null ) ;
1617
@@ -22,21 +23,42 @@ const ImageCropView = ({
2223 let width = containerWidth ;
2324 let height = containerWidth / aspectRatio ;
2425
25-
26- if ( height < containerHeight ) {
27- height = containerHeight ;
28- width = containerHeight * aspectRatio ;
26+ if ( width < containerWidth || height < containerHeight ) {
27+ const containerRatio = containerWidth / containerHeight ;
28+ if ( aspectRatio > containerRatio ) {
29+ height = containerHeight ;
30+ width = containerHeight * aspectRatio ;
31+ } else {
32+ width = containerWidth ;
33+ height = containerWidth / aspectRatio ;
34+ }
2935 }
3036
37+ width *= zoom ;
38+ height *= zoom ;
39+
3140 setImageSize ( { width, height } ) ;
32- setPosition ( { y : ( containerHeight - height ) / 2 } ) ;
41+ setPosition ( {
42+ x : ( containerWidth - width ) / 2 ,
43+ y : ( containerHeight - height ) / 2
44+ } ) ;
45+ } ;
46+
47+ image . onerror = ( err ) => {
48+ console . error ( "ImageCropView: Failed to load image" , src , err ) ;
3349 } ;
34- } , [ src , containerWidth , containerHeight ] ) ;
50+ } , [ src , containerWidth , containerHeight , zoom ] ) ;
3551
36- const constrainY = ( y ) => {
52+ const constrain = ( x , y ) => {
53+ const minX = containerWidth - imageSize . width ;
54+ const maxX = 0 ;
3755 const minY = containerHeight - imageSize . height ;
3856 const maxY = 0 ;
39- return Math . min ( maxY , Math . max ( minY , y ) ) ;
57+
58+ return {
59+ x : Math . min ( maxX , Math . max ( minX , x ) ) ,
60+ y : Math . min ( maxY , Math . max ( minY , y ) )
61+ } ;
4062 } ;
4163
4264 const handleMouseDown = ( e ) => {
@@ -46,16 +68,20 @@ const ImageCropView = ({
4668 }
4769 e . preventDefault ( ) ;
4870 setIsDragging ( true ) ;
49- setStartY ( e . clientY - position . y ) ;
71+ setStartPos ( {
72+ x : e . clientX - position . x ,
73+ y : e . clientY - position . y
74+ } ) ;
5075 } ;
5176
5277 const handleMouseMove = ( e ) => {
5378 if ( ! isEditing || ! isDragging ) {
5479 return ;
5580 }
5681
57- const newY = e . clientY - startY ;
58- setPosition ( { y : constrainY ( newY ) } ) ;
82+ const newX = e . clientX - startPos . x ;
83+ const newY = e . clientY - startPos . y ;
84+ setPosition ( constrain ( newX , newY ) ) ;
5985 } ;
6086
6187 const handleMouseUp = ( ) => {
@@ -65,15 +91,19 @@ const ImageCropView = ({
6591 const handleTouchStart = ( e ) => {
6692 if ( ! isEditing ) return ;
6793 setIsDragging ( true ) ;
68- setStartY ( e . touches [ 0 ] . clientY - position . y ) ;
94+ setStartPos ( {
95+ x : e . touches [ 0 ] . clientX - position . x ,
96+ y : e . touches [ 0 ] . clientY - position . y
97+ } ) ;
6998 } ;
7099
71100 const handleTouchMove = ( e ) => {
72101 if ( ! isEditing || ! isDragging ) return ;
73102 if ( e . cancelable ) e . preventDefault ( ) ;
74103
75- const newY = e . touches [ 0 ] . clientY - startY ;
76- setPosition ( { y : constrainY ( newY ) } ) ;
104+ const newX = e . touches [ 0 ] . clientX - startPos . x ;
105+ const newY = e . touches [ 0 ] . clientY - startPos . y ;
106+ setPosition ( constrain ( newX , newY ) ) ;
77107 } ;
78108
79109 const handleTouchEnd = ( ) => {
@@ -84,27 +114,35 @@ const ImageCropView = ({
84114 if ( ! isEditing ) return ;
85115
86116 const step = 20 ;
87- let newY = position . y ;
88-
89- if ( e . key === 'ArrowUp' ) {
90- e . preventDefault ( ) ;
91- newY -= step ;
92- } else if ( e . key === 'ArrowDown' ) {
93- e . preventDefault ( ) ;
94- newY += step ;
95- } else {
96- return ;
117+ let { x : newX , y : newY } = position ;
118+
119+ switch ( e . key ) {
120+ case 'ArrowUp' :
121+ newY -= step ;
122+ break ;
123+ case 'ArrowDown' :
124+ newY += step ;
125+ break ;
126+ case 'ArrowLeft' :
127+ newX -= step ;
128+ break ;
129+ case 'ArrowRight' :
130+ newX += step ;
131+ break ;
132+ default :
133+ return ;
97134 }
98135
99- setPosition ( { y : constrainY ( newY ) } ) ;
136+ e . preventDefault ( ) ;
137+ setPosition ( constrain ( newX , newY ) ) ;
100138 } ;
101139
102140 return (
103141 < div
104142 ref = { containerRef }
105143 role = "application"
106144 aria-label = "Image Cropper"
107- aria-description = "Use Up and Down arrow keys to adjust current image position."
145+ aria-description = "Use Arrow keys to adjust image position."
108146 tabIndex = { isEditing ? 0 : - 1 }
109147 onKeyDown = { handleKeyDown }
110148 style = { {
@@ -126,11 +164,12 @@ const ImageCropView = ({
126164 width : imageSize . width ,
127165 height : imageSize . height ,
128166 position : "absolute" ,
129- left : "50%" ,
130- transform : `translateX(-50%) translateY(${ position . y } px)` ,
167+ left : 0 ,
168+ top : 0 ,
169+ transform : `translate3d(${ position . x } px, ${ position . y } px, 0)` ,
131170 transition : isDragging ? "none" : "transform 0.1s ease" ,
132171 userSelect : "none" ,
133- objectFit : "cover " ,
172+ objectFit : "fill " ,
134173 pointerEvents : isEditing ? "auto" : "none" ,
135174 touchAction : "none"
136175 } }
0 commit comments