From aa3e3bbe60de3276ba7fcaeba9543424c603166f Mon Sep 17 00:00:00 2001 From: IlyaDoronin Date: Wed, 13 Sep 2023 17:14:27 +0300 Subject: [PATCH] fix: CropModal --- src/pages/Questions/UploadImage/index.tsx | 4 +- .../DescriptionForm/ImageAndVideoButtons.tsx | 4 +- .../Modal/{CropModal2.tsx => CropModal.tsx} | 72 +++- src/ui_kit/Modal/CroppingModal.tsx | 366 ------------------ src/ui_kit/Modal/ImageCrop.tsx | 6 +- 5 files changed, 66 insertions(+), 386 deletions(-) rename src/ui_kit/Modal/{CropModal2.tsx => CropModal.tsx} (84%) delete mode 100644 src/ui_kit/Modal/CroppingModal.tsx diff --git a/src/pages/Questions/UploadImage/index.tsx b/src/pages/Questions/UploadImage/index.tsx index 47f65929..1148dd96 100644 --- a/src/pages/Questions/UploadImage/index.tsx +++ b/src/pages/Questions/UploadImage/index.tsx @@ -2,7 +2,7 @@ import { useParams } from "react-router-dom"; import { useState } from "react"; import { Typography, Box, useTheme, ButtonBase } from "@mui/material"; import UploadBox from "@ui_kit/UploadBox"; -import { CroppingModal } from "@ui_kit/Modal/CroppingModal"; +import { CropModal } from "@ui_kit/Modal/CropModal"; import UploadIcon from "../../../assets/icons/UploadIcon"; import * as React from "react"; import { questionStore, updateQuestionsList } from "@root/questions"; @@ -67,7 +67,7 @@ export default function UploadImage({ totalIndex }: UploadImageProps) { /> - setOpened(false)} picture={listQuestions[quizId][totalIndex].content.back} diff --git a/src/pages/Result/DescriptionForm/ImageAndVideoButtons.tsx b/src/pages/Result/DescriptionForm/ImageAndVideoButtons.tsx index 4512c9fa..da289224 100644 --- a/src/pages/Result/DescriptionForm/ImageAndVideoButtons.tsx +++ b/src/pages/Result/DescriptionForm/ImageAndVideoButtons.tsx @@ -3,7 +3,7 @@ import { Box, Typography, useTheme } from "@mui/material"; import AddImage from "@icons/questionsPage/addImage"; import AddVideofile from "@icons/questionsPage/addVideofile"; import { useState } from "react"; -import { CroppingModal } from "@ui_kit/Modal/CroppingModal"; +import { CropModal } from "@ui_kit/Modal/CropModal"; export default function ImageAndVideoButtons() { const theme = useTheme(); @@ -13,7 +13,7 @@ export default function ImageAndVideoButtons() { return ( setOpened(true)} /> - setOpened(false)} /> + setOpened(false)} /> >; + picture?: string; } -export const CropModal2: FC = ({ opened, onClose }) => { +export const CropModal: FC = ({ opened, onClose, picture }) => { const [imgSrc, setImgSrc] = useState(""); const imgRef = useRef(null); @@ -31,6 +40,12 @@ export const CropModal2: FC = ({ opened, onClose }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(786)); + useEffect(() => { + if (picture) { + setImgSrc(picture); + } + }, [picture]); + const styleModal = { position: "absolute", top: "50%", @@ -77,7 +92,9 @@ export const CropModal2: FC = ({ opened, onClose }) => { if (event.target.files && event.target.files.length > 0) { setCrop(undefined); const reader = new FileReader(); - reader.addEventListener("load", () => setImgSrc(reader.result?.toString() || "")); + reader.addEventListener("load", () => + setImgSrc(reader.result?.toString() || "") + ); reader.readAsDataURL(event.target.files[0]); } }; @@ -111,8 +128,18 @@ export const CropModal2: FC = ({ opened, onClose }) => { useDebounceEffect( async () => { - if (completedCrop?.width && completedCrop?.height && imgRef.current && previewCanvasRef.current) { - canvasPreview(imgRef.current, previewCanvasRef.current, completedCrop, rotate); + if ( + completedCrop?.width && + completedCrop?.height && + imgRef.current && + previewCanvasRef.current + ) { + canvasPreview( + imgRef.current, + previewCanvasRef.current, + completedCrop, + rotate + ); } }, 100, @@ -210,10 +237,21 @@ export const CropModal2: FC = ({ opened, onClose }) => { - - + + - Размер + + Размер + = ({ opened, onClose }) => { /> - Затемнение + + Затемнение + = ({ opened, onClose }) => { /> - + = ({ opened, onClose }) => { color: "white", background: "#7E2AEA", borderRadius: "8px", + marginRight: "6px", }} > - + Обрезать diff --git a/src/ui_kit/Modal/CroppingModal.tsx b/src/ui_kit/Modal/CroppingModal.tsx deleted file mode 100644 index 63368154..00000000 --- a/src/ui_kit/Modal/CroppingModal.tsx +++ /dev/null @@ -1,366 +0,0 @@ -import React, { FC, useEffect, useRef, useState } from "react"; -import { saveAs } from "file-saver"; -import ReactCrop, { Crop } from "react-image-crop"; -import "react-image-crop/dist/ReactCrop.css"; -import { - Box, - Button, - Modal, - Slider, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; - -import quiz from "../../assets/quiz-template-6.png"; -import { ResetIcon } from "@icons/ResetIcon"; - -interface Iprops { - opened: boolean; - onClose: () => void; - picture?: string | ArrayBuffer; -} - -export const CroppingModal: FC = ({ opened, onClose, picture }) => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(786)); - - const style = { - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%)", - width: isMobile ? "343px" : "620px", - bgcolor: "background.paper", - boxShadow: 24, - padding: "20px", - borderRadius: "8px", - }; - - const styleSlider = { - width: isMobile ? "350px" : "250px", - color: "#7E2AEA", - height: "12px", - "& .MuiSlider-track": { - border: "none", - }, - "& .MuiSlider-rail": { - backgroundColor: "#F2F3F7", - border: `1px solid "#9A9AAF"`, - }, - "& .MuiSlider-thumb": { - height: 26, - width: 26, - border: `6px solid #7E2AEA`, - backgroundColor: "white", - boxShadow: `0px 0px 0px 3px white, - 0px 4px 4px 3px #C3C8DD`, - "&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": { - boxShadow: `0px 0px 0px 3px white, - 0px 4px 4px 3px #C3C8DD`, - }, - }, - }; - - const [src, setSrc] = useState(quiz); - const [crop, setCrop] = useState({ - unit: "px", - y: 0, - x: 0, - width: 100, - height: 100, - }); - const [completedCrop, setCompletedCrop] = useState(null); - const [imageSize, setImageSize] = useState(580); - const [darken, setDarken] = useState(0); - const [croppedImageUrl, setCroppedImageUrl] = useState(null); - - const fileInputRef = useRef(null); - - useEffect(() => { - if (picture) { - setSrc(picture); - } - }, [picture]); - - const onCropComplete = (crop: Crop) => { - setCompletedCrop(crop); - - if (src) { - getCroppedAndDarkenedImg(src as string, crop, "cropped.jpeg", darken, rotation) - .then((croppedImageUrl) => { - setCroppedImageUrl(croppedImageUrl); - }) - .catch((error) => { - console.error("Ошибка при обрезке и затемнении изображения:", error); - }); - } - }; - - const handleFileChange = (e: React.ChangeEvent) => { - if (e.target.files && e.target.files.length > 0) { - const file = e.target.files[0]; - const reader = new FileReader(); - reader.onload = (event) => { - if (event.target?.result) { - setSrc(event.target.result); - } - }; - reader.readAsDataURL(file); - } - }; - - const handleDownloadClick = async () => { - if (completedCrop && src) { - const croppedImageUrl = await getCroppedAndDarkenedImg(src, completedCrop, "cropped.jpeg", darken, rotation); - setCroppedImageUrl(croppedImageUrl); - saveAs(croppedImageUrl, "cropped-image.jpeg"); - } - }; - - const [widthImg, setWidthImg] = useState(580); - - const getCroppedAndDarkenedImg = ( - image: string | ArrayBuffer, - crop: Crop, - fileName: string, - darken: number, - rotation: number - ): Promise => { - const img = new Image(); - img.src = image as string; - - let scaleX = 360 / 580; - let scaleY = 219 / 320; - - if (img.naturalWidth) { - scaleX = img.naturalWidth / widthImg; - } - - if (img.naturalHeight) { - scaleY = img.naturalHeight / 320; - } - - const canvas = document.createElement("canvas"); - canvas.width = crop.width!; - canvas.height = crop.height!; - const ctx = canvas.getContext("2d"); - - if (!ctx) { - throw new Error("Canvas context is null"); - } - - // Применяем вращение к canvas - ctx.translate(crop.width / 2, crop.height / 2); - ctx.rotate((rotation * Math.PI) / 180); - ctx.translate(-crop.width / 2, -crop.height / 2); - - ctx.drawImage( - img, - crop.x * scaleX, - crop.y * scaleY, - crop.width * scaleX, - crop.height * scaleY, - 0, - 0, - crop.width, - crop.height - ); - - const imageData = ctx.getImageData(0, 0, crop.width, crop.height); - - const newImageData = imageData.data.map((value, index) => { - if ((index + 1) % 4 === 0) { - return value; - } - - return value * (1 - darken / 100); - }); - - imageData.data.set(newImageData); - - ctx.putImageData(imageData, 0, 0); - - return new Promise((resolve, reject) => { - canvas.toBlob((blob) => { - if (!blob) { - reject(new Error("Canvas is empty")); - return; - } - const file = new File([blob], fileName, { type: "image/jpeg" }); - const imageUrl = window.URL.createObjectURL(file); - resolve(imageUrl); - }, "image/jpeg"); - }); - }; - - const [rotation, setRotation] = useState(0); - - const rotateImage = () => { - const newRotation = (rotation + 90) % 360; - setRotation(newRotation); - }; - - const handleSliderChange = (newValue: number) => { - setDarken(newValue); - - getCroppedAndDarkenedImg(src as string, completedCrop!, "cropped.jpeg", newValue, rotation); - }; - - return ( - <> - Cropped Area - - - - setCrop(newCrop)} - onComplete={onCropComplete} - > - {src && ( - console.log(e.currentTarget)} - style={{ - filter: `brightness(${100 - darken}%)`, - maxWidth: "580px", - transform: `rotate(${rotation}deg)`, - }} - width={580 * (imageSize / 200)} - height={320} - alt="Crop" - /> - )} - - - - - {Math.round(crop.width)}x - {Math.round(crop.width)} - px - - - - { - setCrop((prevCrop: Crop) => ({ - ...prevCrop, - unit: "px", - x: 210, - y: 10, - width: 210, - height: 300, - })); - setRotation(0); - setDarken(0); - setImageSize(580); - }} - style={{ marginBottom: "10px", cursor: "pointer" }} - /> - - Размер - { - setImageSize(newValue as number); - setWidthImg(580 * (imageSize / 200)); - }} - /> - - - Затемнение - handleSliderChange(newValue as number)} - /> - - - - - - - - - - - ); -}; diff --git a/src/ui_kit/Modal/ImageCrop.tsx b/src/ui_kit/Modal/ImageCrop.tsx index e630852a..fb9877d6 100644 --- a/src/ui_kit/Modal/ImageCrop.tsx +++ b/src/ui_kit/Modal/ImageCrop.tsx @@ -1,15 +1,13 @@ import { Box, Button } from "@mui/material"; import { FC, useState } from "react"; -import { CroppingModal } from "./CroppingModal"; -import { CropModal2 } from "./CropModal2"; +import { CropModal } from "./CropModal"; const ImageCrop: FC = () => { const [opened, setOpened] = useState(false); return ( - {/* setOpened(false)} /> */} - setOpened(false)} /> + setOpened(false)} /> ); };