1import { BarCodeScanner, BarCodePoint, BarCodeEvent, BarCodeBounds } from 'expo-barcode-scanner'; 2import * as Permissions from 'expo-permissions'; 3import * as ScreenOrientation from 'expo-screen-orientation'; 4import React from 'react'; 5import { Button, Platform, StyleSheet, Text, View } from 'react-native'; 6import * as Svg from 'react-native-svg'; 7import { NavigationEvents } from 'react-navigation'; 8 9const BUTTON_COLOR = Platform.OS === 'ios' ? '#fff' : '#666'; 10 11interface State { 12 isPermissionsGranted: boolean; 13 type: any; 14 cornerPoints?: BarCodePoint[]; 15 alerting: boolean; 16 haveDimensions: boolean; 17 canvasHeight?: number; 18 canvasWidth?: number; 19 boundingBox?: BarCodeBounds; 20 cornerPointsString?: string; 21 showBoundingBox: boolean; 22 showText: boolean; 23 data: string; 24} 25 26export default class BarcodeScannerExample extends React.Component<object, State> { 27 static navigationOptions = { 28 title: '<BarCodeScanner />', 29 }; 30 31 canChangeOrientation = false; 32 33 readonly state: State = { 34 isPermissionsGranted: false, 35 type: BarCodeScanner.Constants.Type.back, 36 alerting: false, 37 haveDimensions: false, 38 showBoundingBox: false, 39 data: '', 40 showText: false, 41 }; 42 43 componentDidFocus = async () => { 44 const { status } = await Permissions.askAsync(Permissions.CAMERA); 45 this.setState({ isPermissionsGranted: status === 'granted' }); 46 }; 47 48 toggleAlertingAboutResult = () => { 49 this.setState(({ alerting }) => ({ 50 alerting: !alerting, 51 })); 52 }; 53 54 toggleScreenOrientationState = () => { 55 if (this.canChangeOrientation) { 56 ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP); 57 } else { 58 ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.ALL); 59 } 60 this.canChangeOrientation = !this.canChangeOrientation; 61 }; 62 63 setCanvasDimensions = (e: any) => { 64 this.setState({ 65 canvasWidth: e.nativeEvent.layout.width, 66 canvasHeight: e.nativeEvent.layout.height, 67 haveDimensions: true, 68 }); 69 }; 70 71 render() { 72 if (!this.state.isPermissionsGranted) { 73 return ( 74 <View style={styles.container}> 75 <NavigationEvents onDidFocus={this.componentDidFocus} /> 76 <Text>You have not granted permission to use the camera on this device!</Text> 77 </View> 78 ); 79 } 80 81 const circles = []; 82 83 if (this.state.cornerPoints) { 84 for (const point of this.state.cornerPoints) { 85 circles.push( 86 <Svg.Circle 87 cx={point.x} 88 cy={point.y} 89 r={3} 90 strokeWidth={0.5} 91 stroke="#CF4048" 92 fill="#CF4048" 93 /> 94 ); 95 } 96 } 97 98 return ( 99 <View style={styles.container}> 100 <BarCodeScanner 101 onLayout={this.setCanvasDimensions} 102 onBarCodeScanned={this.handleBarCodeScanned} 103 barCodeTypes={[ 104 BarCodeScanner.Constants.BarCodeType.qr, 105 BarCodeScanner.Constants.BarCodeType.pdf417, 106 BarCodeScanner.Constants.BarCodeType.code128, 107 BarCodeScanner.Constants.BarCodeType.code39, 108 ]} 109 type={this.state.type} 110 style={styles.preview} 111 /> 112 113 {this.state.haveDimensions && ( 114 <Svg.Svg 115 height={this.state.canvasHeight} 116 width={this.state.canvasWidth} 117 style={styles.svg}> 118 <Svg.Circle 119 cx={this.state.canvasWidth! / 2} 120 cy={this.state.canvasHeight! / 2} 121 r={2} 122 strokeWidth={2.5} 123 stroke="#e74c3c" 124 fill="#f1c40f" 125 /> 126 {this.state.showBoundingBox && this.state.cornerPointsString && ( 127 <Svg.Polygon 128 points={this.state.cornerPointsString} 129 strokeWidth={2} 130 stroke="#582E6E" 131 fill="none" 132 /> 133 )} 134 {this.state.showText && this.state.boundingBox && ( 135 <Svg.Text 136 fill="#CF4048" 137 stroke="#CF4048" 138 fontSize="14" 139 x={this.state.boundingBox.origin.x} 140 y={this.state.boundingBox.origin.y - 8}> 141 {this.state.data} 142 </Svg.Text> 143 )} 144 145 {circles} 146 </Svg.Svg> 147 )} 148 149 <View style={styles.toolbar}> 150 <Button color={BUTTON_COLOR} title="Direction" onPress={this.toggleType} /> 151 <Button 152 color={BUTTON_COLOR} 153 title="Orientation" 154 onPress={this.toggleScreenOrientationState} 155 /> 156 <Button color={BUTTON_COLOR} title="Bounding box" onPress={this.toggleBoundingBox} /> 157 <Button color={BUTTON_COLOR} title="Text" onPress={this.toggleText} /> 158 <Button color={BUTTON_COLOR} title="Alerting" onPress={this.toggleAlertingAboutResult} /> 159 </View> 160 </View> 161 ); 162 } 163 164 toggleType = () => 165 this.setState(({ type }) => ({ 166 type: 167 type === BarCodeScanner.Constants.Type.back 168 ? BarCodeScanner.Constants.Type.front 169 : BarCodeScanner.Constants.Type.back, 170 })); 171 172 toggleText = () => 173 this.setState(({ showText }) => ({ 174 showText: !showText, 175 })); 176 177 toggleBoundingBox = () => 178 this.setState(({ showBoundingBox }) => ({ 179 showBoundingBox: !showBoundingBox, 180 })); 181 182 getPointsString = (barCodePoints?: BarCodePoint[]): string | undefined => { 183 if (!barCodePoints) { 184 return; 185 } 186 return barCodePoints.map(({ x, y }) => `${Math.round(x)},${Math.round(y)}`).join(' '); 187 }; 188 189 handleBarCodeScanned = (barCodeEvent: BarCodeEvent) => { 190 if (this.state.alerting) { 191 requestAnimationFrame(() => { 192 alert(JSON.stringify(barCodeEvent)); 193 }); 194 } 195 this.setState({ 196 data: barCodeEvent.data, 197 cornerPoints: barCodeEvent.cornerPoints, 198 boundingBox: barCodeEvent.bounds, 199 cornerPointsString: this.getPointsString(barCodeEvent.cornerPoints), 200 }); 201 }; 202} 203 204const styles = StyleSheet.create({ 205 container: { 206 flex: 1, 207 }, 208 preview: { 209 ...StyleSheet.absoluteFillObject, 210 backgroundColor: 'black', 211 }, 212 toolbar: { 213 position: 'absolute', 214 bottom: 0, 215 left: 0, 216 right: 0, 217 paddingVertical: 10, 218 paddingHorizontal: 10, 219 flexDirection: 'row', 220 justifyContent: 'space-between', 221 backgroundColor: 'rgba(255,255,255,0.2)', 222 }, 223 svg: { 224 position: 'absolute', 225 borderWidth: 2, 226 borderColor: 'red', 227 }, 228}); 229