react-native-rectangle-doc-scanner 3.27.0 → 3.31.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.
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
2
- import { Animated, Easing, StyleSheet, View } from 'react-native';
1
+ import React, { useMemo } from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
3
  import type { Rectangle } from '../types';
4
4
 
5
5
  let SvgModule: typeof import('react-native-svg') | null = null;
@@ -11,19 +11,35 @@ try {
11
11
  SvgModule = null;
12
12
  }
13
13
 
14
- const SCAN_DURATION_MS = 2200;
15
14
  const GRID_STEPS = [1 / 3, 2 / 3];
16
15
 
17
- type PolygonMetrics = {
18
- minX: number;
19
- maxX: number;
20
- minY: number;
21
- maxY: number;
22
- width: number;
23
- height: number;
24
- };
16
+ const createPointsString = (polygon: Rectangle): string =>
17
+ [
18
+ `${polygon.topLeft.x},${polygon.topLeft.y}`,
19
+ `${polygon.topRight.x},${polygon.topRight.y}`,
20
+ `${polygon.bottomRight.x},${polygon.bottomRight.y}`,
21
+ `${polygon.bottomLeft.x},${polygon.bottomLeft.y}`,
22
+ ].join(' ');
23
+
24
+ const interpolatePoint = (a: { x: number; y: number }, b: { x: number; y: number }, t: number) => ({
25
+ x: a.x + (b.x - a.x) * t,
26
+ y: a.y + (b.y - a.y) * t,
27
+ });
28
+
29
+ const createGridLines = (polygon: Rectangle) =>
30
+ GRID_STEPS.flatMap((step) => {
31
+ const horizontalStart = interpolatePoint(polygon.topLeft, polygon.bottomLeft, step);
32
+ const horizontalEnd = interpolatePoint(polygon.topRight, polygon.bottomRight, step);
33
+ const verticalStart = interpolatePoint(polygon.topLeft, polygon.topRight, step);
34
+ const verticalEnd = interpolatePoint(polygon.bottomLeft, polygon.bottomRight, step);
35
+
36
+ return [
37
+ { x1: horizontalStart.x, y1: horizontalStart.y, x2: horizontalEnd.x, y2: horizontalEnd.y },
38
+ { x1: verticalStart.x, y1: verticalStart.y, x2: verticalEnd.x, y2: verticalEnd.y },
39
+ ];
40
+ });
25
41
 
