vision-camera-code-scanner
vision-camera-code-scanner copied to clipboard
add a frame for aiming scanning
Somewhere I came across instructions on how to do this, but I can't find it anymore... Does anyone happen to know?
It is very difficult to write such a frame yourself. I am sure there must be a ready-made solution...
I'm currently working on this. I have a working iOS version here.
https://user-images.githubusercontent.com/51379148/217338554-d7ec76ce-0a8f-4cc1-a920-1d83f70bb3b2.mov
const frameProcessor = useFrameProcessor((frame) => {
'worklet';
const squareSize = frame.width * MARKER_WIDTH_RATIO - 100;
const scanFrame: ScanFrame = {
width: squareSize,
height: squareSize,
x: (frame.width - squareSize) / 2,
y: (frame.height - squareSize) / 2,
};
const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], {
checkInverted: true,
scanFrame: scanFrame,
});
runOnJS(setBarcodes)(detectedBarcodes);
}, []);
@rodgomesc is that a feature you would like to add to the the library ? If so I can start working on a PR.
I wish it was for android...
I plan on making the feature available on Android as well, I just don't have an Android device to test it on right now.
@severinferard Why you have 2 barcodes when you scan the QR code ?
I am also interested in this feature. Would be cool if you manage to finish it.
I'm currently working on this. I have a working iOS version here.
const frameProcessor = useFrameProcessor((frame) => { 'worklet'; const squareSize = frame.width * MARKER_WIDTH_RATIO - 100; const scanFrame: ScanFrame = { width: squareSize, height: squareSize, x: (frame.width - squareSize) / 2, y: (frame.height - squareSize) / 2, }; const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], { checkInverted: true, scanFrame: scanFrame, }); runOnJS(setBarcodes)(detectedBarcodes); }, []);
@rodgomesc is that a feature you would like to add to the the library ? If so I can start working on a PR.
I'm need this for iOS and Android :(
I managed to make a crooked but working solution on RN. If you're interested, I can post it....
@valery-lavrik can you please post it? I am interested
Just do not judge strictly, I have little such experience. If you have any ideas what can be improved, please let me know
import React, { useState, useRef, useEffect } from 'react';
import { View, StyleSheet, Alert, Dimensions } from 'react-native';
import { runOnJS } from 'react-native-reanimated';
import { Camera, useCameraDevices, useFrameProcessor } from 'react-native-vision-camera';
import { scanBarcodes, BarcodeFormat } from 'vision-camera-code-scanner';
let rotateListener = null;
export default function QRScanner({
onScan = () => { },
}) {
const AimingBorderSize = useRef(null);
const isFind = useRef(false);
const [qrBorders, setQrBorders] = useState([]);
const devices = useCameraDevices();
const device = devices.back;
const barCodesHandler = (detectedBarcodes, frame) => {
if (!!isFind?.current) {
return null;
}
if (!detectedBarcodes?.length) {
setQrBorders([]);
return null;
}
// 1) отфильтрую одинаковые
const codeFiltered = detectedBarcodes.filter((value, index, self) => self.findIndex((el, i) => el.content.data === value.content.data) === index);
// 2) нарисую им рамки
const bordersStyles = [];
const win = Dimensions.get('window');
const isPortal = win.height > win.width;
const rH = win.height / (isPortal ? Math.max(frame.height, frame.width) : Math.min(frame.height, frame.width));
const rW = win.width / (isPortal ? Math.min(frame.height, frame.width) : Math.max(frame.height, frame.width));
for (let i = 0; i < codeFiltered.length; i++) {
const b = codeFiltered[i];
bordersStyles.push({
inside: false,
boundingBox: {
bottom: b.boundingBox.bottom * rH,
left: b.boundingBox.left * rW,
right: b.boundingBox.right * rW,
top: b.boundingBox.top * rH,
},
style: {
borderWidth: 1,
borderColor: 'red',
position: 'absolute',
top: (Math.min(b.cornerPoints[0].y, b.cornerPoints[1].y)) * rH, // b.cornerPoints[1].y
left: (Math.min(b.cornerPoints[0].x, b.cornerPoints[3].x)) * rW, // b.cornerPoints[3].x
height: (b.cornerPoints[3].y - b.cornerPoints[0].y) * rH,
width: (b.cornerPoints[1].x - b.cornerPoints[0].x) * rW,
},
});
if (isInside(bordersStyles[bordersStyles.length - 1])) {
bordersStyles[bordersStyles.length - 1].inside = true;
bordersStyles[bordersStyles.length - 1].style.borderColor = '#00FF00';
bordersStyles[bordersStyles.length - 1].style.borderWidth = 2;
}
}
setQrBorders(bordersStyles);
// 3) отправлю в коллбек
const insideIndex = bordersStyles.findIndex(el => el.inside);
if (insideIndex !== -1) {
isFind.current = true;
onScan(codeFiltered[insideIndex].displayValue.toLowerCase().trim());
}
};
const isInside = (oneBord) => {
const ab = AimingBorderSize.current;
const bb = oneBord.boundingBox;
if (bb.top > ab.yt && bb.bottom < ab.yb && bb.left > ab.xl && bb.right < ab.xr) {
return true;
}
return false;
};
const frameProcessor = useFrameProcessor((frame) => {
'worklet';
const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], { checkInverted: true });
runOnJS(barCodesHandler)(detectedBarcodes, frame);
}, []);
return (
<View style={{ flex: 1 }}>
{device != null && (
<>
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
frameProcessor={frameProcessor}
frameProcessorFps={5}
/>
{/* Зеленая рамка для прицеливания */}
<FrameForAiming getAimingBorderPos={(b) => (AimingBorderSize.current = b)} />
{/* Подсвечу все что распозналось */}
{!!qrBorders?.length && (
qrBorders.map((b, k) => (<View key={k} style={b.style} />))
)}
</>
)}
</View>
);
}
export function FrameForAiming({
getAimingBorderPos = () => { },
}) {
const [H, setH] = React.useState(0);
const maxLayouts = 5;
const curLayouts = useRef(0);
useEffect(() => {
rotateListener = Dimensions.addEventListener('change', () => {
curLayouts.current = 0;
setH(0);
});
return () => {
rotateListener?.remove();
};
});
return (
<View style={ST_.ScreenWrap}>
<View
style={[ST_.wrap, { height: H }]}
onLayout={(e) => {
if (curLayouts.current < maxLayouts) {
const l = e.nativeEvent.layout;
l.height = l.width;
setH(parseInt(l.width, 10));
getAimingBorderPos({
xl: parseInt(l.x, 10),
xr: parseInt(l.x + l.width, 10),
yt: parseInt(l.y, 10),
yb: parseInt(l.y + l.height, 10),
});
curLayouts.current++;
}
}}
>
<View style={ST_.tlb} />
<View style={ST_.trb} />
<View style={ST_.blb} />
<View style={ST_.brb} />
</View>
</View>
);
}
const BorderWidth = 1;
const BorderStyle = {
position: 'absolute',
width: '35%',
height: '35%',
borderColor: '#00FF00',
};
const ST_ = {
ScreenWrap: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
wrap: {
width: '45%',
},
tlb: {
top: 0,
left: 0,
borderTopWidth: BorderWidth,
borderLeftWidth: BorderWidth,
...BorderStyle,
},
trb: {
top: 0,
right: 0,
borderTopWidth: BorderWidth,
borderRightWidth: BorderWidth,
...BorderStyle,
},
blb: {
bottom: 0,
left: 0,
borderBottomWidth: BorderWidth,
borderLeftWidth: BorderWidth,
...BorderStyle,
},
brb: {
bottom: 0,
right: 0,
borderBottomWidth: BorderWidth,
borderRightWidth: BorderWidth,
...BorderStyle,
},
};
I took inspiration from @valery-lavrik code and came up with the following solution.
index.tsx
import { Text, TouchableRipple, useTheme } from "react-native-paper"
import { AppTheme } from "../../theme"
import createStyles from "./styles"
import { NativeStackScreenProps } from "@react-navigation/native-stack"
import { RootStackParamList } from "../../navigation/RootStackParamList"
import { RouteName } from "../../navigation/RouteName"
import { Camera, useCameraDevices, useFrameProcessor } from "react-native-vision-camera"
import { Dimensions, Platform, StyleSheet, View } from "react-native"
import { useEffect, useState } from "react"
import Icon from "react-native-vector-icons/MaterialIcons";
import { Barcode, BarcodeFormat, scanBarcodes } from "vision-camera-code-scanner"
import Log from "../../utils/log"
import { RNHoleView } from 'react-native-hole-view';
import { heightPercentageToDP, widthPercentageToDP } from 'react-native-responsive-screen'
import { SafeAreaView } from 'react-native-safe-area-context'
import { runOnJS } from 'react-native-reanimated'
type Props = NativeStackScreenProps<RootStackParamList, RouteName.CameraScanner>
const CameraScanner = ({ navigation, route }: Props) => {
const TAG = CameraScanner.name
const theme = useTheme<AppTheme>()
const styles = createStyles(theme)
const [hasPermission, setHasPermission] = useState(false);
const [barcodes, setBarcodes] = useState<Barcode[]>([])
const [isScanned, setIsScanned] = useState<boolean>(false);
const devices = useCameraDevices()
const device = devices.back
const vf_x = widthPercentageToDP('8.5%')
const vf_y = heightPercentageToDP('20%')
const vf_width = widthPercentageToDP('83%')
const vf_height = heightPercentageToDP('25%')
const win = Dimensions.get('window');
useEffect(() => {
requestPermissions()
}, [])
useEffect(() => {
toggleActiveState()
return () => {
barcodes;
}
}, [barcodes])
const toggleActiveState = async () => {
if (barcodes && barcodes.length > 0 && isScanned === false) {
setIsScanned(true)
if (barcodes[0].rawValue !== '') {
setBarcodes([barcodes[0]]);
Log(TAG, 'scanned barcode = ' + barcodes[0].rawValue)
}
}
}
const frameProcessor = useFrameProcessor(frame => {
"worklet"
const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.ALL_FORMATS], { checkInverted: true })
if (detectedBarcodes?.length) {
// console.log('frameProcessor Barcodes :' + JSON.stringify(detectedBarcodes))
var handleBarcode = function () {
// Looked into sample code from https://github.com/rodgomesc/vision-camera-code-scanner/issues/125
for (let barcode of detectedBarcodes) {
const isPortal = win.height > win.width;
const rH = win.height / (isPortal ? Math.max(frame.height, frame.width) : Math.min(frame.height, frame.width));
const rW = win.width / (isPortal ? Math.min(frame.height, frame.width) : Math.max(frame.height, frame.width));
var boundingBottom = (barcode.boundingBox?.bottom ?? 0) * rH
var boundingLeft = (barcode.boundingBox?.left ?? 0) * rW
var boundingRight = (barcode.boundingBox?.right ?? 0) * rW
var boundingTop = (barcode.boundingBox?.top ?? 0) * rH
// iOS - wasn't sending bounding box so using cornerPoints instead
// cornerPoints
// The four corner points of the barcode, in clockwise order starting with the top left relative to the detected image in the view coordinate system.
// These are CGPoints wrapped in NSValues. Due to the possible perspective distortions, this is not necessarily a rectangle.
// https://developers.google.com/ml-kit/reference/swift/mlkitbarcodescanning/api/reference/Classes/Barcode#cornerpoints
if (Platform.OS !== 'android') {
boundingLeft = (barcode.cornerPoints?.[0].x ?? 0) * rW
boundingRight = (barcode.cornerPoints?.[1].x ?? 0) * rW
boundingTop = (barcode.cornerPoints?.[0].y ?? 0) * rH
boundingBottom = (barcode.cornerPoints?.[2].y ?? 0) * rH
}
// console.log(
// 'Bounding Box Bottom = ' + boundingBottom +
// ' Left = ' + boundingLeft +
// ' Right = ' + boundingRight +
// ' Top = ' + boundingTop
// )
const vf_bottom = vf_y + vf_height;
const vf_left = vf_x;
const vf_right = vf_x + vf_width;
const vf_top = vf_y;
// console.log(
// 'view finder Bottom = ' + vf_bottom +
// ' Left = ' + vf_left +
// ' Right = ' + vf_right +
// ' Top = ' + vf_top
// )
if (
boundingTop > vf_top &&
boundingBottom < vf_bottom &&
boundingLeft > vf_left &&
boundingRight < vf_right
) {
// console.log('Barcode is inside View Finder')
runOnJS(setBarcodes)([barcode])
break
}
}
}
handleBarcode()
}
}, [])
const requestPermissions = async () => {
const status = await Camera.requestCameraPermission()
setHasPermission(status === "authorized")
}
return (
<View
style={styles.container}
>
{
device !== undefined &&
hasPermission && frameProcessor !== undefined && (
<>
<Camera
style={StyleSheet.absoluteFill}
device={device}
audio={false}
isActive={!isScanned}
frameProcessor={frameProcessor}
frameProcessorFps={5}
/>
<RNHoleView
holes={[
{
x: vf_x,
y: vf_y,
width: vf_width,
height: vf_height,
borderRadius: 10,
},
]}
style={styles.rnholeView}
/>
<View
style={{
height: 2,
width: widthPercentageToDP('83%'),
marginStart: widthPercentageToDP('8.5%'),
marginTop: heightPercentageToDP('32.5%'),
backgroundColor: 'red'
}}
/>
</>
)
}
<SafeAreaView
style={styles.safeAreaView}
>
<TouchableRipple
style={styles.backButton}
onPress={
() => {
navigation.goBack()
}
}
rippleColor={theme.colors.elevation.level0}
borderless={true}
>
<Icon
name="arrow-back-ios"
size={30}
color={theme.colors.onPrimary}
style={{ start: 10 }}
/>
</TouchableRipple>
{
barcodes.length ?
<View
style={styles.containerView}
>
<View
style={styles.barcodeDetectedView}
>
<Text
variant="titleMedium"
style={styles.barcodeTitle}
>
Barcode Detected
</Text>
<Text
style={styles.barcodeTextURL}
variant="titleLarge"
>
{barcodes[0].displayValue}
</Text>
</View>
</View>
:
<></>
}
</SafeAreaView>
</View>
)
}
export default CameraScanner
styles.ts
import { StyleSheet } from "react-native";
import { MD3Theme } from "react-native-paper";
const createStyles = (theme: MD3Theme) => StyleSheet.create({
container: {
backgroundColor: 'black',
color: 'black',
flex: 1,
},
backButton: {
borderRadius: 50,
height: 40,
width: 40,
marginEnd: 10,
left: 20,
top: 15,
},
rnholeView: {
position: 'absolute',
width: '100%',
height: '100%',
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0,0,0,0.5)',
},
safeAreaView: {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
containerView: {
flex: 1,
},
barcodeDetectedView: {
flex: 1,
justifyContent: 'flex-end',
paddingBottom: 40,
},
barcodeTitle: {
alignSelf: 'center',
color: 'white',
},
barcodeTextURL: {
fontSize: 20,
color: 'white',
alignSelf: 'center',
},
})
export default createStyles
I took inspiration from @valery-lavrik code and came up with the following solution.
index.tsx
import { Text, TouchableRipple, useTheme } from "react-native-paper" import { AppTheme } from "../../theme" import createStyles from "./styles" import { NativeStackScreenProps } from "@react-navigation/native-stack" import { RootStackParamList } from "../../navigation/RootStackParamList" import { RouteName } from "../../navigation/RouteName" import { Camera, useCameraDevices, useFrameProcessor } from "react-native-vision-camera" import { Dimensions, Platform, StyleSheet, View } from "react-native" import { useEffect, useState } from "react" import Icon from "react-native-vector-icons/MaterialIcons"; import { Barcode, BarcodeFormat, scanBarcodes } from "vision-camera-code-scanner" import Log from "../../utils/log" import { RNHoleView } from 'react-native-hole-view'; import { heightPercentageToDP, widthPercentageToDP } from 'react-native-responsive-screen' import { SafeAreaView } from 'react-native-safe-area-context' import { runOnJS } from 'react-native-reanimated' type Props = NativeStackScreenProps<RootStackParamList, RouteName.CameraScanner> const CameraScanner = ({ navigation, route }: Props) => { const TAG = CameraScanner.name const theme = useTheme<AppTheme>() const styles = createStyles(theme) const [hasPermission, setHasPermission] = useState(false); const [barcodes, setBarcodes] = useState<Barcode[]>([]) const [isScanned, setIsScanned] = useState<boolean>(false); const devices = useCameraDevices() const device = devices.back const vf_x = widthPercentageToDP('8.5%') const vf_y = heightPercentageToDP('20%') const vf_width = widthPercentageToDP('83%') const vf_height = heightPercentageToDP('25%') const win = Dimensions.get('window'); useEffect(() => { requestPermissions() }, []) useEffect(() => { toggleActiveState() return () => { barcodes; } }, [barcodes]) const toggleActiveState = async () => { if (barcodes && barcodes.length > 0 && isScanned === false) { setIsScanned(true) if (barcodes[0].rawValue !== '') { setBarcodes([barcodes[0]]); Log(TAG, 'scanned barcode = ' + barcodes[0].rawValue) } } } const frameProcessor = useFrameProcessor(frame => { "worklet" const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.ALL_FORMATS], { checkInverted: true }) if (detectedBarcodes?.length) { // console.log('frameProcessor Barcodes :' + JSON.stringify(detectedBarcodes)) var handleBarcode = function () { // Looked into sample code from https://github.com/rodgomesc/vision-camera-code-scanner/issues/125 for (let barcode of detectedBarcodes) { const isPortal = win.height > win.width; const rH = win.height / (isPortal ? Math.max(frame.height, frame.width) : Math.min(frame.height, frame.width)); const rW = win.width / (isPortal ? Math.min(frame.height, frame.width) : Math.max(frame.height, frame.width)); var boundingBottom = (barcode.boundingBox?.bottom ?? 0) * rH var boundingLeft = (barcode.boundingBox?.left ?? 0) * rW var boundingRight = (barcode.boundingBox?.right ?? 0) * rW var boundingTop = (barcode.boundingBox?.top ?? 0) * rH // iOS - wasn't sending bounding box so using cornerPoints instead // cornerPoints // The four corner points of the barcode, in clockwise order starting with the top left relative to the detected image in the view coordinate system. // These are CGPoints wrapped in NSValues. Due to the possible perspective distortions, this is not necessarily a rectangle. // https://developers.google.com/ml-kit/reference/swift/mlkitbarcodescanning/api/reference/Classes/Barcode#cornerpoints if (Platform.OS !== 'android') { boundingLeft = (barcode.cornerPoints?.[0].x ?? 0) * rW boundingRight = (barcode.cornerPoints?.[1].x ?? 0) * rW boundingTop = (barcode.cornerPoints?.[0].y ?? 0) * rH boundingBottom = (barcode.cornerPoints?.[2].y ?? 0) * rH } // console.log( // 'Bounding Box Bottom = ' + boundingBottom + // ' Left = ' + boundingLeft + // ' Right = ' + boundingRight + // ' Top = ' + boundingTop // ) const vf_bottom = vf_y + vf_height; const vf_left = vf_x; const vf_right = vf_x + vf_width; const vf_top = vf_y; // console.log( // 'view finder Bottom = ' + vf_bottom + // ' Left = ' + vf_left + // ' Right = ' + vf_right + // ' Top = ' + vf_top // ) if ( boundingTop > vf_top && boundingBottom < vf_bottom && boundingLeft > vf_left && boundingRight < vf_right ) { // console.log('Barcode is inside View Finder') runOnJS(setBarcodes)([barcode]) break } } } handleBarcode() } }, []) const requestPermissions = async () => { const status = await Camera.requestCameraPermission() setHasPermission(status === "authorized") } return ( <View style={styles.container} > { device !== undefined && hasPermission && frameProcessor !== undefined && ( <> <Camera style={StyleSheet.absoluteFill} device={device} audio={false} isActive={!isScanned} frameProcessor={frameProcessor} frameProcessorFps={5} /> <RNHoleView holes={[ { x: vf_x, y: vf_y, width: vf_width, height: vf_height, borderRadius: 10, }, ]} style={styles.rnholeView} /> <View style={{ height: 2, width: widthPercentageToDP('83%'), marginStart: widthPercentageToDP('8.5%'), marginTop: heightPercentageToDP('32.5%'), backgroundColor: 'red' }} /> </> ) } <SafeAreaView style={styles.safeAreaView} > <TouchableRipple style={styles.backButton} onPress={ () => { navigation.goBack() } } rippleColor={theme.colors.elevation.level0} borderless={true} > <Icon name="arrow-back-ios" size={30} color={theme.colors.onPrimary} style={{ start: 10 }} /> </TouchableRipple> { barcodes.length ? <View style={styles.containerView} > <View style={styles.barcodeDetectedView} > <Text variant="titleMedium" style={styles.barcodeTitle} > Barcode Detected </Text> <Text style={styles.barcodeTextURL} variant="titleLarge" > {barcodes[0].displayValue} </Text> </View> </View> : <></> } </SafeAreaView> </View> ) } export default CameraScanner
styles.ts
import { StyleSheet } from "react-native"; import { MD3Theme } from "react-native-paper"; const createStyles = (theme: MD3Theme) => StyleSheet.create({ container: { backgroundColor: 'black', color: 'black', flex: 1, }, backButton: { borderRadius: 50, height: 40, width: 40, marginEnd: 10, left: 20, top: 15, }, rnholeView: { position: 'absolute', width: '100%', height: '100%', alignSelf: 'center', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.5)', }, safeAreaView: { position: 'absolute', left: 0, top: 0, right: 0, bottom: 0, }, containerView: { flex: 1, }, barcodeDetectedView: { flex: 1, justifyContent: 'flex-end', paddingBottom: 40, }, barcodeTitle: { alignSelf: 'center', color: 'white', }, barcodeTextURL: { fontSize: 20, color: 'white', alignSelf: 'center', }, }) export default createStyles
In fact, with this implementation, the scanning range is still the entire camera area. When scanning multiple codes, it is still difficult to recognize the desired code.
I'm currently working on this. I have a working iOS version here.
scanFrame-3.mov
const frameProcessor = useFrameProcessor((frame) => { 'worklet'; const squareSize = frame.width * MARKER_WIDTH_RATIO - 100; const scanFrame: ScanFrame = { width: squareSize, height: squareSize, x: (frame.width - squareSize) / 2, y: (frame.height - squareSize) / 2, }; const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], { checkInverted: true, scanFrame: scanFrame, }); runOnJS(setBarcodes)(detectedBarcodes); }, []);
@rodgomesc is that a feature you would like to add to the the library ? If so I can start working on a PR.
Have you implemented this functionality on the Android version?
I found other solution work : https://www.dynamsoft.com/codepool/react-native-qr-code-scanner-vision-camera.html
I wish it was for android...
here's snippet for android. https://github.com/rodgomesc/vision-camera-code-scanner/commit/f4430376396f1bbaead5dc7a1b352c45abf02864
Here is a working overlay that I made from scratch using 4 customizable square corners wrapped by a bigger square container. Makes it easier to make a full rectangle or just use rounded corner like most QR readers use to "aim". To make it without gaps, just change size of squares to be half of the overlay and customize borderRadius to taste:
const OVERLAY_HEIGHT = 250;
const OVERLAY_WIDTH = 250;
const SQUARE_HEIGHT = 50;
const SQUARE_WIDTH = 50;
const SQUARE_BORDER_WIDTH = 3;
const styles = StyleSheet.create({
container: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
iconButton: {
position: 'absolute',
right: 10,
top: 10,
},
overlayContainer: {
flexDirection: 'column',
height: OVERLAY_HEIGHT,
width: OVERLAY_WIDTH,
},
rowContainer: {
flexDirection: 'row',
flex: 1,
justifyContent: 'space-between',
},
});
const SquareComponent = ({ corner }) => {
const { colors } = useTheme();
const borderRadius = 20;
let borderStyle = {};
switch (corner) {
case 'UpperLeft':
borderStyle = {
borderTopWidth: SQUARE_BORDER_WIDTH,
borderLeftWidth: SQUARE_BORDER_WIDTH,
borderColor: colors.white,
borderTopLeftRadius: borderRadius,
alignSelf: 'flex-start',
};
break;
case 'UpperRight':
borderStyle = {
borderTopWidth: SQUARE_BORDER_WIDTH,
borderRightWidth: SQUARE_BORDER_WIDTH,
borderColor: colors.white,
borderTopRightRadius: borderRadius,
alignSelf: 'flex-start',
};
break;
case 'LowerRight':
borderStyle = {
borderBottomWidth: SQUARE_BORDER_WIDTH,
borderRightWidth: SQUARE_BORDER_WIDTH,
borderColor: colors.white,
borderBottomRightRadius: borderRadius,
alignSelf: 'flex-end',
};
break;
case 'LowerLeft':
borderStyle = {
borderBottomWidth: SQUARE_BORDER_WIDTH,
borderLeftWidth: SQUARE_BORDER_WIDTH,
borderColor: colors.white,
borderBottomLeftRadius: borderRadius,
alignSelf: 'flex-end',
};
break;
default:
break;
}
return (
<View
style={{
width: SQUARE_WIDTH,
height: SQUARE_HEIGHT,
backgroundColor: colors.transparent,
...borderStyle,
}}
/>
);
};
const AimpointOverlay = () => {
return (
<View style={styles.overlayContainer}>
<View style={styles.rowContainer}>
<SquareComponent corner="UpperLeft" />
<SquareComponent corner="UpperRight" />
</View>
<View style={styles.rowContainer}>
<SquareComponent corner="LowerLeft" />
<SquareComponent corner="LowerRight" />
</View>
</View>
);
};
<View style={styles.container}>
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive
frameProcessor={frameProcessor}
frameProcessorFps={1}
/>
<AimpointOverlay />
</View>
Hope this helps some React Native users that needs this for both platforms, using simple styling.