import Cropper from "react-easy-crop"; import { useState, useCallback, useRef } from "react"; import Slider from "./Slider"; export default function ImageUploader({ target, id, buttonMsg, handleAvatarChange, imageRef }) { const imageFileRef = useRef(); const [imageDataUrl, setImageDataUrl] = useState(); const [croppedAreaPixels, setCroppedAreaPixels] = useState(); const [rotation] = useState(1); const [crop, setCrop] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState(1); const [imageLoaded, setImageLoaded] = useState(false); const [isImageShown, setIsImageShown] = useState(false); const [shownImage, setShownImage] = useState(); const [imageUploadModalOpen, setImageUploadModalOpen] = useState(false); const openUploaderModal = () => { imageRef ? (setIsImageShown(true), setShownImage(imageRef)) : setIsImageShown(false); setImageUploadModalOpen(!imageUploadModalOpen); }; const closeImageUploadModal = () => { setImageUploadModalOpen(false); }; async function ImageUploadHandler() { const img = await readFile(imageFileRef.current.files[0]); setImageDataUrl(img); CropHandler(); } const readFile = (file) => { return new Promise((resolve) => { const reader = new FileReader(); reader.addEventListener("load", () => resolve(reader.result), false); reader.readAsDataURL(file); }); }; const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => { setCroppedAreaPixels(croppedAreaPixels); }, []); const CropHandler = () => { setCrop({ x: 0, y: 0 }); setZoom(1); setImageLoaded(true); }; const handleZoomSliderChange = ([value]) => { value < 1 ? setZoom(1) : setZoom(value); }; const createImage = (url) => new Promise((resolve, reject) => { const image = new Image(); image.addEventListener("load", () => resolve(image)); image.addEventListener("error", (error) => reject(error)); image.setAttribute("crossOrigin", "anonymous"); // needed to avoid cross-origin issues on CodeSandbox image.src = url; }); function getRadianAngle(degreeValue) { return (degreeValue * Math.PI) / 180; } async function getCroppedImg(imageSrc, pixelCrop, rotation = 0) { const image = await createImage(imageSrc); const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const maxSize = Math.max(image.width, image.height); const safeArea = 2 * ((maxSize / 2) * Math.sqrt(2)); // set each dimensions to double largest dimension to allow for a safe area for the // image to rotate in without being clipped by canvas context canvas.width = safeArea; canvas.height = safeArea; // translate canvas context to a central location on image to allow rotating around the center. ctx.translate(safeArea / 2, safeArea / 2); ctx.rotate(getRadianAngle(rotation)); ctx.translate(-safeArea / 2, -safeArea / 2); // draw rotated image and store data. ctx.drawImage(image, safeArea / 2 - image.width * 0.5, safeArea / 2 - image.height * 0.5); const data = ctx.getImageData(0, 0, safeArea, safeArea); // 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 with correct offsets for x,y crop values. ctx.putImageData( data, Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x), Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y) ); // As Base64 string return canvas.toDataURL("image/jpeg"); } const showCroppedImage = useCallback(async () => { try { const croppedImage = await getCroppedImg(imageDataUrl, croppedAreaPixels, rotation); setIsImageShown(true); setShownImage(croppedImage); setImageLoaded(false); handleAvatarChange(croppedImage); closeImageUploadModal(); } catch (e) { console.error(e); } }, [croppedAreaPixels, rotation]); return (
{imageUploadModalOpen && (
{!imageLoaded && (
{!isImageShown && (

No {target}

)} {isImageShown && ( {target} )}
)} {imageLoaded && (
)}
)}
); }