Crop Selector - Vivid Component
<CropSelector /> will (usually) be used to define an area inside an image to be cropped. While this can wrap any arbitrary element/component you can think of, selecting a complex component with any kind of mouse events could lead to some undesired behaviour.
The crop selection boundaries rely on the element being cropped being a box so if this component ever needs to be used to crop more complex shapes it will need heavy modification.
Features
- Can be fixed to any particular aspect ratio
- Fully controlled -
onChangeprop must set crop value - Can keep selection when clicking outside crop area if desired (as opposed to creating a new crop)
- Will take the height and width of its' child
- Two options for crops returned by onChange and onComplete - Percentage crop and pixel crop
- Percentage crop will return the height and width as a percentage of the image
- Pixel crop will return height and width in absolute pixels
API
CropSelector
<CropSelector /> will overlay an absolutely positioned cropping div on top of any child component/element that you give it.
Props
Key: (optional/required | default value)
crop (optional | undefined)
Crop object with shape { x, y, width, height, aspect }. While this can initially be undefined this prop must be set by the onChange handler for all subsequent crops.
// minimum implementation to get <CropSelector /> to work
import React, { Component } from 'react';
import CropSelector from '@rexlabs/crop-selector';
class Screen extends Component {
state = {
crop: {
x: 0,
y: 0,
// width and height of 0 to not populate a crop initially
width: 0,
height: 0,
// null or undefined to not force aspect ratio
aspect: 16 / 9,
},
};
render() {
return (
<CropSelector
crop={this.state.crop}
onChange={(crop, pixelCrop) => {
this.setState({ crop });
}}
>
<img src='path/to/image' />
</CropSelector>
);
}
}
disabled (optional | false)
Stops crop from being able to be modified and applies the 'disabled' class.
minWidth (optional | 0)
Sets the minimum width of the crop, as a percentage of the image width. This can make for some fairly janky movements so use sparingly.
minHeight (optional | 0)
Sets the minimum height of the crop, as a percentage of the image height. Same caveats as minWidth.
maxWidth (optional | 100)
Sets the maximum width of the crop, as a percentage of the image width.
maxHeight (optional | 100)
Sets the maximum height of the crop, as a percentage of the image height.
keepSelection (optional | false)
If this is set to true the first crop selection that is made then can't be destroyed, only moved and resized.
onChange (required)
Callback function called with the new crop object and the pixel crop object on every change. This is run on the mousemove event listener so is called extremely frequently - don't do any heavy lifting in here.
onComplete (optional | noop}
Callback function called with the new crop object and the pixel crop object on completion of any crop manipulation.
onDragStart (optional | noop)
Callback function called at the start of any drag action (resizing and moving the crop)
onDragEnd (optional | noop)
Callback function called at the end of any drag acion (resizing and moving the crop). This happens the same time as the onComplete callback but only if the dragStart callback has been called.
children (required)
The component/element to render the crop selector on top of.
withAspectCrop
Utility function that creates a crop object with a given width OR height using the given aspect. Useful if you want a default crop but don't want to do the math to work out the required width and height given the aspect ratio.
Args
crop
Normal crop object but with required aspect property and only either width OR height.
drawSurfaceAspect
The aspect of the draw surface (usually just an image) that the crop selector will be placed on top of.
import React from 'react';
import CropSelector, { makeAspectCrop } from '@rexlabs/crop-selector';
import image from 'path/to/image';
class Example extends React.Component {
state = {
crop: undefined,
};
handleImageLoaded(e) {
this.setState({
crop: makeAspectCrop(
{
x: 0,
y: 0,
width: 60,
aspect: 4 / 3,
},
e.target.width / e.target.height
),
});
}
render() {
return (
<CropSelector
{...this.props}
crop={this.state.crop}
onChange={(crop) => {
this.setState({ crop });
}}
>
<img src={image} onLoad={this.handleImageLoaded} />
</CropSelector>
);
}
}
getCroppedImage
Utility function that returns a base64 or blob representation of the given crop within the given image, asynchronously. Can supply either the imageSrc as a string or the imageFile as a File.
Args (object):
imageSrc (optional if imageFile is defined)
The source of the image, as a string.
pixelCrop (required)
The pixel crop object returned by the onChange and onComplete callbacks.
fileName (optional if base64 is true)
The name of the file when returning a blob. Can be null if base64 is true.
base64 (optional | true)
Returns a base64 representation instead of a blob, defaults to false.
quality (optional | 1)
The quality of the returned jpeg image, between 0 and 1.
import React from 'react';
import CropSelector, { getCroppedImage } from '@rexlabs/crop-selector';
import image from 'path/to/image';
class Example extends React.Component {
state = {
crop: {
x: 0,
y: 0,
width: 0,
height: 0,
},
image,
};
handleComplete(crop, pixelCrop) {
getCroppedImage({
imageSrc: image,
pixelCrop,
}).then((file) => {
this.setState({ image: base64Empty(file) ? image : file });
});
}
render() {
return (
<div>
<CropSelector
{...this.props}
crop={this.state.crop}
onChange={(crop) => {
this.setState({ crop });
}}
onComplete={this.handleComplete}
>
<img src={image} />
</CropSelector>
<img src={this.state.image} />
</div>
);
}
}
base64Empty
Small utility function that checks if the base64 image is empty.
Args
base64 (required)
The base64 encoded string to check.
Development
Install dependencies
$ yarn
Available Commands
$ yarn start # starts storybook, for visually testing crop-selector
$ yarn test # runs all units tests
$ yarn test:watch # runs unit tests when files change
$ yarn build # bundles the package for production
Legal
Many algorithms copied from react-image-crop
ISC License Copyright (c) 2015, Dominic Tobias
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.