react-native-rectangle-doc-scanner 3.26.0 → 3.27.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/dist/utils/overlay.js +33 -18
- package/package.json +1 -1
- package/src/utils/overlay.tsx +40 -24
package/dist/utils/overlay.js
CHANGED
|
@@ -58,7 +58,6 @@ const calculateMetrics = (polygon) => {
|
|
|
58
58
|
maxY,
|
|
59
59
|
width: maxX - minX,
|
|
60
60
|
height: maxY - minY,
|
|
61
|
-
centerX: minX + (maxX - minX) / 2,
|
|
62
61
|
};
|
|
63
62
|
};
|
|
64
63
|
const createPointsString = (polygon) => [
|
|
@@ -84,25 +83,24 @@ const createGridLines = (polygon) => GRID_STEPS.flatMap((step) => {
|
|
|
84
83
|
const ScannerOverlay = ({ active, color = '#0b7ef4', lineWidth = react_native_1.StyleSheet.hairlineWidth, polygon, }) => {
|
|
85
84
|
const scanProgress = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
86
85
|
const fallbackBase = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
86
|
+
const [scanY, setScanY] = (0, react_1.useState)(null);
|
|
87
87
|
const metrics = (0, react_1.useMemo)(() => (polygon ? calculateMetrics(polygon) : null), [polygon]);
|
|
88
88
|
const scanBarHeight = (0, react_1.useMemo)(() => {
|
|
89
89
|
if (!metrics)
|
|
90
90
|
return 0;
|
|
91
91
|
return Math.max(metrics.height * 0.2, 16);
|
|
92
92
|
}, [metrics]);
|
|
93
|
-
const
|
|
94
|
-
if (!metrics
|
|
95
|
-
return
|
|
93
|
+
const travelDistance = (0, react_1.useMemo)(() => {
|
|
94
|
+
if (!metrics) {
|
|
95
|
+
return 0;
|
|
96
96
|
}
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
outputRange: [metrics.minY, Math.max(metrics.minY, metrics.maxY - scanBarHeight)],
|
|
100
|
-
});
|
|
101
|
-
}, [metrics, scanBarHeight, scanProgress]);
|
|
97
|
+
return Math.max(metrics.height - scanBarHeight, 0);
|
|
98
|
+
}, [metrics, scanBarHeight]);
|
|
102
99
|
(0, react_1.useEffect)(() => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
scanProgress.stopAnimation();
|
|
101
|
+
scanProgress.setValue(0);
|
|
102
|
+
setScanY(null);
|
|
103
|
+
if (!active || !metrics || travelDistance <= 0) {
|
|
106
104
|
return undefined;
|
|
107
105
|
}
|
|
108
106
|
const loop = react_native_1.Animated.loop(react_native_1.Animated.sequence([
|
|
@@ -122,16 +120,32 @@ const ScannerOverlay = ({ active, color = '#0b7ef4', lineWidth = react_native_1.
|
|
|
122
120
|
loop.start();
|
|
123
121
|
return () => {
|
|
124
122
|
loop.stop();
|
|
123
|
+
scanProgress.stopAnimation();
|
|
124
|
+
};
|
|
125
|
+
}, [active, metrics, scanProgress, travelDistance]);
|
|
126
|
+
(0, react_1.useEffect)(() => {
|
|
127
|
+
if (!metrics || travelDistance <= 0) {
|
|
128
|
+
setScanY(null);
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
const listenerId = scanProgress.addListener(({ value }) => {
|
|
132
|
+
const nextValue = metrics.minY + travelDistance * value;
|
|
133
|
+
if (Number.isFinite(nextValue)) {
|
|
134
|
+
setScanY(nextValue);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return () => {
|
|
138
|
+
scanProgress.removeListener(listenerId);
|
|
125
139
|
};
|
|
126
|
-
}, [
|
|
140
|
+
}, [metrics, scanProgress, travelDistance]);
|
|
127
141
|
if (!polygon || !metrics || metrics.width <= 0 || metrics.height <= 0) {
|
|
128
142
|
return null;
|
|
129
143
|
}
|
|
130
144
|
if (SvgModule) {
|
|
131
145
|
const { default: Svg, Polygon, Line, Defs, LinearGradient, Stop, Rect } = SvgModule;
|
|
132
|
-
const AnimatedRect = react_native_1.Animated.createAnimatedComponent(Rect);
|
|
133
146
|
const gridLines = createGridLines(polygon);
|
|
134
147
|
const points = createPointsString(polygon);
|
|
148
|
+
const scanRectY = scanY ?? metrics.minY;
|
|
135
149
|
return (react_1.default.createElement(react_native_1.View, { pointerEvents: "none", style: react_native_1.StyleSheet.absoluteFill },
|
|
136
150
|
react_1.default.createElement(Svg, { style: react_native_1.StyleSheet.absoluteFill },
|
|
137
151
|
react_1.default.createElement(Polygon, { points: points, fill: color, opacity: 0.15 }),
|
|
@@ -142,10 +156,11 @@ const ScannerOverlay = ({ active, color = '#0b7ef4', lineWidth = react_native_1.
|
|
|
142
156
|
react_1.default.createElement(Stop, { offset: "0%", stopColor: "rgba(255,255,255,0)" }),
|
|
143
157
|
react_1.default.createElement(Stop, { offset: "50%", stopColor: color, stopOpacity: 0.8 }),
|
|
144
158
|
react_1.default.createElement(Stop, { offset: "100%", stopColor: "rgba(255,255,255,0)" }))),
|
|
145
|
-
active &&
|
|
159
|
+
active && travelDistance > 0 && Number.isFinite(scanRectY) && (react_1.default.createElement(Rect, { x: metrics.minX, width: metrics.width, height: scanBarHeight, fill: "url(#scanGradient)", y: scanRectY })))));
|
|
146
160
|
}
|
|
147
|
-
|
|
148
|
-
|
|
161
|
+
const relativeTranslate = metrics && travelDistance > 0
|
|
162
|
+
? react_native_1.Animated.multiply(scanProgress, travelDistance)
|
|
163
|
+
: fallbackBase;
|
|
149
164
|
return (react_1.default.createElement(react_native_1.View, { pointerEvents: "none", style: react_native_1.StyleSheet.absoluteFill },
|
|
150
165
|
react_1.default.createElement(react_native_1.View, { style: [
|
|
151
166
|
styles.fallbackBox,
|
|
@@ -157,7 +172,7 @@ const ScannerOverlay = ({ active, color = '#0b7ef4', lineWidth = react_native_1.
|
|
|
157
172
|
borderColor: color,
|
|
158
173
|
borderWidth: lineWidth,
|
|
159
174
|
},
|
|
160
|
-
] }, active && (react_1.default.createElement(react_native_1.Animated.View, { style: [
|
|
175
|
+
] }, active && travelDistance > 0 && (react_1.default.createElement(react_native_1.Animated.View, { style: [
|
|
161
176
|
styles.fallbackScanBar,
|
|
162
177
|
{
|
|
163
178
|
backgroundColor: color,
|
package/package.json
CHANGED
package/src/utils/overlay.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useRef } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { Animated, Easing, StyleSheet, View } from 'react-native';
|
|
3
3
|
import type { Rectangle } from '../types';
|
|
4
4
|
|
|
@@ -21,7 +21,6 @@ type PolygonMetrics = {
|
|
|
21
21
|
maxY: number;
|
|
22
22
|
width: number;
|
|
23
23
|
height: number;
|
|
24
|
-
centerX: number;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
const calculateMetrics = (polygon: Rectangle): PolygonMetrics => {
|
|
@@ -57,7 +56,6 @@ const calculateMetrics = (polygon: Rectangle): PolygonMetrics => {
|
|
|
57
56
|
maxY,
|
|
58
57
|
width: maxX - minX,
|
|
59
58
|
height: maxY - minY,
|
|
60
|
-
centerX: minX + (maxX - minX) / 2,
|
|
61
59
|
};
|
|
62
60
|
};
|
|
63
61
|
|
|
@@ -102,6 +100,7 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
102
100
|
}) => {
|
|
103
101
|
const scanProgress = useRef(new Animated.Value(0)).current;
|
|
104
102
|
const fallbackBase = useRef(new Animated.Value(0)).current;
|
|
103
|
+
const [scanY, setScanY] = useState<number | null>(null);
|
|
105
104
|
|
|
106
105
|
const metrics = useMemo(() => (polygon ? calculateMetrics(polygon) : null), [polygon]);
|
|
107
106
|
|
|
@@ -110,21 +109,19 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
110
109
|
return Math.max(metrics.height * 0.2, 16);
|
|
111
110
|
}, [metrics]);
|
|
112
111
|
|
|
113
|
-
const
|
|
114
|
-
if (!metrics
|
|
115
|
-
return
|
|
112
|
+
const travelDistance = useMemo(() => {
|
|
113
|
+
if (!metrics) {
|
|
114
|
+
return 0;
|
|
116
115
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
inputRange: [0, 1],
|
|
120
|
-
outputRange: [metrics.minY, Math.max(metrics.minY, metrics.maxY - scanBarHeight)],
|
|
121
|
-
});
|
|
122
|
-
}, [metrics, scanBarHeight, scanProgress]);
|
|
116
|
+
return Math.max(metrics.height - scanBarHeight, 0);
|
|
117
|
+
}, [metrics, scanBarHeight]);
|
|
123
118
|
|
|
124
119
|
useEffect(() => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
scanProgress.stopAnimation();
|
|
121
|
+
scanProgress.setValue(0);
|
|
122
|
+
setScanY(null);
|
|
123
|
+
|
|
124
|
+
if (!active || !metrics || travelDistance <= 0) {
|
|
128
125
|
return undefined;
|
|
129
126
|
}
|
|
130
127
|
|
|
@@ -148,8 +145,27 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
148
145
|
loop.start();
|
|
149
146
|
return () => {
|
|
150
147
|
loop.stop();
|
|
148
|
+
scanProgress.stopAnimation();
|
|
151
149
|
};
|
|
152
|
-
}, [active, metrics, scanProgress]);
|
|
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]);
|
|
153
169
|
|
|
154
170
|
if (!polygon || !metrics || metrics.width <= 0 || metrics.height <= 0) {
|
|
155
171
|
return null;
|
|
@@ -157,10 +173,9 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
157
173
|
|
|
158
174
|
if (SvgModule) {
|
|
159
175
|
const { default: Svg, Polygon, Line, Defs, LinearGradient, Stop, Rect } = SvgModule;
|
|
160
|
-
const AnimatedRect = Animated.createAnimatedComponent(Rect);
|
|
161
|
-
|
|
162
176
|
const gridLines = createGridLines(polygon);
|
|
163
177
|
const points = createPointsString(polygon);
|
|
178
|
+
const scanRectY = scanY ?? metrics.minY;
|
|
164
179
|
|
|
165
180
|
return (
|
|
166
181
|
<View pointerEvents="none" style={StyleSheet.absoluteFill}>
|
|
@@ -186,13 +201,13 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
186
201
|
<Stop offset="100%" stopColor="rgba(255,255,255,0)" />
|
|
187
202
|
</LinearGradient>
|
|
188
203
|
</Defs>
|
|
189
|
-
{active &&
|
|
190
|
-
<
|
|
204
|
+
{active && travelDistance > 0 && Number.isFinite(scanRectY) && (
|
|
205
|
+
<Rect
|
|
191
206
|
x={metrics.minX}
|
|
192
207
|
width={metrics.width}
|
|
193
208
|
height={scanBarHeight}
|
|
194
209
|
fill="url(#scanGradient)"
|
|
195
|
-
y={
|
|
210
|
+
y={scanRectY}
|
|
196
211
|
/>
|
|
197
212
|
)}
|
|
198
213
|
</Svg>
|
|
@@ -200,9 +215,10 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
200
215
|
);
|
|
201
216
|
}
|
|
202
217
|
|
|
203
|
-
// Fallback rendering without react-native-svg
|
|
204
218
|
const relativeTranslate =
|
|
205
|
-
|
|
219
|
+
metrics && travelDistance > 0
|
|
220
|
+
? Animated.multiply(scanProgress, travelDistance)
|
|
221
|
+
: fallbackBase;
|
|
206
222
|
|
|
207
223
|
return (
|
|
208
224
|
<View pointerEvents="none" style={StyleSheet.absoluteFill}>
|
|
@@ -219,7 +235,7 @@ export const ScannerOverlay: React.FC<ScannerOverlayProps> = ({
|
|
|
219
235
|
},
|
|
220
236
|
]}
|
|
221
237
|
>
|
|
222
|
-
{active && (
|
|
238
|
+
{active && travelDistance > 0 && (
|
|
223
239
|
<Animated.View
|
|
224
240
|
style={[
|
|
225
241
|
styles.fallbackScanBar,
|