design modal crop & handle crop image
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user