design modal crop & handle crop image

This commit is contained in:
Ehsan
2024-10-25 22:16:00 +03:30
parent e874284bb6
commit 638d0d78f5
11 changed files with 193 additions and 56 deletions
+51
View File
@@ -0,0 +1,51 @@
import { Button } from "@mui/material";
import { useState } from "react";
import Cropper from "react-easy-crop";
import { cropImage } from "./cropImg";
function Crop({ data, uploadImg, handleClose }) {
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
const onComplete = (imagePromisse) =>
imagePromisse.then((image) => uploadImg(image));
return (
<div className="mt-[16px]">
<div className="px-[24px]">
<div className="relative w-[424px] h-[380px] rounded-[8px] overflow-hidden">
<Cropper
crop={crop}
zoom={zoom}
aspect={4 / 3}
image={data.blob}
onCropChange={setCrop}
onCropComplete={(_, croppedAreaPixels) =>
setCroppedAreaPixels(croppedAreaPixels)
}
onZoomChange={setZoom}
/>
</div>
</div>
<div className="flex items-center justify-end gap-[16px] mt-[16px]">
<Button
variant="outlined"
onClick={handleClose}
className="!text-[16px] !max-w-[calc(50%_-_8px)] dark:!bg-[#C7CEF4] dark:!text-[#5559CE] !font-medium !border-[#5559CE] !py-[5px] md:!py-[7px] lg:!py-[9px] !shadow-none"
>
انصراف
</Button>
<Button
variant="contained"
onClick={() => onComplete(cropImage(data.blob, croppedAreaPixels))}
className="!text-[16px] !max-w-[calc(50%_-_8px)] !font-medium !shadow-none !py-[5px] md:!py-[7px] lg:!py-[9px]"
>
ذخیره
</Button>
</div>
</div>
);
}
export default Crop;
+29
View File
@@ -0,0 +1,29 @@
import CloseModalD from "@/components/icons/CloseModalD";
import { styleDefault } from "@/mui";
import { Box, Modal } from "@mui/material";
import Crop from "./Crop";
function ModalUpload({ open, handleClose, data, uploadImg }) {
return (
<Modal open={open} onClose={handleClose} className="!hidden md:!flex">
<Box
sx={styleDefault}
className="!pt-[12px] md:!pt-[14px] lg:!py-[16px]
dark:!bg-[#222433] dark:!shadow-none !px-[12px] sm:!px-[16px] md:!px-[20px] lg:!px-[24px]
!pb-[12px] sm:!pb-[16px] lg:!pb-[20px] dark:!border-transparent"
>
<div className="flex items-center justify-between">
<p className="text-[#3B3B3B] dark:text-[#DBDBDB] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium">
برش عکس
</p>
<div className="cursor-pointer" onClick={handleClose}>
<CloseModalD />
</div>
</div>
<Crop data={data} uploadImg={uploadImg} handleClose={handleClose} />
</Box>
</Modal>
);
}
export default ModalUpload;
+47
View File
@@ -0,0 +1,47 @@
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");
image.src = url;
});
async function getCroppedImg(imageSrc, pixelCrop) {
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));
canvas.width = safeArea;
canvas.height = safeArea;
ctx.drawImage(
image,
safeArea / 2 - image.width * 0.5,
safeArea / 2 - image.height * 0.5
);
const data = ctx.getImageData(0, 0, safeArea, safeArea);
canvas.width = pixelCrop.width;
canvas.height = pixelCrop.height;
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)
);
return canvas.toDataURL("image/jpeg");
}
export const cropImage = async (image, croppedAreaPixels, onError) => {
try {
const croppedImage = await getCroppedImg(image, croppedAreaPixels);
return croppedImage;
} catch (err) {
onError(err);
}
};
+71
View File
@@ -0,0 +1,71 @@
"use client";
import { useRef, useState } from "react";
import { Button } from "@mui/material";
import ProfileP from "@/components/icons/ProfileP";
import ArrowLeftPU from "@/components/icons/ArrowLeftPU";
import { uploadImage } from "@/helper";
import ModalUpload from "./ModalUpload";
import Image from "next/image";
function UploadFile({ image, setImage }) {
const fileRef = useRef();
const [open, setOpen] = useState(false);
const [data, setData] = useState();
const handleClose = () => setOpen(false);
const handleOpen = () => setOpen(true);
const openInput = () => fileRef.current.click();
const handleChange = async (event) => {
const result = await uploadImage(event);
handleOpen();
setData(result);
};
const uploadImg = (prop) => {
setImage(prop);
handleClose();
};
return (
<>
<ModalUpload
open={open}
data={data}
uploadImg={uploadImg}
handleClose={handleClose}
/>
<input
type="file"
ref={fileRef}
className="contents"
onChange={handleChange}
/>
{image ? (
<Image
width={24}
height={24}
src={image}
alt="img-uploaded"
className="w-[74px] h-[74px] rounded-full overflow-hidden object-cover"
/>
) : (
<div className="p-[24px] grid place-items-center border border-solid border-[#EFEFEF] bg-[#EFEFEF] rounded-full">
<ProfileP />
</div>
)}
<Button
variant="text"
onClick={openInput}
className="!flex !p-1 dark:!text-[#A1A1A1] !text-[#525252] !text-[16px] !font-medium !items-center !justify-center !gap-[4px]"
>
انتخاب تصویر
<ArrowLeftPU />
</Button>
</>
);
}
export default UploadFile;