React native camera with a transparent view for barcode scanner mask

FisNaN picture FisNaN · Jan 29, 2018 · Viewed 20.9k times · Source

How to add a mask on top of the react-native-camera?

I'm building the UI for a React Native QRCode scanner app using react-native-camera.

The overlay mask on top of the camera should be in light grey color, but the middle part must keep transparent (see-through).

But when I change the backgroundColor on my outer mask, it seems also affect the center part. I mean, of course, it is behind its child view.

The code down below is a simplified version of the snapshot.

<Camera
  ref={cam => {
    this.camera = cam;
  }}
  onBarCodeRead={this._onBarCodeRead}
  style={styles.cameraView}
  aspect={Camera.constants.Aspect.fill}
  playSoundOnCapture
>
  <View
    style={{
      position: 'absolute',
      top: 0,
      left: 0,
      width: '100%',
      height: '100%',
      backgroundColor: 'rgba(0.2, 0.2, 0.2, 0.2)',
      alignItems: 'center',
      justifyContent: 'space-around',
    }}
  >
    <View
      style={{
        width: 300,
        height: 300,
        backgroundColor: 'transparent',
        borderColor: 'white',
        borderWidth: 1,
      }}
    />
  </View>
</Camera>

The basic idea is here

Any idea how to get this done?

Answer

FisNaN picture FisNaN · Feb 1, 2018

I finally figure out this one. The idea is to create 3 rows like a burger and then calculate the height and width at runtime.

The center row has 3 view components, the middle one has a transparent background and white border.

(the value, 300, comes from the size of the center view (transparent area), I divided it by 10 to compute a smaller ratio for flexboxes)

Barcode mask demo

export default class CameraScreen extends React.Component<any, any> {
  render() {
    const { height, width } = Dimensions.get('window');
    const maskRowHeight = Math.round((AppStore.height - 300) / 20);
    const maskColWidth = (width - 300) / 2;

    return (
      <View style={styles.container}>
        <Camera
          ref={cam => {
            this.camera = cam;
          }}
          onBarCodeRead={this._onBarCodeRead}
          style={styles.cameraView}
          aspect={Camera.constants.Aspect.fill}
          playSoundOnCapture
        >
          <View style={styles.maskOutter}>
            <View style={[{ flex: maskRowHeight  }, styles.maskRow, styles.maskFrame]} />
             <View style={[{ flex: 30 }, styles.maskCenter]}>
             <View style={[{ width: maskColWidth }, styles.maskFrame]} />
             <View style={styles.maskInner} />
            <View style={[{ width: maskColWidth }, styles.maskFrame]} />
          </View>
        <View style={[{ flex: maskRowHeight }, styles.maskRow, styles.maskFrame]} />
      </View>
        </Camera>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  cameraView: {
    flex: 1,
    justifyContent: 'flex-start',
  },
  maskOutter: {
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    alignItems: 'center',
    justifyContent: 'space-around',
  },
  maskInner: {
    width: 300,
    backgroundColor: 'transparent',
    borderColor: 'white',
    borderWidth: 1,
  },
  maskFrame: {
    backgroundColor: 'rgba(1,1,1,0.6)',
  },
  maskRow: {
    width: '100%',
  },
  maskCenter: { flexDirection: 'row' },
});

Update: The height ratio changes between the different brand phones depend on it uses physical/soft buttons. I replaced the fixed height with flex instead.