An Interest In:
Web News this Week
- April 26, 2024
- April 25, 2024
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
Cropping and resizing images in React
In my last post I wrote about how I struggled with images clogging up my Firebase Database/Storage. One of the ways that helped decrease the file size was by allowing users to crop their image and then resizing it before it was uploaded to my Firebase Storage. Here's how I set up basic image cropping using the react-easy-crop library.
Install react-easy-crop
Using npm:
npm install react-easy-crop --save
or using yarn:
yarn add react-easy-crop
Set up ImageCropper component
Here's my basic setup for the cropper. I'm using getBlob()
in order to pass the cropped image up from this child component.
Setting aspect={1}
will force the image to be cropped into a square, but you can change the aspect ratio to whatever you want. For me, I kept the aspect ratio to 1 as I'm using it for user avatars in my app, and square images are easier to style.
// ImageCropper.jsimport React, { useState } from 'react'import Cropper from 'react-easy-crop'import { getCroppedImg } from './cropImage'const ImageCropper = ({ getBlob, inputImg }) => { const [crop, setCrop] = useState({ x: 0, y: 0 }) const [zoom, setZoom] = useState(1) /* onCropComplete() will occur each time the user modifies the cropped area, which isn't ideal. A better implementation would be getting the blob only when the user hits the submit button, but this works for now */ const onCropComplete = async (_, croppedAreaPixels) => { const croppedImage = await getCroppedImg( inputImg, croppedAreaPixels ) getBlob(croppedImage) } return ( /* need to have a parent with `position: relative` to prevent cropper taking up whole page */ <div className='cropper'> <Cropper image={inputImg} crop={crop} zoom={zoom} aspect={1} onCropChange={setCrop} onCropComplete={onCropComplete} onZoomChange={setZoom} /> </div> )}export default ImageCropper
Set up a component that takes the image file
The cropper takes in an image url or base64. Here I allowed the user to upload their own image and then converted it to base64.
// ImageUpload.jsimport React, { useState } from 'react'import * as firebase from 'firebase/app'import ImageCropper from './ImageCropper'const ImageUpload = () => { const [blob, setBlob] = useState(null) const [inputImg, setInputImg] = useState('') const getBlob = (blob) => { // pass blob up from the ImageCropper component setBlob(blob) } const onInputChange = (e) => { // convert image file to base64 string const file = e.target.files[0] const reader = new FileReader() reader.addEventListener('load', () => { setInputImg(reader.result) }, false) if (file) { reader.readAsDataURL(file) } } const handleSubmitImage = (e) => { // upload blob to firebase 'images' folder with filename 'image' e.preventDefault() firebase .storage() .ref('images') .child('image') .put(blob, { contentType: blob.type }) .then(() => { // redirect user }) } return ( <form onSubmit={handleSubmitImage}> <input type='file' accept='image/*' onChange={onInputChange} /> { inputImg && ( <ImageCropper getBlob={getBlob} inputImg={inputImg} /> ) } <button type='submit'>Submit</button> </form> )}export default ImageUpload
Set up function to crop and save the image
To save only the 'cropped' section of the image, we create a canvas and use .useContext('2d')
to create a 2d shape on it. We draw only the cropped section of the image on our canvas using .drawImage()
, and then return the canvas as a blob.
Set the canvas.width
and canvas.height
to however big you want to store the cropped image as (in pixels). For me, I kept it to 250px by 250px.
// cropImage.js// create the image with a src of the base64 stringconst 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 })export const getCroppedImg = async (imageSrc, crop) => { const image = await createImage(imageSrc) const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') /* setting canvas width & height allows us to resize from the original image resolution */ canvas.width = 250 canvas.height = 250 ctx.drawImage( image, crop.x, crop.y, crop.width, crop.height, 0, 0, canvas.width, canvas.height ) return new Promise((resolve) => { canvas.toBlob((blob) => { resolve(blob) }, 'image/jpeg') })}
This should leave you with a working image cropping tool! When a user uploads an image, the cropper will appear. The user is then able to drag the cropped area and zoom in/out to their content. When they hit submit, the final cropped image is then uploaded (in my case to Firebase Storage) and resized to reduce the file size.
Here's how mine looks after a bit of styling:
Thanks for reading!
Original Link: https://dev.to/shaerins/cropping-and-resizing-images-in-react-360a
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To