After Cropping the image got shifted to the corner
alisarfaraj opened this issue · comments
Without any code, I'm not able to help you. The best for me to help would be to share a codesandbox.
this is the jsx code for image cropper
import React, { useCallback, useState } from "react";
import { Modal, Slider } from "antd";
import Cropper from "react-easy-crop";
import { Area } from "react-easy-crop/types";
import { getCroppedImg } from "@/utils/cropperFunction";
import {
handleImageCropperToggle,
imageCropperToggle,
} from "@/redux/reducers/modalsToggle";
import { IMAGE_VARIENTS } from "@/utils/enum";
import { useAppDispatch, useAppSelector } from "@/redux/hooks/hooks";
interface imageCropperI {
fileToCrop: string;
setCroppedImage: (arg: string | null) => void;
imageType: IMAGE_VARIENTS;
fileExtension: string;
}
const ImageCropperComponent = ({
fileToCrop,
setCroppedImage,
imageType,
fileExtension,
}: imageCropperI) => {
// redux selector and dispatcher
const dispatch = useAppDispatch();
const toggleImageCropper = useAppSelector(imageCropperToggle);
// state management
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [rotation, setRotation] = useState(0);
const [zoom, setZoom] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
const onCropComplete = useCallback((croppedAreaPixels: Area) => {
setCroppedAreaPixels(croppedAreaPixels);
}, []);
const showCroppedImage = useCallback(async () => {
try {
const croppedImage = await getCroppedImg(
fileToCrop,
croppedAreaPixels,
rotation,
fileExtension
);
setCroppedImage(croppedImage);
setTimeout(() => {
dispatch(handleImageCropperToggle(false));
}, 1000);
} catch (e) {
console.error(e);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [croppedAreaPixels, rotation, setCroppedImage]);
return (
<Modal
open={toggleImageCropper}
onOk={() => showCroppedImage()}
onCancel={() => dispatch(handleImageCropperToggle(false))}
title="Crop your image in your desire ratio"
>
<div
style={{
position: "relative",
width: "100%",
height: 250,
background: "#333",
}}
>
<Cropper
image={fileToCrop}
crop={crop}
rotation={rotation}
zoom={zoom}
aspect={
imageType === IMAGE_VARIENTS.profile
? 1 / 1
: imageType === IMAGE_VARIENTS.event
? 100 / 100
: 1 / 1
}
cropShape={imageType === IMAGE_VARIENTS.event ? "rect" : "round"}
onCropChange={setCrop}
onRotationChange={setRotation}
onCropComplete={onCropComplete}
onZoomChange={setZoom}
/>
</div>
<div style={{ display: "flex", alignItems: "center", marginTop: "20px" }}>
<h5 style={{ fontSize: "16px", fontWeight: 700 }}>Zoom</h5>
<div style={{ width: "70%", marginLeft: "30px" }}>
<Slider
value={zoom}
min={1}
max={10}
onChange={(zoom) => setZoom(zoom)}
/>
</div>
</div>
</Modal>
);
};
export default ImageCropperComponent;
and these are the functions used for cropping
import { Area } from "react-easy-crop/types";
// crop image function
export const createImage = (url: string): Promise<HTMLImageElement> =>
new Promise((resolve, reject) => {
const image = new Image();
image.addEventListener("load", () => resolve(image));
image.addEventListener("error", (error) => reject(error));
image.setAttribute("crossOrigin", "anonymous");
image.src = url;
});
export function getRadianAngle(degreeValue: number) {
return (degreeValue * Math.PI) / 180;
}
/**
* Returns the new bounding area of a rotated rectangle.
*/
export function rotateSize(width: number, height: number, rotation: number) {
const rotRad = getRadianAngle(rotation);
return {
width:
Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
height:
Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height),
};
}
// get cropped image
export async function getCroppedImg(
imageSrc: string,
pixelCrop: Area | null,
rotation = 0,
fileType: string,
flip = { horizontal: false, vertical: false }
) {
const image = await createImage(imageSrc);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
return null;
}
const rotRad = getRadianAngle(rotation);
// calculate bounding box of the rotated image
const { width: bBoxWidth, height: bBoxHeight } = rotateSize(
image.width,
image.height,
rotation
);
// set canvas size to match the bounding box
canvas.width = bBoxWidth;
canvas.height = bBoxHeight;
// translate canvas context to a central location to allow rotating and flipping around the center
ctx.translate(bBoxWidth / 2, bBoxHeight / 2);
ctx.rotate(rotRad);
ctx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1);
ctx.translate(-image.width / 2, -image.height / 2);
// draw rotated image
ctx.drawImage(image, 0, 0);
// croppedAreaPixels values are bounding box relative
// extract the cropped image using these values
if (pixelCrop) {
const data = ctx.getImageData(
pixelCrop.x,
pixelCrop.y,
pixelCrop.width,
pixelCrop.height
);
// set canvas width to final desired crop size - this will clear existing context
canvas.width = pixelCrop.width;
canvas.height = pixelCrop.height;
// paste generated rotate image at the top left corner
ctx.putImageData(data, 0, 0);
}
// As Base64 string
return canvas.toDataURL(`image/${fileType.split("/").pop()}`);
// return new Promise((resolve, reject) => {
// canvas.toBlob((blob) => {
// resolve(
// new File(
// [blob],
// `${Date.now() + `newFile.${fileType.split("/").pop()}`}`,
// {
// type: fileType,
// }
// )
// );
// }, fileType);
// });
// // As a blob
// return new Promise((resolve, reject) => {
// canvas.toBlob((file) => {
// resolve(URL.createObjectURL(file));
// }, "image/jpeg");
// });
}
At the first glance (as this is a lot of code), I don't see the issue. I'd really be able to help if you could create a codesandbox showing the exact issue with the minimum amount of code.
Closing as there was no answer for a while. Comment to re-open 😉