cssreact-nativereact-native-camera

React native camera with a transparent view for barcode scanner mask


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?


Solution

  • 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.