26
- const calculateMetrics = (polygon: Rectangle): PolygonMetrics => {
42
+ const getBounds = (polygon: Rectangle) => {
27
43
  const minX = Math.min(
28
44
  polygon.topLeft.x,
29
45
  polygon.bottomLeft.x,
@@ -51,40 +67,12 @@ const calculateMetrics = (polygon: Rectangle): PolygonMetrics => {
51
67
 
52
68
  return {
53
69
  minX,
54
- maxX,
55
70
  minY,
56
- maxY,
57
71
  width: maxX - minX,
58
72
  height: maxY - minY,
59
73
  };
60
74
  };
61
75
 
62
- const createPointsString = (polygon: Rectangle): string =>
63
- [
64
- `${polygon.topLeft.x},${polygon.topLeft.y}`,
65
- `${polygon.topRight.x},${polygon.topRight.y}`,
66
- `${polygon.bottomRight.x},${polygon.bottomRight.y}`,
67
- `${polygon.bottomLeft.x},${polygon.bottomLeft.y}`,
68
- ].join(' ');
69
-
70
- const interpolatePoint = (a: { x: number; y: number }, b: { x: number; y: number }, t: number) => ({
71
- x: a.x + (b.x - a.x) * t,
72
- y: a.y + (b.y - a.y) * t,
73
- });
74
-
75
- const createGridLines = (polygon: Rectangle) =>
76
- GRID_STEPS.flatMap((step) => {
77
- const horizontalStart = interpolatePoint(polygon.topLeft, polygon.bottomLeft, step);
78
- const horizontalEnd = interpolatePoint(polygon.topRight, polygon.bottomRight, step);
79
- const verticalStart = interpolatePoint(polygon.topLeft, polygon.topRight, step);
80
- const verticalEnd = interpolatePoint(polygon.bottomLeft, polygon.bottomRight, step);
81
-
82
- return [
83
- { x1: horizontalStart.x, y1: horizontalStart.y, x2: horizontalEnd.x, y2: horizontalEnd.y },
84
- { x1: verticalStart.x, y1: verticalStart.y, x2: verticalEnd.x, y2: verticalEnd.y },
85
- ];
86
- });
87
-
88
76
  export interface ScannerOverlayProps {
89
77
  active: boolean;
90
78
  color?: string;
@@ -93,89 +81,21 @@ export interface ScannerOverlayProps {
93
81
  }
94
82
 
95
83
  export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
96
- active,
84
+ active: _active, // kept for compatibility; no animation currently
97
85
  color = '#0b7ef4',
98
86
  lineWidth = StyleSheet.hairlineWidth,
99
87
  polygon,
100
88
  }) => {
101
- const scanProgress = useRef(new Animated.Value(0)).current;
102
- const fallbackBase = useRef(new Animated.Value(0)).current;
103
- const [scanY, setScanY] = useState<number | null>(null);
104
-
105
- const metrics = useMemo(() => (polygon ? calculateMetrics(polygon) : null), [polygon]);
106
-
107
- const scanBarHeight = useMemo(() => {
108
- if (!metrics) return 0;
109
- return Math.max(metrics.height * 0.2, 16);
110
- }, [metrics]);
111
-
112
- const travelDistance = useMemo(() => {
113
- if (!metrics) {
114
- return 0;
115
- }
116
- return Math.max(metrics.height - scanBarHeight, 0);
117
- }, [metrics, scanBarHeight]);
118
-
119
- useEffect(() => {
120
- scanProgress.stopAnimation();
121
- scanProgress.setValue(0);
122
- setScanY(null);
123
-
124
- if (!active || !metrics || travelDistance <= 0) {
125
- return undefined;
126
- }
127
-
128
- const loop = Animated.loop(
129
- Animated.sequence([
130
- Animated.timing(scanProgress, {
131
- toValue: 1,
132
- duration: SCAN_DURATION_MS,
133
- easing: Easing.inOut(Easing.quad),
134
- useNativeDriver: false,
135
- }),
136
- Animated.timing(scanProgress, {
137
- toValue: 0,
138
- duration: SCAN_DURATION_MS,
139
- easing: Easing.inOut(Easing.quad),
140
- useNativeDriver: false,
141
- }),
142
- ]),
143
- );
89
+ const points = useMemo(() => (polygon ? createPointsString(polygon) : null), [polygon]);
90
+ const gridLines = useMemo(() => (polygon ? createGridLines(polygon) : []), [polygon]);
91
+ const bounds = useMemo(() => (polygon ? getBounds(polygon) : null), [polygon]);
144
92
 
145
- loop.start();
146
- return () => {
147
- loop.stop();
148
- scanProgress.stopAnimation();
149
- };
150
- }, [active, metrics, scanProgress, travelDistance]);
151
-
152
- useEffect(() => {
153
- if (!metrics || travelDistance <= 0) {
154
- setScanY(null);
155
- return undefined;
156
- }
157
-
158
- const listenerId = scanProgress.addListener(({ value }) => {
159
- const nextValue = metrics.minY + travelDistance * value;
160
- if (Number.isFinite(nextValue)) {
161
- setScanY(nextValue);
162
- }
163
- });
164
-
165
- return () => {
166
- scanProgress.removeListener(listenerId);
167
- };
168
- }, [metrics, scanProgress, travelDistance]);
169
-
170
- if (!polygon || !metrics || metrics.width <= 0 || metrics.height <= 0) {
93
+ if (!polygon || !points || !bounds) {
171
94
  return null;
172
95
  }
173
96
 
174
97
  if (SvgModule) {
175
- const { default: Svg, Polygon, Line, Defs, LinearGradient, Stop, Rect } = SvgModule;
176
- const gridLines = createGridLines(polygon);
177
- const points = createPointsString(polygon);
178
- const scanRectY = scanY ?? metrics.minY;
98
+ const { default: Svg, Polygon, Line } = SvgModule;
179
99
 
180
100
  return (
181
101
  <View pointerEvents="none" style={StyleSheet.absoluteFill}>
@@ -194,60 +114,26 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
194
114
  />
195
115
  ))}
196
116
  <Polygon points={points} stroke={color} strokeWidth={lineWidth} fill="none" />
197
- <Defs>
198
- <LinearGradient id="scanGradient" x1="0" y1="0" x2="0" y2="1">
199
- <Stop offset="0%" stopColor="rgba(255,255,255,0)" />
200
- <Stop offset="50%" stopColor={color} stopOpacity={0.8} />
201
- <Stop offset="100%" stopColor="rgba(255,255,255,0)" />
202
- </LinearGradient>
203
- </Defs>
204
- {active && travelDistance > 0 && Number.isFinite(scanRectY) && (
205
- <Rect
206
- x={metrics.minX}
207
- width={metrics.width}
208
- height={scanBarHeight}
209
- fill="url(#scanGradient)"
210
- y={scanRectY}
211
- />
212
- )}
213
117
  </Svg>
214
118
  </View>
215
119
  );
216
120
  }
217
121
 
218
- const relativeTranslate =
219
- metrics && travelDistance > 0
220
- ? Animated.multiply(scanProgress, travelDistance)
221
- : fallbackBase;
222
-
223
122
  return (
224
123
  <View pointerEvents="none" style={StyleSheet.absoluteFill}>
225
124
  <View
226
125
  style={[
227
126
  styles.fallbackBox,
228
127
  {
229
- left: metrics.minX,
230
- top: metrics.minY,
231
- width: metrics.width,
232
- height: metrics.height,
128
+ left: bounds.minX,
129
+ top: bounds.minY,
130
+ width: bounds.width,
131
+ height: bounds.height,
233
132
  borderColor: color,
234
133
  borderWidth: lineWidth,
235
134
  },
236
135
  ]}
237
- >
238
- {active && travelDistance > 0 && (
239
- <Animated.View
240
- style={[
241
- styles.fallbackScanBar,
242
- {
243
- backgroundColor: color,
244
- height: scanBarHeight,
245
- transform: [{ translateY: relativeTranslate }],
246
- },
247
- ]}
248
- />
249
- )}
250
- </View>
136
+ />
251
137
  </View>
252
138
  );
253
139
  };
@@ -256,10 +142,5 @@ const styles = StyleSheet.create({
256
142
  fallbackBox: {
257
143
  position: 'absolute',
258
144
  backgroundColor: 'rgba(11, 126, 244, 0.1)',
259
- overflow: 'hidden',
260
- },
261
- fallbackScanBar: {
262
- width: '100%',
263
- opacity: 0.4,
264
145
  },
265
146
  });