react-native-rectangle-doc-scanner 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,25 +5,56 @@ VisionCamera + Fast-OpenCV powered document scanner template built for React Nat
5
5
  ## Features
6
6
  - Real-time quad detection using `react-native-fast-opencv`
7
7
  - Frame processor worklet executed on the UI thread via `react-native-vision-camera`
8
- - Resize plugin to keep frame processing fast on lower-end devices
9
- - Skia overlay for visualizing detected document contours
10
- - Stability tracker for auto-capture once the document is steady
8
+ - Resize plugin keeps frame processing fast on lower-end devices
9
+ - Skia overlay visualises detected document contours
10
+ - Stability tracker enables auto-capture once the document is steady
11
+
12
+ ## Requirements
13
+ Install the module alongside these peer dependencies (your host app should already include them or install them now):
14
+
15
+ - `react-native-vision-camera` (v3+) with frame processors enabled
16
+ - `vision-camera-resize-plugin`
17
+ - `react-native-fast-opencv`
18
+ - `react-native-reanimated` + `react-native-worklets-core`
19
+ - `@shopify/react-native-skia`
20
+ - `react`, `react-native`
11
21
 
12
22
  ## Installation
13
- Install the template as a package and make sure the peer dependencies already exist in your app:
14
23
 
15
24
  ```sh
16
- yarn add react-native-rectangle-doc-scanner
25
+ yarn add react-native-rectangle-doc-scanner \
26
+ react-native-vision-camera \
27
+ vision-camera-resize-plugin \
28
+ react-native-fast-opencv \
29
+ react-native-reanimated \
30
+ react-native-worklets-core \
31
+ @shopify/react-native-skia
17
32
  ```
18
33
 
34
+ Follow each dependency’s native installation guide:
35
+
36
+ - Run `npx pod-install` after adding iOS dependencies.
37
+ - For `react-native-reanimated`, add the Babel plugin, enable the JSI runtime, and ensure Reanimated is the first import in `index.js`.
38
+ - Configure `react-native-fast-opencv` according to its README (adds native OpenCV binaries on both platforms).
39
+ - For `react-native-vision-camera`, enable frame processors by adding the new architecture build and the proxy registration they describe. You must also request camera permissions at runtime.
40
+ - Register the resize plugin once in native code (for example inside your `VisionCameraProxy` setup):
41
+
42
+ ```ts
43
+ import { VisionCameraProxy } from 'react-native-vision-camera';
44
+ import { ResizePlugin } from 'vision-camera-resize-plugin';
45
+
46
+ VisionCameraProxy.installFrameProcessorPlugin('resize', ResizePlugin);
47
+ ```
48
+
19
49
  ## Usage
50
+
20
51
  ```tsx
21
52
  import React from 'react';
22
- import { View } from 'react-native';
53
+ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
23
54
  import { DocScanner } from 'react-native-rectangle-doc-scanner';
24
55
 
25
56
  export const ScanScreen = () => (
26
- <View style={{ flex: 1 }}>
57
+ <View style={styles.container}>
27
58
  <DocScanner
28
59
  onCapture={({ path, quad }) => {
29
60
  console.log('Document captured at', path, quad);
@@ -31,11 +62,51 @@ export const ScanScreen = () => (
31
62
  overlayColor="#ff8800"
32
63
  autoCapture
33
64
  minStableFrames={8}
34
- />
65
+ cameraProps={{ enableZoomGesture: true }}
66
+ >
67
+ <View style={styles.overlayControls}>
68
+ <TouchableOpacity style={styles.button}>
69
+ <Text style={styles.label}>Manual Capture</Text>
70
+ </TouchableOpacity>
71
+ </View>
72
+ </DocScanner>
35
73
  </View>
36
74
  );
75
+
76
+ const styles = StyleSheet.create({
77
+ container: { flex: 1 },
78
+ overlayControls: {
79
+ position: 'absolute',
80
+ bottom: 32,
81
+ alignSelf: 'center',
82
+ },
83
+ button: {
84
+ paddingHorizontal: 24,
85
+ paddingVertical: 12,
86
+ borderRadius: 999,
87
+ backgroundColor: 'rgba(0,0,0,0.7)',
88
+ },
89
+ label: { color: '#fff', fontWeight: '600' },
90
+ });
37
91
  ```
38
92
 
93
+ Passing `children` lets you render any UI on top of the camera preview, so you can freely add buttons, tutorials, or progress indicators without modifying the package.
94
+
95
+ ### Props
96
+
97
+ - `onCapture({ path, quad })` — called when a photo is taken; `quad` contains the detected corner coordinates (or `null` if none were found).
98
+ - `overlayColor` (default `#e7a649`) — stroke colour for the contour overlay.
99
+ - `autoCapture` (default `true`) — when `true`, captures automatically after stability is reached; set to `false` to show the built-in shutter button.
100
+ - `minStableFrames` (default `8`) — number of consecutive stable frames required before auto capture triggers.
101
+ - `cameraProps` — forwarded to the underlying `Camera` (except for `frameProcessor`), enabling features such as zoom gestures, HDR, torch control, device selection, etc.
102
+ - `children` — rendered over the camera/overlay for fully custom controls.
103
+
104
+ ### Notes on camera behaviour
105
+
106
+ - If you disable `autoCapture`, the built-in shutter button appears; you can still provide your own UI as `children` to replace or augment it.
107
+ - The internal frame processor handles document detection; do not override `frameProcessor` in `cameraProps`.
108
+ - Adjust `minStableFrames` or tweak lighting conditions if auto capture is too sensitive or too slow.
109
+
39
110
  ## Build
