react-native-vision-camera
Version:
VisionCamera is the fastest and most powerful Camera for react-native.
137 lines (136 loc) • 6.01 kB
JavaScript
import React, { useImperativeHandle, useMemo, useRef } from 'react';
import { callback } from 'react-native-nitro-modules';
import { useExposureUpdater } from '../hooks/internal/useExposureUpdater';
import { useGestureControllers } from '../hooks/internal/useGestureControllers';
import { useTorchModeUpdater } from '../hooks/internal/useTorchModeUpdater';
import { useZoomUpdater } from '../hooks/internal/useZoomUpdater';
import { useCamera } from '../hooks/useCamera';
import { usePreviewOutput } from '../hooks/usePreviewOutput';
import { NativePreviewView } from './NativePreviewView';
function getAnimatableNumberInitialValue(value) {
if (value == null)
return undefined;
else if (typeof value === 'number')
return value;
else
return value.get();
}
function CameraImpl({ implementationMode, resizeMode, onPreviewStarted, onPreviewStopped, outputs = [], zoom, exposure, torchMode, enableNativeZoomGesture = false, enableNativeTapToFocusGesture = false, ref, ...props }) {
// 1. Create `PreviewOutput` for the Camera
const previewOutput = usePreviewOutput();
// 3. Create session/controller and add the preview output
const controller = useCamera({
...props,
outputs: [previewOutput, ...outputs],
getInitialExposureBias: () => getAnimatableNumberInitialValue(exposure),
getInitialZoom: () => getAnimatableNumberInitialValue(zoom),
});
// 4. Create `ref` for `PreviewView`
const previewViewRef = useRef(null);
const setHybridRef = useMemo(() => callback((r) => {
previewViewRef.current = r;
}), []);
// 5. Create a ref that exposes some funcs on the Controller and the PreviewView.
useImperativeHandle(ref, () => ({
startZoomAnimation(zoom, rate) {
if (controller == null)
throw new Error(`Camera is not yet ready!`);
return controller.startZoomAnimation(zoom, rate);
},
cancelZoomAnimation() {
if (controller == null)
throw new Error(`Camera is not yet ready!`);
return controller.cancelZoomAnimation();
},
async focusTo(viewPoint, options = {}) {
if (controller == null)
throw new Error(`Camera is not yet ready!`);
if (previewViewRef.current == null)
throw new Error(`Camera Preview is not yet ready!`);
const meteringPoint = previewViewRef.current.createMeteringPoint(viewPoint.x, viewPoint.y);
await controller.focusTo(meteringPoint, options);
},
async resetFocus() {
if (controller == null)
throw new Error(`Camera is not yet ready!`);
await controller.resetFocus();
},
createMeteringPoint(x, y, size) {
if (previewViewRef.current == null)
throw new Error(`Camera Preview is not yet ready!`);
return previewViewRef.current.createMeteringPoint(x, y, size);
},
takeSnapshot() {
if (previewViewRef.current == null)
throw new Error(`Camera Preview is not yet ready!`);
return previewViewRef.current.takeSnapshot();
},
convertCameraPointToViewPoint(cameraPoint) {
if (previewViewRef.current == null)
throw new Error(`Camera Preview is not yet ready!`);
return previewViewRef.current.convertCameraPointToViewPoint(cameraPoint);
},
convertViewPointToCameraPoint(viewPoint) {
if (previewViewRef.current == null)
throw new Error(`Camera Preview is not yet ready!`);
return previewViewRef.current.convertViewPointToCameraPoint(viewPoint);
},
convertScannedObjectCoordinatesToViewCoordinates(scannedObject) {
if (previewViewRef.current == null)
throw new Error(`Camera Preview is not yet ready!`);
return previewViewRef.current.convertScannedObjectCoordinatesToViewCoordinates(scannedObject);
},
get preview() {
return previewViewRef.current ?? undefined;
},
get controller() {
return controller;
},
}), [controller]);
// 6. Update CameraController props
useZoomUpdater(controller, zoom);
useExposureUpdater(controller, exposure);
useTorchModeUpdater(controller, torchMode);
// 7. Attach any native gesture controllers
if (enableNativeZoomGesture && zoom != null) {
throw new Error(`\`zoom\` must not be set if \`enableNativeZoomGesture\` is enabled!`);
}
const gestureControllers = useGestureControllers({
controller: controller,
enableNativeZoomGesture: enableNativeZoomGesture,
enableNativeTapToFocusGesture: enableNativeTapToFocusGesture,
});
// 8. Render view
return (React.createElement(NativePreviewView, { ...props, implementationMode: implementationMode, resizeMode: resizeMode, gestureControllers: gestureControllers,
// TODO: Memoize?
onPreviewStarted: callback(onPreviewStarted), onPreviewStopped: callback(onPreviewStopped), hybridRef: setHybridRef, previewOutput: previewOutput }));
}
/**
* The `<Camera />` component.
*
* This is a convenience wrapper around {@linkcode useCamera | useCamera(...)}
* that adds a {@linkcode PreviewView}, wraps methods in a {@linkcode CameraRef},
* and supports updating {@linkcode CameraViewProps.zoom | zoom} and
* {@linkcode CameraViewProps.exposure | exposure} via
* Reanimated {@linkcode SharedValue}s.
*
* @example
* ```tsx
* function App() {
* const camera = useRef<CameraRef>(null)
* const device = useCameraDevice('back')
* const photoOutput = usePhotoOutput()
*
* return (
* <Camera
* style={StyleSheet.absoluteFill}
* ref={camera}
* device={device}
* outputs={[photoOutput]}
* isActive={true}
* />
* )
* }
* ```
*/
export const Camera = React.memo(CameraImpl);