diff options
author | eug-vs <eug-vs@keemail.me> | 2020-10-29 23:53:13 +0300 |
---|---|---|
committer | eug-vs <eug-vs@keemail.me> | 2020-10-29 23:54:35 +0300 |
commit | 3fef7795681c405322aed6e1c876948ebc2cc932 (patch) | |
tree | c961340ae0364e189361ff7032377f41756e79c7 /src/components | |
parent | e086da62473bf542d972aa53e605926f9af796f5 (diff) | |
download | which-ui-3fef7795681c405322aed6e1c876948ebc2cc932.tar.gz |
refactor: separate ImageCropAreaSelect component
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/AvatarCrop/AvatarCrop.tsx | 74 | ||||
-rw-r--r-- | src/components/AvatarCrop/canvasUtils.js | 54 | ||||
-rw-r--r-- | src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx | 55 | ||||
-rw-r--r-- | src/components/ModalScreen/ModalScreen.tsx | 7 |
4 files changed, 57 insertions, 133 deletions
diff --git a/src/components/AvatarCrop/AvatarCrop.tsx b/src/components/AvatarCrop/AvatarCrop.tsx deleted file mode 100644 index ef6d9c8..0000000 --- a/src/components/AvatarCrop/AvatarCrop.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, {useCallback, useContext, useState} from 'react';
-import Cropper from 'react-easy-crop';
-import {makeStyles} from '@material-ui/core/styles';
-import SendIcon from "@material-ui/icons/Send";
-import ModalScreen from "../ModalScreen/ModalScreen";
-import { getCroppedImg } from './canvasUtils'
-
-interface PropTypes {
- avatarToCrop: string;
- setAvatarToCrop: (src: string) => void;
- callback: (file: File) => void;
-}
-
-const useStyles = makeStyles(theme => ({
- cropContainer: {
- position: 'relative',
- width: '100%',
- height: '100vh',
- background: '#333',
- [theme.breakpoints.up('sm')]: {
- height: 400,
- },
- }
-}));
-
-const AvatarCrop: React.FC<PropTypes> = ({ avatarToCrop, setAvatarToCrop, callback }) => {
- const classes = useStyles();
- const [crop, setCrop] = useState({x: 0, y: 0});
- const [zoom, setZoom] = useState(1);
- const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
-
- const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
- setCroppedAreaPixels(croppedAreaPixels)
- }, []);
-
- const handleLoadCroppedImage = useCallback(async () => {
- try {
- const croppedImage = await getCroppedImg(avatarToCrop, croppedAreaPixels);
- callback(croppedImage);
- } catch (e) {
- console.error(e)
- }
- }, [avatarToCrop, croppedAreaPixels]);
-
- const handleCloseModal = useCallback( () => {
- setAvatarToCrop('');
- },[]);
-
- return (
- <ModalScreen
- title="Choose area"
- actionIcon={<SendIcon />}
- handleAction={handleLoadCroppedImage}
- isActionDisabled={false}
- handleCloseModal={handleCloseModal}
- >
- <div className={classes.cropContainer}>
- <Cropper
- image={avatarToCrop}
- crop={crop}
- zoom={zoom}
- aspect={1}
- cropShape="round"
- showGrid={false}
- onCropChange={setCrop}
- onCropComplete={onCropComplete}
- onZoomChange={setZoom}
- />
- </div>
- </ModalScreen>
- )
-};
-
-export default AvatarCrop;
diff --git a/src/components/AvatarCrop/canvasUtils.js b/src/components/AvatarCrop/canvasUtils.js deleted file mode 100644 index b01aadc..0000000 --- a/src/components/AvatarCrop/canvasUtils.js +++ /dev/null @@ -1,54 +0,0 @@ -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
- });
-
-export 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));
-
- // 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.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');
-
- // As a blob
-
- return new Promise(resolve => {
- canvas.toBlob(file => resolve(file))
- })
-}
diff --git a/src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx b/src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx new file mode 100644 index 0000000..015dd47 --- /dev/null +++ b/src/components/ImageCropAreaSelect/ImageCropAreaSelect.tsx @@ -0,0 +1,55 @@ +import React, { useCallback, useState } from 'react'; +import Cropper from 'react-easy-crop'; +import { makeStyles } from '@material-ui/core/styles'; + +interface Area { + x: number; + y: number; + width: number; + height: number; +} + +interface PropTypes { + image: string; + setArea: (area: Area) => void; +} + +const useStyles = makeStyles(theme => ({ + root: { + position: 'relative', + width: '100%', + height: '100vh', + background: '#333', + [theme.breakpoints.up('sm')]: { + height: 400, + } + } +})); + +const ImageCropAreaSelect: React.FC<PropTypes> = ({ image, setArea }) => { + const classes = useStyles(); + const [crop, setCrop] = useState({ x: 0, y: 0 }); + const [zoom, setZoom] = useState(1); + + const onCropComplete = useCallback((areaPercentage: Area, areaPixels: Area): void => { + setArea(areaPixels); + }, [setArea]); + + return ( + <div className={classes.root}> + <Cropper + image={image} + crop={crop} + zoom={zoom} + aspect={1} + cropShape="round" + showGrid={false} + onCropChange={setCrop} + onCropComplete={onCropComplete} + onZoomChange={setZoom} + /> + </div> + ) +}; + +export default ImageCropAreaSelect; diff --git a/src/components/ModalScreen/ModalScreen.tsx b/src/components/ModalScreen/ModalScreen.tsx index 61cf44a..b71c2c8 100644 --- a/src/components/ModalScreen/ModalScreen.tsx +++ b/src/components/ModalScreen/ModalScreen.tsx @@ -1,5 +1,4 @@ import React, { useState, useCallback } from 'react'; -import { useHistory } from 'react-router-dom'; import { AppBar, Dialog, @@ -20,7 +19,7 @@ interface PropTypes { actionIcon?: JSX.Element; handleAction?: () => void; isActionDisabled?: boolean; - handleCloseModal: ()=> void; + handleCloseModal?: ()=> void; } const useStyles = makeStyles(theme => ({ @@ -48,11 +47,9 @@ const ModalScreen: React.FC<PropTypes> = ({ title, actionIcon, handleAction, isA const classes = useStyles(); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - const history = useHistory(); - const handleClose = useCallback(() => setIsOpen(false), [setIsOpen]); - const onExited = useCallback(handleCloseModal, [history, handleAction]); + const onExited = useCallback(() => handleCloseModal && handleCloseModal(), [handleCloseModal]); const handleClickAction = useCallback(async () => { if (handleAction) await handleAction(); |