40
111
  ```sh
41
112
  yarn build
@@ -1,5 +1,7 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
+ import { Camera } from 'react-native-vision-camera';
2
3
  import type { Point } from './types';
4
+ type CameraOverrides = Omit<React.ComponentProps<typeof Camera>, 'style' | 'ref' | 'frameProcessor'>;
3
5
  interface Props {
4
6
  onCapture?: (photo: {
5
7
  path: string;
@@ -8,6 +10,8 @@ interface Props {
8
10
  overlayColor?: string;
9
11
  autoCapture?: boolean;
10
12
  minStableFrames?: number;
13
+ cameraProps?: CameraOverrides;
14
+ children?: ReactNode;
11
15
  }
12
16
  export declare const DocScanner: React.FC<Props>;
13
17
  export {};
@@ -42,7 +42,7 @@ const react_native_reanimated_1 = require("react-native-reanimated");
42
42
  const react_native_fast_opencv_1 = require("react-native-fast-opencv");
43
43
  const overlay_1 = require("./utils/overlay");
44
44
  const stability_1 = require("./utils/stability");
45
- const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, minStableFrames = 8, }) => {
45
+ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, minStableFrames = 8, cameraProps, children, }) => {
46
46
  const device = (0, react_native_vision_camera_1.useCameraDevice)('back');
47
47
  const { hasPermission, requestPermission } = (0, react_native_vision_camera_1.useCameraPermission)();
48
48
  const { resize } = (0, vision_camera_resize_plugin_1.useResizePlugin)();
@@ -115,11 +115,13 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
115
115
  };
116
116
  capture();
117
117
  }, [autoCapture, minStableFrames, onCapture, quad, stable]);
118
- if (!device || !hasPermission) {
118
+ const { device: overrideDevice, ...cameraRestProps } = cameraProps ?? {};
119
+ const resolvedDevice = overrideDevice ?? device;
120
+ if (!resolvedDevice || !hasPermission) {
119
121
  return null;
120
122
  }
121
123
  return (react_1.default.createElement(react_native_1.View, { style: { flex: 1 } },
122
- react_1.default.createElement(react_native_vision_camera_1.Camera, { ref: handleCameraRef, style: react_native_1.StyleSheet.absoluteFillObject, device: device, isActive: true, photo: true, frameProcessor: frameProcessor, frameProcessorFps: 15 }),
124
+ react_1.default.createElement(react_native_vision_camera_1.Camera, { ref: handleCameraRef, style: react_native_1.StyleSheet.absoluteFillObject, device: resolvedDevice, isActive: true, photo: true, frameProcessor: frameProcessor, frameProcessorFps: 15, ...cameraRestProps }),
123
125
  react_1.default.createElement(overlay_1.Overlay, { quad: quad, color: overlayColor }),
124
126
  !autoCapture && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.button, onPress: async () => {
125
127
  if (!camera.current) {
@@ -127,7 +129,8 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
127
129
  }
128
130
  const photo = await camera.current.takePhoto({ qualityPrioritization: 'quality' });
129
131
  onCapture?.({ path: photo.path, quad });
130
- } }))));
132
+ } })),
133
+ children));
131
134
  };
132
135
  exports.DocScanner = DocScanner;
133
136
  const styles = react_native_1.StyleSheet.create({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { View, TouchableOpacity, StyleSheet } from 'react-native';
3
3
  import { Camera, useCameraDevice, useCameraPermission, useFrameProcessor } from 'react-native-vision-camera';
4
4
  import { useResizePlugin } from 'vision-camera-resize-plugin';
@@ -22,11 +22,15 @@ type CameraRef = {
22
22
  }>;
23
23
  };
24
24
 
25
+ type CameraOverrides = Omit<React.ComponentProps<typeof Camera>, 'style' | 'ref' | 'frameProcessor'>;
26
+
25
27
  interface Props {
26
28
  onCapture?: (photo: { path: string; quad: Point[] | null }) => void;
27
29
  overlayColor?: string;
28
30
  autoCapture?: boolean;
29
31
  minStableFrames?: number;
32
+ cameraProps?: CameraOverrides;
33
+ children?: ReactNode;
30
34
  }
31
35
 
32
36
  export const DocScanner: React.FC<Props> = ({
@@ -34,6 +38,8 @@ export const DocScanner: React.FC<Props> = ({
34
38
  overlayColor = '#e7a649',
35
39
  autoCapture = true,
36
40
  minStableFrames = 8,
41
+ cameraProps,
42
+ children,
37
43
  }) => {
38
44
  const device = useCameraDevice('back');
39
45
  const { hasPermission, requestPermission } = useCameraPermission();
@@ -126,7 +132,10 @@ export const DocScanner: React.FC<Props> = ({
126
132
  capture();
127
133
  }, [autoCapture, minStableFrames, onCapture, quad, stable]);
128
134
 
129
- if (!device || !hasPermission) {
135
+ const { device: overrideDevice, ...cameraRestProps } = cameraProps ?? {};
136
+ const resolvedDevice = overrideDevice ?? device;
137
+
138
+ if (!resolvedDevice || !hasPermission) {
130
139
  return null;
131
140
  }
132
141
 
@@ -135,11 +144,12 @@ export const DocScanner: React.FC<Props> = ({
135
144
  <Camera
136
145
  ref={handleCameraRef}
137
146
  style={StyleSheet.absoluteFillObject}
138
- device={device}
147
+ device={resolvedDevice}
139
148
  isActive
140
149
  photo
141
150
  frameProcessor={frameProcessor}
142
151
  frameProcessorFps={15}
152
+ {...cameraRestProps}
143
153
  />
144
154
  <Overlay quad={quad} color={overlayColor} />
145
155
  {!autoCapture && (
@@ -155,6 +165,7 @@ export const DocScanner: React.FC<Props> = ({
155
165
  }}
156
166
  />
157
167
  )}
168
+ {children}
158
169
  </View>
159
170
  );
160
171
  };