Skip to content

Instantly share code, notes, and snippets.

@ceessay
Created December 27, 2019 11:30
Show Gist options
  • Save ceessay/2c7f9eb4a2fb15ca5539e0f0d33d2bab to your computer and use it in GitHub Desktop.
Save ceessay/2c7f9eb4a2fb15ca5539e0f0d33d2bab to your computer and use it in GitHub Desktop.
Box alignments in RN
import React, {Component} from 'react';
import {
Text,
View,
SafeAreaView,
StyleSheet,
StatusBar,
TouchableOpacity,
Image,
PixelRatio,
ImageBackground,
} from 'react-native';
import Spinner from 'react-native-loading-spinner-overlay';
import {RNCamera} from 'react-native-camera';
import ImageEditor from '@react-native-community/image-editor';
import vision from '@react-native-firebase/ml-vision';
import Colors from '../constants/Colors';
import FontSizes from '../constants/FontSizes';
import Layout from '../constants/Layout';
import {Icon} from 'react-native-elements';
import {
calcWidth,
calcHeight,
calcItemWidth,
calcItemHeight,
} from '../helpers/utils';
import TagInput from '../components/TagInput';
import Dialog from 'react-native-dialog';
import {TextInputMask} from 'react-native-masked-text';
const WIDTH = Layout.window.width;
const HEIGHT = Layout.window.height;
class ScannerScreen extends Component {
constructor(props) {
super(props);
this.state = {
photoUri: null,
focusedScreen: true,
scan: false,
zoomValue: 0,
textElements: [],
selectedElements: [],
stringValue: null,
showModal: false,
flashMode: false,
inputValid: false,
isLoading: false,
};
}
static navigationOptions = {
header: null,
};
// static navigationOptions = ({navigation, navigationOptions}) => {
// return {
// title: 'Scanner',
// headerStyle: {
// backgroundColor: 'transparent',
// },
// // headerTintColor: 'transparent',
// headerTransparent: true,
// };
// };
takePicture = async () => {
this.setState({isLoading: true});
try {
const options = {
quality: 0.8,
base64: true,
skipProcessing: true,
};
const picture = await this.camera.takePictureAsync(options);
console.log('picture', picture);
const {uri, width, height} = picture;
this.setState({
isLoading: false,
photoUri: uri,
imgPixelHeight: height,
imgPixelWidth: width,
ratio: width / height,
});
} catch (e) {
console.warn(e);
this.setState({isLoading: false});
}
};
reset = () => {
this.setState({
newPhotos: false,
photoUri: null,
textElements: [],
selectedElements: [],
stringValue: null,
});
};
processImage = async () => {
this.setState({isLoading: true});
const {photoUri} = this.state;
console.log('processing Image...');
vision()
.textRecognizerProcessImage(photoUri)
.then(result => {
console.log('processImage response', result);
this.displayProcessResult(result);
this.setState({isLoading: false});
})
.catch(error => {
console.log('process error', error);
this.setState({isLoading: false});
});
};
displayProcessResult(result) {
const textElements = this.getElements(result);
this.setState({displayRectangles: true, textElements});
}
getElements(result) {
let allElements = [];
const {blocks} = result;
blocks.forEach(block => {
const {lines} = block;
lines.forEach(line => {
const {elements} = line;
allElements = [...allElements, ...elements];
});
});
// console.log('displayProcessResult response', allElements);
return allElements;
}
drawRectangles() {
const {textElements} = this.state;
return textElements.map((element, index) => this.drawBox(element, index));
}
drawBox = (element, index) => {
// return null;
let [left, top, right, bottom] = element.boundingBox;
let {imageWidh, imageHeight, imgPixelWidth, imgPixelHeight} = this.state;
const density = PixelRatio.get();
const imageActuelPxWidth = PixelRatio.roundToNearestPixel(imageWidh);
const imageActuelPxHeight = PixelRatio.roundToNearestPixel(imageHeight);
const widthRatio = imgPixelWidth / imageActuelPxWidth;
const heightRatio = imgPixelHeight / imageActuelPxHeight;
left = left / widthRatio;
right = right / widthRatio;
top = top / heightRatio;
bottom = bottom / heightRatio;
if (index === 0) {
console.log('imgPixelWidth', imgPixelWidth);
console.log('imageActuelPxWidth', imageActuelPxWidth);
console.log('imgPixelHeight', imgPixelHeight);
console.log('imageActuelPxHeight', imageActuelPxHeight);
console.log('widthRatio', widthRatio);
console.log('heightRatio', heightRatio);
console.log('hori', left, right);
console.log('vert', top, bottom);
// console.log('elements box', element);
}
const boxWidth = Math.abs(left - right);
const boxHeight = Math.abs(top - bottom);
const isSelected = this.state.selectedElements.includes(index);
const color = isSelected ? Colors.green : Colors.yellow;
return (
<TouchableOpacity
onPress={() => this.onBoxSelect(index)}
key={index}
style={{
position: 'absolute',
left,
top,
width: boxWidth,
height: boxHeight,
borderWidth: 2,
borderRadius: 4,
borderColor: color,
zIndex: 2000,
}}
/>
);
};
onBoxSelect = item => {
const {selectedElements} = this.state;
if (selectedElements.includes(item)) {
const indexToRemove = selectedElements.indexOf(item);
if (indexToRemove > -1) {
selectedElements.splice(indexToRemove, 1);
}
} else {
selectedElements.push(item);
}
this.setState({selectedElements});
};
onRemoveItemPress = indexToRemove => {
console.log('onRemovePress', indexToRemove);
const {selectedElements} = this.state;
selectedElements.splice(indexToRemove, 1);
this.setState({selectedElements});
};
onValidate = () => {
const {selectedElements, textElements} = this.state;
const values = selectedElements.map(item => textElements[item].text);
const stringValue = values.join('');
console.log('values', stringValue);
this.setState({stringValue, showModal: true});
};
onSubmit = () => {
this.setState({showModal: false}, () => {
this.props.navigation.state.params.onGoBack(
this.state.stringValue,
this.state.photoUri,
);
this.props.navigation.goBack();
});
};
renderCameraOrImage() {
// render cam or img
const {photoUri, flashMode} = this.state;
return (
<View style={{flex: 9}}>
{!photoUri ? (
<RNCamera
ref={ref => {
this.camera = ref;
}}
style={styles.camera}
type={RNCamera.Constants.Type.back}
flashMode={
flashMode
? RNCamera.Constants.FlashMode.torch
: RNCamera.Constants.FlashMode.off
}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
zoom={this.state.zoomValue}
>
<TouchableOpacity
style={{
alignSelf: 'flex-end',
marginTop: '98%',
marginRight: 20,
padding: 10,
}}
onPress={() => {
this.setState({flashMode: !this.state.flashMode});
}}
>
<Icon
name={flashMode ? 'ios-flash-off' : 'ios-flash'}
type="ionicon"
size={40}
color={Colors.white}
/>
</TouchableOpacity>
</RNCamera>
) : (
<ImageBackground
source={{uri: this.state.photoUri}}
style={{width: WIDTH, height: WIDTH * this.state.ratio}}
// resizeMode={'center'}
// resizeMethod={'scale'}
onLayout={event => {
var {x, y, width, height} = event.nativeEvent.layout;
console.log('native evente', event.nativeEvent);
this.setState({
imageWidh: width,
imageHeight: height,
imageX: x,
imageY: y,
});
}}
>
{this.renderImageOverLay()}
</ImageBackground>
)}
</View>
);
}
renderImageOverLay() {
let {imageWidh, imageHeight, imageX, imageY, textElements} = this.state;
return (
<View
style={{
position: 'absolute',
top: imageY,
left: imageX,
width: imageWidh,
height: imageHeight,
borderColor: 'red',
zIndex: 2000,
}}
>
<View
style={{
width: imageWidh,
height: imageHeight,
position: 'relative',
borderWidth: 2,
borderColor: 'red',
}}
>
{textElements.length > 0 &&
textElements.map((element, index) => this.drawBox(element, index))}
</View>
</View>
);
}
renderButtons() {
const {photoUri, selectedElements, textElements} = this.state;
const hasProcessed = textElements.length > 0;
const hasSelected = selectedElements.length > 0;
return (
<View
style={{
flexDirection: 'row',
backgroundColor: Colors.transparent,
alignItems: 'center',
paddingHorizontal: 8,
width: WIDTH,
height: HEIGHT * 0.1,
}}
>
<View style={{width: '10%'}}>
{!photoUri ? (
<TouchableOpacity
style={{flex: 1, justifyContent: 'center'}}
onPress={() => {
this.props.navigation.pop();
}}
>
<Icon name="ios-close" type="ionicon" size={35} />
</TouchableOpacity>
) : (
<TouchableOpacity
style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}
onPress={() => this.reset()}
>
<Icon
name="ios-trash"
type="ionicon"
color={Colors.red}
size={30}
/>
</TouchableOpacity>
)}
</View>
<View style={{width: '80%', alignItems: 'center'}}>
{!photoUri && (
<TouchableOpacity
style={{
backgroundColor: Colors.red,
height: 50,
width: 50,
borderRadius: 50 / 2,
}}
onPress={() => this.takePicture()}
>
<View />
</TouchableOpacity>
)}
{photoUri && !hasProcessed && (
<TouchableOpacity
style={{
backgroundColor: Colors.green,
height: 50,
width: 50,
borderRadius: 50 / 2,
alignItems: 'center',
justifyContent: 'center',
}}
onPress={() => this.processImage()}
>
<Icon
name="barcode-scan"
type="material-community"
color={Colors.white}
size={20}
/>
</TouchableOpacity>
)}
{photoUri && hasProcessed && (
<View
style={{
height: '100%',
borderWidth: 1,
borderRadius: 8,
borderColor: Colors.tintColor,
}}
>
{this.renderInput()}
</View>
)}
</View>
<View style={{width: '10%'}}>
{photoUri && hasProcessed && (
<TouchableOpacity
style={{flex: 1, justifyContent: 'center'}}
onPress={this.onValidate}
>
<Icon
name="ios-checkmark-circle"
type="ionicon"
color={Colors.green}
size={30}
/>
</TouchableOpacity>
)}
</View>
</View>
);
}
renderInput() {
const {selectedElements, textElements} = this.state;
const values = selectedElements.map(item => textElements[item]);
if (!values.length) {
return (
<Text
style={{
flex: 1,
padding: 8,
alignSelf: 'stretch',
textAlign: 'center',
textAlignVertical: 'center',
color: Colors.tintColor,
}}
>
Sélectionner les valeurs de la mesure
</Text>
);
}
return (
<TagInput
placeholder={'Selectionner ls valeurs'}
values={values}
onRemovePress={index => this.onRemoveItemPress(index)}
/>
);
}
isInputvalid = () => {
let result = true;
const input = this.state.stringValue;
const validChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'];
if (input && input.length) {
for (let index = 0; index < input.length; index++) {
if (!validChars.includes(input[index])) {
result = false;
break;
}
}
} else {
result = false;
}
this.setState({isInputvalid: result});
};
renderDialog() {
const {stringValue, showModal, inputValid} = this.state;
console.log('renderDialog stringValue', stringValue);
return (
<Dialog.Container visible={showModal}>
<Dialog.Title>Valider le relevé</Dialog.Title>
<TextInputMask
options={{
mask: '*** *** *** ***',
}}
underlineColorAndroid="transparent"
type={'custom'}
style={styles.inputContainerStyle}
onSubmitEditing={this.onSubmit}
blurOnSubmit={true}
keyboardType={'number-pad'}
value={stringValue}
onChangeText={text => {
this.setState({stringValue: text}, () => {
this.isInputvalid();
});
}}
placeholder="*** *** *** ***"
label="Relevé"
textAlign={'center'}
autoFocus={false}
/>
<View style={{height: 50}}>
{!inputValid && (
<Text
style={{
color: Colors.red,
}}
>
La saisie comporte des caractères invalides. Veuillez reprendre.
</Text>
)}
</View>
<Dialog.Button
color={Colors.tintColor}
onPress={() => {
this.setState({showModal: false});
}}
label="Annuler"
/>
<Dialog.Button
// disabled={!inputValid}
color={Colors.tintColor}
onPress={this.onSubmit}
label="Confirmer"
/>
</Dialog.Container>
);
}
render() {
return (
<View style={styles.container}>
<StatusBar hidden />
<Spinner
visible={this.state.isLoading}
textContent={'Traitemnt en cours...'}
textStyle={styles.spinnerTextStyle}
/>
<View>
{this.renderCameraOrImage()}
{this.renderButtons()}
{this.renderDialog()}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
// justifyContent: 'center',
alignItems: 'center',
},
camera: {
flex: 1,
width: WIDTH,
height: HEIGHT * 0.9,
},
cameraWrapper: {
width: WIDTH,
height: HEIGHT,
justifyContent: 'center',
alignItems: 'center',
},
sliderContainer: {
position: 'absolute',
bottom: 150,
left: 200,
right: 30,
},
buttonsContainer: {
position: 'absolute',
bottom: 0,
left: 0,
width: WIDTH,
padding: 15,
marginBottom: 20,
flexDirection: 'row',
justifyContent: 'space-between',
},
cancelButtonContainer: {
width: 80,
height: 80,
// borderColor: "gray",
// borderWidth: 1,
paddingHorizontal: 10,
marginBottom: 2,
marginVertical: 10,
justifyContent: 'center',
alignItems: 'center',
},
shotButtonContainer: {
width: 80,
height: 80,
paddingHorizontal: 10,
marginBottom: 2,
marginVertical: 10,
justifyContent: 'center',
alignItems: 'center',
},
scanButtonContainer: {
width: 80,
height: 80,
paddingHorizontal: 10,
marginBottom: 2,
marginVertical: 10,
justifyContent: 'center',
alignItems: 'center',
},
buttonText: {
color: Colors.white,
},
inputContainerStyle: {
borderBottomWidth: 1,
borderRadius: 10,
borderColor: Colors.tintDark,
paddingHorizontal: 10,
fontSize: FontSizes.biggest,
width: '80%',
marginVertical: 16,
},
spinnerTextStyle: {
color: '#FFF',
},
});
export default ScannerScreen;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment