A React component to crop images/videos with easy interactions
Check out the examples:
- Basic example with hooks
- Basic example with class
- Example with output of the cropped image
- Example with image selected by the user (+ auto-rotation for phone pictures)
- Example with round crop area and no grid
- Example without restricted position
- Example with crop saved/loaded to/from local storage
- Example with a video
- Supports drag, zoom and rotate interactions
- Provides crop dimensions as pixels and percentages
- Supports any images format (JPEG, PNG, even GIF) as url or base 64 string
- Supports any videos format supported in HTML5
- Mobile friendly
yarn add react-easy-crop
or
npm install react-easy-crop --save
The Cropper is styled with
position: absolute
to take the full space of its parent. Thus, you need to wrap it with an element that usesposition: relative
or the Cropper will fill the whole page.
import Cropper from 'react-easy-crop'
class App extends React.Component {
state = {
image: 'your-image-url or as base64',
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 4 / 3,
}
onCropChange = crop => {
this.setState({ crop })
}
onCropComplete = (croppedArea, croppedAreaPixels) => {
console.log(croppedArea, croppedAreaPixels)
}
onZoomChange = zoom => {
this.setState({ zoom })
}
render() {
return (
<Cropper
image={this.state.image}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
/>
)
}
}
Prop | Type | Required | Description |
---|---|---|---|
image |
string | The image to be cropped. image or video is required. |
|
video |
string | The video to be cropped. image or video is required. |
|
crop |
{ x: number, y: number } |
✓ | Position of the media. { x: 0, y: 0 } will center the media under the cropper. |
zoom |
number | Zoom of the media between minZoom and maxZoom . Defaults to 1. |
|
rotation |
number (in degrees) | Rotation of the media. Defaults to 0. | |
aspect |
number | Aspect of the cropper. The value is the ratio between its width and its height. The default value is 4/3 |
|
minZoom |
number | Minimum zoom of the media. Defaults to 1. | |
maxZoom |
number | Maximum zoom of the media. Defaults to 3. | |
zoomWithScroll |
boolean | Enable zoom by scrolling. Defaults to true |
|
cropShape |
'rect' | 'round' | Shape of the crop area. Defaults to 'rect'. | |
cropSize |
{ width: number, height: number } |
Size of the crop area (in pixels). If you don't provide it, it will be computed automatically using the aspect prop and the media size. Warning: This property is rerendering the component when changed so you need to ensure that you are not providing a new object instance at each render. |
|
showGrid |
boolean | Whether to show or not the grid (third-lines). Defaults to true . |
|
zoomSpeed |
number | Multiplies the value by which the zoom changes. Defaults to 1. | |
onCropChange |
crop => void | ✓ | Called everytime the crop is changed. Use it to update your crop state. |
onZoomChange |
zoom => void | Called everytime the zoom is changed. Use it to update your zoom state. |
|
onRotationChange |
rotation => void | Called everytime the rotation is changed (with mobile gestures). Use it to update your rotation state. |
|
onCropComplete |
Function | Called when the user stops moving the media or stops zooming. It will be passed the corresponding cropped area on the media in percentages and pixels | |
style |
{ containerStyle: object, mediaStyle: object, cropAreaStyle: object } |
Custom styles to be used with the Cropper. Styles passed via the style prop are merged with the defaults. | |
classes |
{ containerClassName: string, mediaClassName: string, cropAreaClassName: string } |
Custom class names to be used with the Cropper. Classes passed via the classes prop are merged with the defaults. | |
mediaProps |
object | The properties you want to apply to the media tag ( or | |
restrictPosition |
boolean | Whether the position of the media should be restricted to the boundaries of the cropper. Useful setting in case of zoom < 1 or if the cropper should preserve all media content while forcing a specific aspect ratio for media throughout the application. Example: https://codesandbox.io/s/1rmqky233q. |
|
initialCroppedAreaPixels |
{ width: number, height: number, x: number, y: number} |
Use this to set the initial crop position/zoom of the cropper (for example, when editing a previously cropped media). The value should be the same as the croppedAreaPixels passed to onCropComplete Example: https://codesandbox.io/s/pmj19vp2yx. |
|
onInteractionStart |
Function |
Called everytime a user starts a wheel, touch or mousedown event. | |
onInteractionEnd |
Function |
Called everytime a user ends a wheel, touch or mousedown event. | |
onMediaLoaded |
Function |
Called when media gets loaded. Gets passed an mediaSize object like { width, height, naturalWidth, naturalHeight } |
This callback is the one you should use to save the cropped area of the media. It's passed 2 arguments:
croppedArea
: coordinates and dimensions of the cropped area in percentage of the media dimensioncroppedAreaPixels
: coordinates and dimensions of the cropped area in pixels.
Both arguments have the following shape:
const area = {
x: number, // x/y are the coordinates of the top/left corner of the cropped area
y: number,
width: number, // width of the cropped area
height: number, // height of the cropped area
}
Called when media gets successfully loaded. This is useful if you want to have a custom zoom/crop strategy based on media size.
Example:
const CONTAINER_HEIGHT = 300
const CroppedImage = ({ image }) => {
const [crop, onCropChange] = React.useState({ x: 0, y: 0 })
const [zoom, onZoomChange] = React.useState(1)
return (
<Cropper
image={image}
crop={crop}
zoom={zoom}
onCropChange={onCropChange}
onZoomChange={onZoomChange}
onMediaLoaded={mediaSize => {
// Adapt zoom based on media size to fit max height
onZoomChange(CONTAINER_HEIGHT / mediaSize.naturalHeight)
}}
/>
)
}
yarn
yarn start
Now, open http://localhost:3001/index.html
and start hacking!
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!