DEV Community

Cover image for react-cropper + mui with rotate and flip option
Krishna Pankhania
Krishna Pankhania

Posted on

react-cropper + mui with rotate and flip option

Demo & Code : https://codesandbox.io/s/react-cropper-js-mui-with-rotate-and-flip-option-y7mm6n

Download code from : https://github.com/krishnapankhania/react-mui-cropper-demo

Cropper Demo

How to use?

Create react project. More stuff on react docs.

npx create-react-app my-app
cd my-app
npm start
Enter fullscreen mode Exit fullscreen mode

Let's install Material Ui react components, because we are going to use that

https://mui.com

Run below command

npm install @mui/material @emotion/react @emotion/styled
Enter fullscreen mode Exit fullscreen mode

Let's install react cropper

Run below command

npm i react-cropper
Enter fullscreen mode Exit fullscreen mode

We will create 3 components

  • Upload: File input.
  • Popup: A popup to open after the file is selected
  • CropperDemo: Cropper logic resides here

Upload.js

This component will take a file input from the user and will return file data to the parent component.

import * as React from "react";
import { styled } from "@mui/material/styles";
import Button from "@mui/material/Button";

const Input = styled("input")({
  display: "none"
});

export default function Upload({ getUploadedFile }) {
  const onChange = (e) => {
    e.preventDefault();
    let files;
    if (e.dataTransfer) {
      files = e.dataTransfer.files;
    } else if (e.target) {
      files = e.target.files;
    }
    if (files.length === 0) {
      return alert("Please select a file.");
    }
    const reader = new FileReader();
    reader.onload = () => {
      getUploadedFile(reader.result);
    };
    reader.readAsDataURL(files[0]);
  };
  return (
    <label htmlFor="contained-button-file">
      <Input
        accept="image/*"
        id="contained-button-file"
        multiple
        type="file"
        onChange={onChange}
      />
      <Button variant="contained" component="span">
        Upload
      </Button>
    </label>
  );
}
Enter fullscreen mode Exit fullscreen mode

Popup.js

This component is a MUI dialog component that will hold the cropper.

import * as React from "react";
import Dialog from "@mui/material/Dialog";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import CropperDemo from "./Cropper";

export default function Popup({ open, image, handleClose, getCroppedFile }) {
  return (
    <div>
      <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="alert-dialog-title"
        aria-describedby="alert-dialog-description"
      >
        <DialogTitle id="alert-dialog-title">Crop Image</DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            <CropperDemo
              handleClose={handleClose}
              src={image}
              getCroppedFile={getCroppedFile}
            />
          </DialogContentText>
        </DialogContent>
      </Dialog>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Cropper.js

This component does the cropping of an image and returns it to parent. Check the rotate and flip options as well. you can play around with other options too.

Check out available options

import React, { useRef, useState } from "react";
import Cropper from "react-cropper";
import Skeleton from "@mui/material/Skeleton";
import Button from "@mui/material/Button";
import ButtonGroup from "@mui/material/ButtonGroup";
import Box from "@mui/material/Box";

import "cropperjs/dist/cropper.css";

export default function CropperDemo({ src, getCroppedFile }) {
  const cropperRef = useRef(null);
  const [loading, setLoading] = useState(true);
  const [scaleX, setScaleX] = useState(1);
  const [scaleY, setScaleY] = useState(1);

  const handleClick = () => {
    const imageElement = cropperRef?.current;
    const cropper = imageElement?.cropper;
    const img = cropper.getCroppedCanvas().toDataURL();
    getCroppedFile(img);
  };
  const rotate = () => {
    const imageElement = cropperRef?.current;
    const cropper = imageElement?.cropper;
    cropper.rotate(90);
  };
  const flip = (type) => {
    const imageElement = cropperRef?.current;
    const cropper = imageElement?.cropper;
    if (type === "h") {
      cropper.scaleX(scaleX === 1 ? -1 : 1);
      setScaleX(scaleX === 1 ? -1 : 1);
    } else {
      cropper.scaleY(scaleY === 1 ? -1 : 1);
      setScaleY(scaleY === 1 ? -1 : 1);
    }
  };
  return (
    <>
      {loading && (
        <Skeleton variant="rectangular" width={"100%"} height={400} />
      )}
      <Box display={"flex"} justifyContent={"flex-end"} mb={1}>
        <ButtonGroup disableElevation variant="contained">
          <Button onClick={rotate}>Rotate</Button>
          <Button onClick={() => flip("h")}>Flip H</Button>
          <Button onClick={() => flip("v")}>Flip V</Button>
        </ButtonGroup>
      </Box>

      <Cropper
        src={src}
        style={{ height: 400, width: "100%" }}
        // Cropper.js options
        initialAspectRatio={16 / 9}
        guides={false}
        ready={() => {
          setLoading(false);
        }}
        ref={cropperRef}
      />
      <Button
        sx={{
          float: "right",
          mt: 1
        }}
        onClick={handleClick}
        autoFocus
        color="success"
        variant="contained"
      >
        Crop
      </Button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

App.js

Let's use all components.

import * as React from "react";
import Box from "@mui/material/Box";
import Upload from "./Upload";
import Popup from "./Popup";
export default function App() {
  const [open, setOpen] = React.useState(false);
  const [image, setImage] = React.useState(
    "https://images.unsplash.com/photo-1612232134966-a9b076b9fbe7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80"
  );

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div>
      <Box
        sx={{
          display: "flex",
          flexDirection: "column",
          alignItems: "center"
        }}
      >
        <Box my={2}>
          <img src={image} alt="cropped" height={400} />
        </Box>

        <Upload
          getUploadedFile={(image) => {
            setOpen(true);
            setImage(image);
          }}
        />
        <Popup
          open={open}
          handleClose={handleClose}
          image={image}
          getCroppedFile={(image) => {
            setImage(image);
            handleClose();
          }}
        />
      </Box>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
hanifaarrumaisha profile image
Hanifa Arrumaisha

is react-cropper library still maintained? last commit was last year? How do you think about use that on production?