UNPKG

react-native-image-cropper

Version:

Crop image in React Native using pan to move, pinch to zoom -> capture to file or base64

225 lines (205 loc) 7.22 kB
import React, { Component } from 'react' import PropTypes from 'prop-types' import { View, Image, PixelRatio, PanResponder } from 'react-native' import {Surface} from 'gl-react-native' const {Image: GLImage} = require("./Image") const imageDimensionsAfterZoom = (viewport, dimensions, zoom) => { const ImageRatio = dimensions.width/dimensions.height const ViewportRatio = viewport.width/viewport.height if (ImageRatio > ViewportRatio){ return { height: Math.floor(viewport.height/zoom), width: Math.floor((viewport.height*ImageRatio)/zoom) } } else return { height: Math.floor((viewport.width/ImageRatio)/zoom), width: Math.floor(viewport.width/zoom) } } const movementFromZoom = (gestureState, viewport, dimensions, offsets, zoom) =>{ let newPosX, newPosY // X-axis let widthOffset = dimensions.width-viewport.width let pxVsMovX = (1/dimensions.width) let moveX = (gestureState.dx*pxVsMovX) * zoom newPosX = (parseFloat(offsets.x) - parseFloat(moveX)) // Y-axis let heightOffset = dimensions.height-viewport.height let pxVsMovY = (1/dimensions.height) let moveY = (gestureState.dy*pxVsMovY) * zoom newPosY = (parseFloat(offsets.y) - parseFloat(moveY)) return { x: newPosX, y: newPosY, } } class ImageCrop extends Component { constructor(props) { super(props) this.state = { zoom: 1, //pan settings centerX: 0.5, centerY: 0.5, //Image sizes imageHeight: 300, imageWidth: 300, imageDimHeight: 0, imageDimWidth: 0, currentCapture: '', } } componentWillMount(){ Image.getSize(this.props.image, (width, height) => { //update state this.setState({ imageHeight: height, imageWidth: width, }) }) // //get dimensions after crop // this._dimensionAfterZoom = imageDimensionsAfterZoom( {height: this.props.cropHeight, width: this.props.cropWidth}, {height: this.state.imageHeight, width: this.state.imageWidth}, this.state.zoom ) this.setState({ imageDimHeight: this._dimensionAfterZoom.height, imageDimWidth: this._dimensionAfterZoom.width }) this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: (evt, gestureState) => true, onStartShouldSetPanResponderCapture: (evt, gestureState) => true, onMoveShouldSetPanResponder: (evt, gestureState) => true, onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onPanResponderTerminationRequest: (evt, gestureState) => false, onShouldBlockNativeResponder: (evt, gestureState) => true, onPanResponderGrant: (evt, gestureState) => { //move variables this.offsetX = this.state.centerX this.offsetY = this.state.centerY //zoom variables this.zoomLastDistance=0 this.zoomCurrentDistance=0 }, onPanResponderMove: (evt, gestureState) => { //We are moving the image if (evt.nativeEvent.changedTouches.length <= 1){ var trackX = (gestureState.dx/this.props.cropWidth)*this.state.zoom var trackY = (gestureState.dy/this.props.cropHeight)*this.state.zoom var newPosX = (Number(this.offsetX) - Number(trackX)) var newPosY = (Number(this.offsetY) - Number(trackY)) if (newPosX> 1) newPosX = Number(1) if (newPosY> 1) newPosY = Number(1) if (newPosX< 0) newPosX = Number(0) if (newPosY< 0) newPosY = Number(0) var movement = movementFromZoom( gestureState, {width: this.props.cropWidth, height: this.props.cropHeight}, {width: this.state.imageDimWidth, height: this.state.imageDimHeight}, {x: this.offsetX, y: this.offsetY}, this.state.zoom ) this.setState({centerX: movement.x}) this.setState({centerY: movement.y}) }else{ //We are zooming the image if (this.zoomLastDistance == 0){ let a = evt.nativeEvent.changedTouches[0].locationX - evt.nativeEvent.changedTouches[1].locationX let b = evt.nativeEvent.changedTouches[0].locationY - evt.nativeEvent.changedTouches[1].locationY let c = Math.sqrt( a*a + b*b ) this.zoomLastDistance = c.toFixed(1) }else{ let a = evt.nativeEvent.changedTouches[0].locationX - evt.nativeEvent.changedTouches[1].locationX let b = evt.nativeEvent.changedTouches[0].locationY - evt.nativeEvent.changedTouches[1].locationY let c = Math.sqrt( a*a + b*b ) this.zoomCurrentDistance = c.toFixed(1) //what is the zoom level var screenDiagonal = Math.sqrt(this.state.imageHeight*this.state.imageHeight + this.state.imageWidth*this.state.imageWidth) var distance = (this.zoomCurrentDistance-this.zoomLastDistance)/400 var zoom = this.state.zoom-distance if (zoom<0)zoom=0.0000001 if (zoom>1)zoom=1 this.setState({ zoom: zoom, }) //Set last distance.. this.zoomLastDistance=this.zoomCurrentDistance } } } }) } componentWillReceiveProps(nextProps){ if (this.props.zoom != nextProps.zoom) { var zoom = (100 - nextProps.zoom)/100 this.setState({ zoom: zoom }) } // //get dimensions after crop // this._dimensionAfterZoom = imageDimensionsAfterZoom( {height: this.props.cropHeight, width: this.props.cropWidth}, {height: this.state.imageHeight, width: this.state.imageWidth}, this.state.zoom ) this.setState({ imageDimHeight: this._dimensionAfterZoom.height, imageDimWidth: this._dimensionAfterZoom.width }) } render() { return ( <View {...this._panResponder.panHandlers}> <Surface width={this.props.cropWidth} height={this.props.cropHeight} pixelRatio={this.props.pixelRatio} backgroundColor="transparent" ref="cropit"> <GLImage source={{ uri: this.props.image}} imageSize={{height: this.state.imageHeight, width: this.state.imageWidth}} resizeMode="cover" zoom={this.state.zoom} center={[this.state.centerX, this.state.centerY]} /> </Surface> </View> ) } crop(){ return this.refs.cropit.captureFrame({quality: this.props.quality, type: this.props.type, format: this.props.format, filePath: this.props.filePath}) } } ImageCrop.defaultProps = { image: '', cropWidth: 300, cropHeight: 300, zoomFactor: 0, minZoom: 0, maxZoom: 100, quality: 1, pixelRatio: PixelRatio.get(), type: 'jpg', format: 'base64', filePath: '' } ImageCrop.propTypes = { image: PropTypes.string.isRequired, cropWidth: PropTypes.number.isRequired, cropHeight: PropTypes.number.isRequired, zoomFactor: PropTypes.number, maxZoom: PropTypes.number, minZoom: PropTypes.number, quality: PropTypes.number, pixelRatio: PropTypes.number, type: PropTypes.string, format: PropTypes.string, filePath: PropTypes.string } module.exports=ImageCrop