Welcome folks today in this blog post we will be looking at react-easy-crop
library to crop,rotate and zoom images and download it as png in browser using javascript. All the full source code of the application is shown below
Get Started
In order to get started you need to make a new react.js
project using the below command
npx create-react-app sampleapp
cd sampleapp
And now we need to install the library using the npm
command as shown below
npm i react-easy-crop
npm i @material-ui/core
And now you will see the below directory
structure of the react.js app as shown below
And now we need to create the App.jsx
file and copy paste the following code
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
import React from "react"; import "./App.css"; import Cropper from "react-easy-crop"; import Slider from "@material-ui/core/Slider"; import Button from "@material-ui/core/Button"; import { generateDownload } from "./utils/cropImage"; export default function App() { const inputRef = React.useRef(); const triggerFileSelectPopup = () => inputRef.current.click(); const [image, setImage] = React.useState(null); const [croppedArea, setCroppedArea] = React.useState(null); const [crop, setCrop] = React.useState({ x: 0, y: 0 }); const [zoom, setZoom] = React.useState(1); const onCropComplete = (croppedAreaPercentage, croppedAreaPixels) => { setCroppedArea(croppedAreaPixels); }; const onSelectFile = (event) => { if (event.target.files && event.target.files.length > 0) { const reader = new FileReader(); reader.readAsDataURL(event.target.files[0]); reader.addEventListener("load", () => { setImage(reader.result); }); } }; const onDownload = () => { generateDownload(image, croppedArea); }; return ( <div className='container'> <div className='container-cropper'> {image ? ( <> <div className='cropper'> <Cropper image={image} crop={crop} zoom={zoom} aspect={1} onCropChange={setCrop} onZoomChange={setZoom} onCropComplete={onCropComplete} /> </div> <div className='slider'> <Slider min={1} max={3} step={0.1} value={zoom} onChange={(e, zoom) => setZoom(zoom)} /> </div> </> ) : null} </div> <div className='container-buttons'> <input type='file' accept='image/*' ref={inputRef} onChange={onSelectFile} style={{ display: "none" }} /> <Button variant='contained' color='primary' onClick={triggerFileSelectPopup} style={{ marginRight: "10px" }} > Choose </Button> <Button variant='contained' color='secondary' onClick={onDownload}> Download </Button> </div> </div> ); } |
As you can see we are importing the material-ui
library and then we have the two buttons where we allow the user to choose the image
file from the laptop or pc and then we are showing the slider
to zoom the image or crop the image and we have the download
button to download the resultant image.
And also we need to add the App.css
file and copy paste the following code
App.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
.container { height: 100vh; width: 100vw; } .container-cropper { height: 90%; padding: 10px; } .cropper { height: 90%; position: relative; } .slider { height: 10%; display: flex; align-items: center; margin: auto; width: 60%; } .container-buttons { border: 1px solid #fffcfc; height: 10%; display: flex; align-items: center; justify-content: center; } |
And now we need to define the utils
folder and inside it we need to create the cropImage.js
file and copy paste the following code
cropImage.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
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; }); function getRadianAngle(degreeValue) { return (degreeValue * Math.PI) / 180; } export default async function getCroppedImg(imageSrc, pixelCrop, rotation = 0) { 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.rotate(getRadianAngle(rotation)); 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, 0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x, 0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y ); // As Base64 string // return canvas.toDataURL("image/jpeg"); return canvas; } export const generateDownload = async (imageSrc, crop) => { if (!crop || !imageSrc) { return; } const canvas = await getCroppedImg(imageSrc, crop); canvas.toBlob( (blob) => { const previewUrl = window.URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.download = "image.jpeg"; anchor.href = URL.createObjectURL(blob); anchor.click(); window.URL.revokeObjectURL(previewUrl); }, "image/jpeg", 0.66 ); }; |
As you can see we are defining all the methods to crop
the image with the user selected portion
and then we are downloading the cropped
portion as an blob png image as shown below