diff --git a/packages/components/src/image-cropper/hook.ts b/packages/components/src/image-cropper/hook.ts index 947ca3fdb98e2..cf1977cdd64c0 100644 --- a/packages/components/src/image-cropper/hook.ts +++ b/packages/components/src/image-cropper/hook.ts @@ -61,6 +61,7 @@ export const useImageCropper = ( { x, y, } = imageRef.current.getBoundingClientRect(); + // Save the initial position and distances from the origin. memo = { initial: { x: state.image.x, y: state.image.y }, distances: { @@ -72,6 +73,7 @@ export const useImageCropper = ( { dispatch( { type: 'ZOOM', scale, + // Calculate the new position based on the scale from the origin. position: { x: memo.initial.x - diff --git a/packages/components/src/image-cropper/reducer.ts b/packages/components/src/image-cropper/reducer.ts index 204d30c85b9e5..e7b480b95c881 100644 --- a/packages/components/src/image-cropper/reducer.ts +++ b/packages/components/src/image-cropper/reducer.ts @@ -180,6 +180,18 @@ function imageCropperReducer( state: State, action: Action ) { const nextRadian = degreeToRadian( action.angle + rotations * 90 ); const scaledWidth = image.width * absScale; const scaledHeight = image.height * absScale; + + // Calculate the translation of the image center after the rotation. + // This is needed to rotate from the center of the cropper rather than the + // center of the image. + const deltaRadians = nextRadian - radian; + const rotatedPosition = rotatePoint( + { x: image.x, y: image.y }, + deltaRadians + ); + + // Calculate the minimum scale to fit the image within the cropper. + // TODO: Optimize the performance? const minScale = getMinScale( nextRadian, @@ -187,13 +199,17 @@ function imageCropperReducer( state: State, action: Action ) { scaledHeight, cropper.width, cropper.height, - image.x, - image.y + rotatedPosition.x, + rotatedPosition.y ) * absScale; const nextScale = Math.min( Math.max( absScale, minScale ), 10 ); return { ...state, + image: { + ...state.image, + ...rotatedPosition, + }, transforms: { ...state.transforms, angle: action.angle,