react-native-expo-cropper 1.2.44 → 1.2.46
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/.babelrc +6 -6
- package/App.js +27 -27
- package/app.json +2 -2
- package/babel.config.js +5 -5
- package/dist/CustomCamera.js +8 -4
- package/dist/ImageCropper.js +37 -13
- package/dist/ImageCropperStyles.js +11 -0
- package/dist/ImageMaskProcessor.js +25 -25
- package/dist/ImageProcessor.js +7 -7
- package/index.js +6 -6
- package/package.json +59 -59
- package/src/CustomCamera.js +355 -352
- package/src/ImageCropper.js +1453 -1447
- package/src/ImageCropperStyles.js +255 -244
- package/src/ImageMaskProcessor.js +143 -143
- package/src/ImageProcessor.js +20 -20
package/src/CustomCamera.js
CHANGED
|
@@ -1,352 +1,355 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
StyleSheet,
|
|
4
|
-
Text,
|
|
5
|
-
View,
|
|
6
|
-
TouchableOpacity,
|
|
7
|
-
Alert,
|
|
8
|
-
SafeAreaView,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from 'react-native';
|
|
13
|
-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
14
|
-
import { Camera, CameraView } from 'expo-camera';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const [
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
...(Platform.OS === '
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
}),
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
},
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
Text,
|
|
5
|
+
View,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
Alert,
|
|
8
|
+
SafeAreaView,
|
|
9
|
+
Image,
|
|
10
|
+
Platform,
|
|
11
|
+
useWindowDimensions,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
14
|
+
import { Camera, CameraView } from 'expo-camera';
|
|
15
|
+
|
|
16
|
+
// Max width for camera preview on large screens (tablets) so it doesn't stretch full width
|
|
17
|
+
const CAMERA_PREVIEW_MAX_WIDTH = 500;
|
|
18
|
+
|
|
19
|
+
export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
|
|
20
|
+
const { width: windowWidth } = useWindowDimensions();
|
|
21
|
+
const cameraPreviewWidth = Math.min(windowWidth, CAMERA_PREVIEW_MAX_WIDTH);
|
|
22
|
+
const [isReady, setIsReady] = useState(false);
|
|
23
|
+
const [loadingBeforeCapture, setLoadingBeforeCapture] = useState(false);
|
|
24
|
+
const [hasPermission, setHasPermission] = useState(null);
|
|
25
|
+
const cameraRef = useRef(null);
|
|
26
|
+
const cameraWrapperRef = useRef(null);
|
|
27
|
+
const insets = useSafeAreaInsets();
|
|
28
|
+
const [cameraWrapperLayout, setCameraWrapperLayout] = useState({ width: 0, height: 0, x: 0, y: 0 });
|
|
29
|
+
const [greenFrame, setGreenFrame] = useState(null);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
(async () => {
|
|
33
|
+
const { status } = await Camera.requestCameraPermissionsAsync();
|
|
34
|
+
setHasPermission(status === 'granted');
|
|
35
|
+
})();
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
// Helper function to wait for multiple render cycles (works on iOS)
|
|
39
|
+
const waitForRender = (cycles = 5) => {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
let count = 0;
|
|
42
|
+
const tick = () => {
|
|
43
|
+
count++;
|
|
44
|
+
if (count >= cycles) {
|
|
45
|
+
resolve();
|
|
46
|
+
} else {
|
|
47
|
+
setImmediate(tick);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
setImmediate(tick);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// ✅ CRITICAL FIX: Calculate green frame coordinates relative to camera preview
|
|
57
|
+
// The green frame should be calculated on the wrapper (as it's visually drawn there)
|
|
58
|
+
// But we store it with wrapper dimensions so ImageCropper can map it correctly
|
|
59
|
+
//
|
|
60
|
+
// NOTE: Camera capture aspect ratio (typically 4:3) may differ from wrapper aspect ratio (9:16)
|
|
61
|
+
// This is handled in ImageCropper by using "cover" mode to match preview content
|
|
62
|
+
const calculateGreenFrameCoordinates = () => {
|
|
63
|
+
const wrapperWidth = cameraWrapperLayout.width;
|
|
64
|
+
const wrapperHeight = cameraWrapperLayout.height;
|
|
65
|
+
|
|
66
|
+
if (wrapperWidth === 0 || wrapperHeight === 0) {
|
|
67
|
+
console.warn("Camera wrapper layout not ready, cannot calculate green frame");
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ✅ Calculate green frame as percentage of WRAPPER
|
|
72
|
+
const frameWidth = wrapperWidth * 0.85; // 85% of wrapper width
|
|
73
|
+
const frameHeight = wrapperHeight * 0.70; // 70% of wrapper height
|
|
74
|
+
const frameX = (wrapperWidth - frameWidth) / 2; // Centered horizontally
|
|
75
|
+
const frameY = (wrapperHeight - frameHeight) / 2; // Centered vertically
|
|
76
|
+
|
|
77
|
+
const frameCoords = {
|
|
78
|
+
x: frameX,
|
|
79
|
+
y: frameY,
|
|
80
|
+
width: frameWidth,
|
|
81
|
+
height: frameHeight,
|
|
82
|
+
wrapperWidth,
|
|
83
|
+
wrapperHeight,
|
|
84
|
+
// ✅ Store percentages for easier mapping later
|
|
85
|
+
percentX: (frameX / wrapperWidth) * 100,
|
|
86
|
+
percentY: (frameY / wrapperHeight) * 100,
|
|
87
|
+
percentWidth: 85, // 85% of wrapper width
|
|
88
|
+
percentHeight: 70 // 70% of wrapper height
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
console.log("✅ Green frame coordinates calculated:", frameCoords);
|
|
92
|
+
return frameCoords;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// 🔁 Keep green frame state in sync with wrapper layout so we can both render it
|
|
96
|
+
// and send the exact same coordinates along with the captured photo.
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const coords = calculateGreenFrameCoordinates();
|
|
99
|
+
if (coords) {
|
|
100
|
+
setGreenFrame(coords);
|
|
101
|
+
}
|
|
102
|
+
}, [cameraWrapperLayout]);
|
|
103
|
+
|
|
104
|
+
const takePicture = async () => {
|
|
105
|
+
if (cameraRef.current) {
|
|
106
|
+
try {
|
|
107
|
+
// Show loading after a delay (using setImmediate for iOS compatibility)
|
|
108
|
+
waitForRender(5).then(() => {
|
|
109
|
+
setLoadingBeforeCapture(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Wait a bit before taking picture (works on iOS)
|
|
113
|
+
await waitForRender(2);
|
|
114
|
+
|
|
115
|
+
// ✅ OPTIMIZED: Capture with maximum quality and native camera ratio
|
|
116
|
+
// Platform-specific optimizations for best quality
|
|
117
|
+
const captureOptions = {
|
|
118
|
+
// Maximum quality (0-1, 1 = best quality, no compression)
|
|
119
|
+
quality: 1,
|
|
120
|
+
|
|
121
|
+
// Disable shutter sound for better UX
|
|
122
|
+
shutterSound: false,
|
|
123
|
+
|
|
124
|
+
// ✅ CRITICAL: skipProcessing preserves original resolution and avoids interpolation
|
|
125
|
+
// This ensures pixel-perfect quality and prevents premature resizing
|
|
126
|
+
skipProcessing: true,
|
|
127
|
+
|
|
128
|
+
// Include EXIF metadata (orientation, camera settings, etc.)
|
|
129
|
+
exif: true,
|
|
130
|
+
|
|
131
|
+
// ✅ Platform-specific optimizations
|
|
132
|
+
...(Platform.OS === 'ios' && {
|
|
133
|
+
// iOS: Use native capture format (typically 4:3 or 16:9 depending on device)
|
|
134
|
+
// No additional processing to preserve quality
|
|
135
|
+
}),
|
|
136
|
+
...(Platform.OS === 'android' && {
|
|
137
|
+
// Android: Ensure maximum resolution capture
|
|
138
|
+
// skipProcessing already handles this, but we can add Android-specific options if needed
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
console.log("📸 Capturing photo with maximum quality settings:", {
|
|
143
|
+
platform: Platform.OS,
|
|
144
|
+
options: captureOptions,
|
|
145
|
+
wrapperSize: { width: cameraWrapperLayout.width, height: cameraWrapperLayout.height }
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const photo = await cameraRef.current.takePictureAsync(captureOptions);
|
|
149
|
+
|
|
150
|
+
// ✅ Validate captured photo dimensions
|
|
151
|
+
if (!photo.width || !photo.height || photo.width === 0 || photo.height === 0) {
|
|
152
|
+
throw new Error("Invalid photo dimensions received from camera");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const capturedAspectRatio = photo.width / photo.height;
|
|
156
|
+
console.log("✅ Photo captured with maximum quality:", {
|
|
157
|
+
uri: photo.uri,
|
|
158
|
+
width: photo.width,
|
|
159
|
+
height: photo.height,
|
|
160
|
+
aspectRatio: capturedAspectRatio.toFixed(3),
|
|
161
|
+
expectedRatio: "~1.33 (4:3) or ~1.78 (16:9)",
|
|
162
|
+
exif: photo.exif ? "present" : "missing",
|
|
163
|
+
fileSize: photo.uri ? "available" : "unknown"
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ✅ CRITICAL FIX: Use the same green frame coordinates that are used for rendering
|
|
167
|
+
// Fallback to recalculation if, for some reason, state is not yet set
|
|
168
|
+
const greenFrameCoords = greenFrame || calculateGreenFrameCoordinates();
|
|
169
|
+
|
|
170
|
+
if (!greenFrameCoords) {
|
|
171
|
+
throw new Error("Green frame coordinates not available");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ✅ Send photo URI and frame data to ImageCropper
|
|
175
|
+
// The photo maintains its native resolution and aspect ratio
|
|
176
|
+
// ImageCropper will handle display and cropping while preserving quality
|
|
177
|
+
onPhotoCaptured(photo.uri, {
|
|
178
|
+
greenFrame: greenFrameCoords,
|
|
179
|
+
capturedImageSize: {
|
|
180
|
+
width: photo.width,
|
|
181
|
+
height: photo.height,
|
|
182
|
+
aspectRatio: capturedAspectRatio
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
setLoadingBeforeCapture(false);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error("❌ Error capturing photo:", error);
|
|
189
|
+
setLoadingBeforeCapture(false);
|
|
190
|
+
Alert.alert(
|
|
191
|
+
"Erreur",
|
|
192
|
+
`Impossible de capturer la photo: ${error.message || "Erreur inconnue"}. Veuillez réessayer.`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<SafeAreaView style={styles.outerContainer}>
|
|
201
|
+
<View
|
|
202
|
+
style={[styles.cameraWrapper, { width: cameraPreviewWidth }]}
|
|
203
|
+
ref={cameraWrapperRef}
|
|
204
|
+
onLayout={(e) => {
|
|
205
|
+
const layout = e.nativeEvent.layout;
|
|
206
|
+
setCameraWrapperLayout({
|
|
207
|
+
width: layout.width,
|
|
208
|
+
height: layout.height,
|
|
209
|
+
x: layout.x,
|
|
210
|
+
y: layout.y
|
|
211
|
+
});
|
|
212
|
+
console.log("Camera wrapper layout updated:", layout);
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
<CameraView
|
|
216
|
+
style={styles.camera}
|
|
217
|
+
facing="back"
|
|
218
|
+
ref={cameraRef}
|
|
219
|
+
onCameraReady={() => {
|
|
220
|
+
setIsReady(true);
|
|
221
|
+
console.log("✅ Camera ready - Maximum quality capture enabled");
|
|
222
|
+
}}
|
|
223
|
+
// ✅ Ensure camera uses native aspect ratio (typically 4:3 for most devices)
|
|
224
|
+
// The wrapper constrains the view, but capture uses full sensor resolution
|
|
225
|
+
// This ensures preview matches what will be captured
|
|
226
|
+
/>
|
|
227
|
+
|
|
228
|
+
{/* Loading overlay */}
|
|
229
|
+
{loadingBeforeCapture && (
|
|
230
|
+
<>
|
|
231
|
+
<View style={styles.loadingOverlay}>
|
|
232
|
+
<Image
|
|
233
|
+
source={require('../src/assets/loadingCamera.gif')}
|
|
234
|
+
style={styles.loadingGif}
|
|
235
|
+
resizeMode="contain"
|
|
236
|
+
/>
|
|
237
|
+
</View>
|
|
238
|
+
<View style={styles.touchBlocker} pointerEvents="auto" />
|
|
239
|
+
</>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
{/* Cadre de scan - rendered using calculated coordinates (x, y, width, height) */}
|
|
243
|
+
{greenFrame && (
|
|
244
|
+
<View
|
|
245
|
+
style={[
|
|
246
|
+
styles.scanFrame,
|
|
247
|
+
{
|
|
248
|
+
left: greenFrame.x,
|
|
249
|
+
top: greenFrame.y,
|
|
250
|
+
width: greenFrame.width,
|
|
251
|
+
height: greenFrame.height,
|
|
252
|
+
},
|
|
253
|
+
]}
|
|
254
|
+
/>
|
|
255
|
+
)}
|
|
256
|
+
</View>
|
|
257
|
+
|
|
258
|
+
<View style={[styles.buttonContainer, { bottom: (insets?.bottom || 0) + 16, marginBottom: 0 }]}>
|
|
259
|
+
<TouchableOpacity
|
|
260
|
+
style={styles.button}
|
|
261
|
+
onPress={takePicture}
|
|
262
|
+
disabled={!isReady || loadingBeforeCapture}
|
|
263
|
+
/>
|
|
264
|
+
</View>
|
|
265
|
+
</SafeAreaView>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const PRIMARY_GREEN = '#198754';
|
|
270
|
+
const DEEP_BLACK = '#0B0B0B';
|
|
271
|
+
const GLOW_WHITE = 'rgba(255, 255, 255, 0.85)';
|
|
272
|
+
|
|
273
|
+
const styles = StyleSheet.create({
|
|
274
|
+
outerContainer: {
|
|
275
|
+
flex: 1,
|
|
276
|
+
backgroundColor: DEEP_BLACK,
|
|
277
|
+
justifyContent: 'center',
|
|
278
|
+
alignItems: 'center',
|
|
279
|
+
},
|
|
280
|
+
cameraWrapper: {
|
|
281
|
+
aspectRatio: 9 / 16,
|
|
282
|
+
borderRadius: 30,
|
|
283
|
+
overflow: 'hidden',
|
|
284
|
+
alignItems: 'center',
|
|
285
|
+
justifyContent: 'center',
|
|
286
|
+
position: 'relative',
|
|
287
|
+
},
|
|
288
|
+
camera: {
|
|
289
|
+
...StyleSheet.absoluteFillObject,
|
|
290
|
+
},
|
|
291
|
+
scanFrame: {
|
|
292
|
+
position: 'absolute',
|
|
293
|
+
borderWidth: 4,
|
|
294
|
+
borderColor: PRIMARY_GREEN,
|
|
295
|
+
borderRadius: 5,
|
|
296
|
+
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
|
297
|
+
},
|
|
298
|
+
loadingOverlay: {
|
|
299
|
+
...StyleSheet.absoluteFillObject,
|
|
300
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
301
|
+
zIndex: 20,
|
|
302
|
+
justifyContent: 'center',
|
|
303
|
+
alignItems: 'center',
|
|
304
|
+
},
|
|
305
|
+
loadingGif: {
|
|
306
|
+
width: 100,
|
|
307
|
+
height: 100,
|
|
308
|
+
},
|
|
309
|
+
touchBlocker: {
|
|
310
|
+
...StyleSheet.absoluteFillObject,
|
|
311
|
+
zIndex: 21,
|
|
312
|
+
backgroundColor: 'transparent',
|
|
313
|
+
},
|
|
314
|
+
cancelIcon: {
|
|
315
|
+
position: 'absolute',
|
|
316
|
+
top: 20,
|
|
317
|
+
left: 20,
|
|
318
|
+
backgroundColor: PRIMARY_GREEN,
|
|
319
|
+
borderRadius: 5,
|
|
320
|
+
padding: 8,
|
|
321
|
+
},
|
|
322
|
+
buttonContainer: {
|
|
323
|
+
position: 'absolute',
|
|
324
|
+
bottom: 0,
|
|
325
|
+
marginBottom: 20,
|
|
326
|
+
flexDirection: 'row',
|
|
327
|
+
justifyContent: 'center',
|
|
328
|
+
},
|
|
329
|
+
button: {
|
|
330
|
+
width: 80,
|
|
331
|
+
height: 80,
|
|
332
|
+
borderRadius: 50,
|
|
333
|
+
backgroundColor: GLOW_WHITE,
|
|
334
|
+
borderWidth: 5,
|
|
335
|
+
borderColor: PRIMARY_GREEN,
|
|
336
|
+
alignItems: 'center',
|
|
337
|
+
justifyContent: 'center',
|
|
338
|
+
},
|
|
339
|
+
text: {
|
|
340
|
+
fontSize: 18,
|
|
341
|
+
color: GLOW_WHITE,
|
|
342
|
+
},
|
|
343
|
+
container: {
|
|
344
|
+
flex: 1,
|
|
345
|
+
backgroundColor: DEEP_BLACK,
|
|
346
|
+
justifyContent: 'center',
|
|
347
|
+
alignItems: 'center',
|
|
348
|
+
padding: 20,
|
|
349
|
+
},
|
|
350
|
+
iconText: {
|
|
351
|
+
fontSize: 18,
|
|
352
|
+
color: GLOW_WHITE,
|
|
353
|
+
fontWeight: '600',
|
|
354
|
+
},
|
|
355
|
+
});
|