Created
October 12, 2017 14:36
-
-
Save Michaelvilleneuve/857469c2fb900d8b85fa718a69c5531a to your computer and use it in GitHub Desktop.
Document Scanner and Cropper used together
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Dimensions, Image, Text, TouchableOpacity, View, LayoutAnimation } from 'react-native'; | |
import DocumentScanner from 'react-native-document-scanner'; | |
import Icon from 'react-native-vector-icons/Ionicons'; | |
import React, { Component } from 'react'; | |
import CustomCrop from 'react-native-custom-crop'; | |
import { I18n } from '../utils'; | |
import FloatingButton from '../shared/FloatingButton'; | |
class AutomaticCardAdd extends Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
image: null, | |
torchEnabled: false, | |
stableCounter: 0, | |
lastDetectionTime: new Date().getTime(), | |
detectionCountBeforeCapture: 5, | |
indicatorVisible: false, | |
blackAndWhite: false, | |
brightness: 0, | |
contrast: 1, | |
saturation: 1, | |
}; | |
} | |
componentDidMount() { | |
this.interval = setInterval(() => { | |
const timeDifference = new Date().getTime() - this.state.lastDetectionTime; | |
this.setState({ | |
showCapture: (Math.floor(timeDifference / 1000) > 5), | |
indicatorVisible: (Math.floor(timeDifference / 1000) < 2), | |
}); | |
}, 1000); | |
} | |
componentWillUpdate() { | |
LayoutAnimation.spring(); | |
} | |
componentWillUnmount() { | |
this.setState({ torchEnabled: false }); | |
clearInterval(this.interval); | |
} | |
toggleBlackAndWhite() { | |
if (!this.state.blackAndWhite) { | |
this.setState({ saturation: 0, contrast: 1.8, brightness: 0.4 }); | |
} else { | |
this.setState({ saturation: 1, contrast: 1, brightness: 0 }); | |
} | |
this.setState({ blackAndWhite: !this.state.blackAndWhite }); | |
} | |
startCrop() { | |
this.setState({ cropping: true }); | |
} | |
endCrop() { | |
this.setState({ cropping: false }); | |
} | |
handleCapture() { | |
this.props.handleCapture( | |
`data:image/jpeg;base64,${this.state.image}`, | |
this.props.index, | |
this.state.finalImageWidth, | |
this.state.finalImageHeight, | |
); | |
} | |
showTutorialContent() { | |
switch (this.state.lastDetectionType) { | |
case 0: | |
return I18n.t('correctRectangle'); | |
case 1: | |
return I18n.t('badAngle'); | |
case 2: | |
return I18n.t('tooFar'); | |
default: | |
return I18n.t('tutoCapture'); | |
} | |
} | |
updateImage(image, coordinates) { | |
Image.getSize(`data:image/jpeg;base64,${image}`, (width, height) => { | |
this.setState({ | |
image, | |
cropping: false, | |
rectangleCoordinates: coordinates, | |
finalImageHeight: height, | |
finalImageWidth: width, | |
}); | |
}); | |
} | |
crop() { this.customCrop.crop(); } | |
capture() { this.scanner.capture(); } | |
renderRectangleCountIndicator() { | |
const framesLeft = this.state.detectionCountBeforeCapture - this.state.stableCounter; | |
if (framesLeft < 4 && framesLeft > 0 && this.state.indicatorVisible) { | |
setTimeout(() => this.setState({ indicatorVisible: false }), 1000); | |
return ( | |
<View style={s.indicator} blurType="light" blurAmount={10}> | |
<Text style={s.indicatorT}>{framesLeft}...</Text> | |
</View> | |
); | |
} | |
} | |
renderCrop() { | |
return ( | |
<View style={s.container}> | |
<CustomCrop | |
ref={(ref) => this.customCrop = ref} | |
updateImage={this.updateImage.bind(this)} | |
rectangleCoordinates={this.state.rectangleCoordinates} | |
initialImage={this.state.initialImage} | |
height={this.state.imageHeight} | |
width={this.state.imageWidth} | |
overlayColor="rgba(18,190,210, 1)" | |
overlayStrokeColor="rgba(20,190,210, 1)" | |
handlerColor="rgba(20,150,160, 1)" | |
/> | |
<View style={[s.actions]}> | |
<TouchableOpacity style={s.button} onPress={this.endCrop.bind(this)}> | |
<Icon style={s.buttonI} name="md-arrow-back" /> | |
</TouchableOpacity> | |
<TouchableOpacity style={s.button} onPress={this.crop.bind(this)}> | |
<Icon style={s.buttonI} name="md-checkmark" /> | |
</TouchableOpacity> | |
</View> | |
</View> | |
); | |
} | |
renderScanner() { | |
return ( | |
<View style={s.container}> | |
{this.state.image ? | |
<Image | |
source={{ uri: `data:image/jpeg;base64,${this.state.image}` }} | |
style={s.imageResult} | |
resizeMode="contain" | |
/> : | |
<DocumentScanner | |
ref={(ref) => this.scanner = ref} | |
onPictureTaken={data => { | |
Image.getSize(`data:image/jpeg;base64,${data.initialImage}`, (width, height) => { | |
this.setState({ | |
imageWidth: width, | |
imageHeight: height, | |
finalImageHeight: height, | |
finalImageWidth: width, | |
image: data.croppedImage || data.initialImage, | |
initialImage: data.initialImage, | |
rectangleCoordinates: data.rectangleCoordinates, | |
}); | |
}); | |
}} | |
onRectangleDetect={({ stableCounter, lastDetectionType }) => { | |
this.setState({ | |
stableCounter, | |
lastDetectionType, | |
lastDetectionTime: new Date().getTime() | |
}); | |
}} | |
enableTorch={this.state.torchEnabled} | |
brightness={this.state.brightness} | |
saturation={this.state.saturation} | |
contrast={this.state.contrast} | |
detectionCountBeforeCapture={this.state.detectionCountBeforeCapture} | |
detectionRefreshRateInMS={50} | |
overlayColor="rgba(20,190,210, 0.7)" | |
style={s.scanner} | |
/> | |
} | |
{this.state.image && | |
<View style={s.actions}> | |
<TouchableOpacity style={s.button} onPress={() => this.setState({ image: null })}> | |
<Icon style={s.buttonI} name="md-refresh" /> | |
</TouchableOpacity> | |
<TouchableOpacity style={s.button} onPress={this.startCrop.bind(this)}> | |
<Icon style={s.buttonI} name="md-crop" /> | |
</TouchableOpacity> | |
<TouchableOpacity style={s.button} onPress={this.handleCapture.bind(this)}> | |
<Icon style={s.buttonI} name="md-checkmark" /> | |
</TouchableOpacity> | |
</View> | |
} | |
{!this.state.image && | |
<View style={s.tutoC}> | |
<Text style={s.tuto}>{this.showTutorialContent()}</Text> | |
</View> | |
} | |
{!this.state.image && | |
<View style={s.actions}> | |
<FloatingButton | |
onPress={() => this.setState({ torchEnabled: !this.state.torchEnabled })} | |
> | |
{this.state.torchEnabled ? I18n.t('disable') : I18n.t('enable')} flash | |
</FloatingButton> | |
<FloatingButton onPress={this.toggleBlackAndWhite.bind(this)}> | |
{this.state.blackAndWhite ? I18n.t('colors') : I18n.t('b&w')} | |
</FloatingButton> | |
{this.state.showCapture && | |
<View style={s.capture}> | |
<TouchableOpacity onPress={() => this.capture()} style={s.captureI} /> | |
</View> | |
} | |
</View> | |
} | |
{this.renderRectangleCountIndicator()} | |
</View> | |
); | |
} | |
render() { | |
return this.state.cropping ? this.renderCrop() : this.renderScanner(); | |
} | |
} | |
const s = { | |
container: { flex: 1, backgroundColor: 'black' }, | |
scanner: { flex: 1, backgroundColor: 'black' }, | |
imageResult: { flex: 1 }, | |
capture: { | |
position: 'absolute', | |
width: 65, | |
height: 65, | |
left: (Dimensions.get('window').width / 2) - 32.5, | |
bottom: 75, | |
borderWidth: 2, | |
borderColor: '#FFF', | |
alignItems: 'center', | |
justifyContent: 'center', | |
borderRadius: 100, | |
}, | |
captureI: { | |
backgroundColor: '#fff', | |
borderRadius: 100, | |
width: 55, | |
height: 55, | |
}, | |
crop: { | |
position: 'absolute', | |
bottom: 100, | |
width: 40, | |
height: 40, | |
backgroundColor: 'black', | |
}, | |
tutoC: { | |
position: 'absolute', | |
width: Dimensions.get('window').width - 40, | |
left: 20, | |
top: 24, | |
padding: 10, | |
borderRadius: 15, | |
backgroundColor: 'rgba(255,255,255, 0.2)', | |
}, | |
actionsPicture: { | |
position: 'absolute', | |
width: 50, | |
top: 84, | |
left: 15, | |
height: 200, | |
alignItems: 'center', | |
justifyContent: 'flex-start', | |
flexDirection: 'column' | |
}, | |
indicator: { | |
width: 200, | |
height: 60, | |
left: (Dimensions.get('window').width / 2) - 100, | |
position: 'absolute', | |
alignItems: 'center', | |
justifyContent: 'center', | |
bottom: 80, | |
borderRadius: 15, | |
}, | |
indicatorT: { | |
fontSize: 21, | |
color: '#FFF', | |
}, | |
tuto: { | |
color: '#000', | |
textAlign: 'center', | |
fontSize: 17, | |
}, | |
actions: { | |
backgroundColor: '#111', | |
width: Dimensions.get('window').width, | |
flex: 0.1, | |
alignItems: 'center', | |
padding: 0, | |
justifyContent: 'space-between', | |
flexDirection: 'row' | |
}, | |
button: { | |
backgroundColor: 'transparent', | |
borderRadius: 200, | |
height: 54, | |
paddingHorizontal: 15, | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
buttonI: { | |
color: '#FFF', | |
fontSize: 30, | |
} | |
}; | |
export default AutomaticCardAdd; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment