react-clipper
Version:
A responsive image clipper tool for React.
210 lines (194 loc) • 7.7 kB
JavaScript
;
import React, { PropTypes } from 'react'
import ReactCrop from 'react-image-crop'
import 'react-image-crop/dist/ReactCrop.css'
import '../index.css'
class Clipper extends React.Component {
static propTypes = {
clipWidth: PropTypes.number,
clipHeight: PropTypes.number,
originUri: PropTypes.string,
crop: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
}),
minWidth: PropTypes.number,
minHeight: PropTypes.number,
maxWidth: PropTypes.number,
maxHeight: PropTypes.number,
keepSelection: PropTypes.bool,
onChange: PropTypes.func,
onComplete: PropTypes.func,
onImageLoaded: PropTypes.func,
onAspectRatioChange: PropTypes.func,
onDragStart: PropTypes.func,
onDragEnd: PropTypes.func,
disabled: PropTypes.bool,
crossorigin: PropTypes.string,
}
static defaultProps = {
originUri: '',
clipWidth: 100,
clipHeight: 100,
crop: undefined,
crossorigin: undefined,
disabled: false,
maxWidth: 100,
maxHeight: 100,
minWidth: 0,
minHeight: 0,
keepSelection: false,
onChange: () => {},
onComplete: () => {},
onImageLoaded: () => {},
onAspectRatioChange: () => {},
onCropComplete: () => {},
onDragStart: () => {},
onDragEnd: () => {},
}
constructor (props) {
super(props);
this.state = {
...props
}
}
render () {
const {originUri, clipWidth, clipHeight, crop, keepSelection, disabled, crossorigin, maxWidth, maxHeight, minWidth, minHeight} = this.state
return (
<div className="clipper-wrap" >
<div style={{width: 545 + clipWidth, height: 500}}>
<div className="clipper-main" ref="imgWrap">
{this._renderUploadBtn()}
<ReactCrop src={originUri}
onImageLoaded={(crop, image, pixelCrop) => this.onImageLoaded(crop, image, pixelCrop)}
onComplete={(crop, pixelCrop) => this.onCropComplete(crop, pixelCrop)}
onAspectRatioChange={(crop, pixelCrop) => this.onAspectRatioChange(crop, pixelCrop)}
onChange={(crop, pixelCrop) => this.onChange(crop, pixelCrop)}
onDragStart={(e) => this.onDragStart(e)}
onDragEnd={(e) => this.onDragEnd(e)}
crop={crop}
maxWidth={maxWidth}
maxHeight={maxHeight}
minWidth={minWidth}
minHeight={minHeight}
keepSelection={keepSelection}
disabled={disabled}
crossorigin={crossorigin}
/>
</div>
<div className="clipper-sub" style={{width: 30 + clipWidth}}>
<div className="preview">预览</div>
<div className="imgItem" style={{width: clipWidth, height: clipHeight}}>
<canvas id="clipped" ref="clipped" style={{width: clipWidth, height: clipHeight}}></canvas>
</div>
<div className="describe">{clipWidth}px x {clipHeight}px</div>
</div>
</div>
{this._renderReUploadBtn()}
</div>
)
}
onChange (crop, pixelCrop) {
const {originUri, clipWidth, clipHeight, onChange} = this.state
const canvas = this.refs.clipped
const ctx = canvas.getContext('2d')
const img = new Image()
img.src = originUri
img.onload = () => {
canvas.width = clipWidth
canvas.height = clipHeight
ctx.drawImage(img, pixelCrop.x, pixelCrop.y, pixelCrop.width, pixelCrop.height, 0, 0, clipWidth, clipHeight)
const clippedUri = canvas.toDataURL()
this.setState({
clippedUri: clippedUri,
crop: {
x: crop.x,
y: crop.y,
width: crop.width,
height: crop.height,
aspect: this.state.crop.aspect
}
})
onChange(crop, pixelCrop, clippedUri)
}
}
onDragStart () {
const {onDragStart} = this.state
onDragStart()
}
onDragEnd () {
const {onDragEnd, clippedUri, originUri} = this.state
onDragEnd(clippedUri, originUri)
}
onImageLoaded (crop, image, pixelCrop) {
const {onImageLoaded} = this.state
this.onChange(crop, pixelCrop)
onImageLoaded(crop, image, pixelCrop)
}
onCropComplete (crop, pixelCrop) {
const {onCropComplete} = this.state
onCropComplete(crop, pixelCrop)
}
onAspectRatioChange (crop, pixelCrop) {
const {onAspectRatioChange} = this.state
onAspectRatioChange(crop, pixelCrop)
}
_renderUploadBtn () {
const {originUri} = this.state
if (!originUri) {
return <a href="javascript:;" className="upload-btn">
<input type="file" onChange={(e) => this.upload(e)}/>选择图片
</a>
}
}
_renderReUploadBtn () {
const {originUri} = this.state
if (originUri) {
return <div className="clear">
<a href="javascript:;" className="upload-btn">
<input type="file" onChange={(e) => this.upload(e)}/>重新上传
</a>
</div>
}
}
upload (e) {
const file = e.target.files[0]
const reader = new FileReader()
reader.onload = (e) => {
const data = e.target.result
this.paintImg(data)
}
reader.readAsDataURL(file);
}
//原图缩放
paintImg (uri) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const wrapWidth = this.refs.imgWrap.offsetWidth
const wrapHeight = this.refs.imgWrap.offsetHeight
let ctxImgWidth, ctxImgHeight
const img = new Image()
img.src = uri
img.onload = () => {
//等比例缩放图片(如果图片宽高都比容器小,则绘制的图片宽高 = 原图片的宽高。)
//如果图片的宽度或者高度比容器大,则宽度或者高度 = 容器的宽度或者高度,另一高度或者宽度则等比例缩放
if (img.width < wrapWidth && img.height < wrapHeight) {
ctxImgWidth = img.width
ctxImgHeight = img.height
} else {
const pWidth = img.width / (img.height / wrapHeight)
const pHeight = img.height / (img.width / wrapWidth)
ctxImgWidth = img.width > img.height ? wrapWidth : pWidth
ctxImgHeight = img.height > img.width ? wrapHeight : pHeight
}
canvas.width = ctxImgWidth
canvas.height = ctxImgHeight
ctx.drawImage(img, 0, 0, ctxImgWidth, ctxImgHeight)
const originUri = canvas.toDataURL()
this.setState({originUri: originUri})
}
}
}
export default Clipper;