react-native-expo-cropper 1.2.37 → 1.2.39

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.
@@ -17,6 +17,8 @@ var styles = _reactNative.StyleSheet.create({
17
17
  container: {
18
18
  flex: 1,
19
19
  alignItems: 'center',
20
+ justifyContent: 'flex-start',
21
+ // ✅ Start from top, allow content to flow down
20
22
  backgroundColor: DEEP_BLACK
21
23
  },
22
24
  buttonContainer: {
@@ -32,6 +34,20 @@ var styles = _reactNative.StyleSheet.create({
32
34
  zIndex: 10,
33
35
  gap: 10
34
36
  },
37
+ buttonContainerBelow: {
38
+ // ✅ Buttons positioned BELOW image, not overlapping, above Android navigation bar
39
+ position: 'absolute',
40
+ bottom: 0,
41
+ left: 0,
42
+ right: 0,
43
+ flexDirection: 'row',
44
+ justifyContent: 'center',
45
+ alignItems: 'center',
46
+ paddingHorizontal: 10,
47
+ paddingTop: 16,
48
+ gap: 8,
49
+ backgroundColor: DEEP_BLACK // Fond noir pour séparer visuellement
50
+ },
35
51
  iconButton: {
36
52
  backgroundColor: PRIMARY_GREEN,
37
53
  width: 60,
@@ -41,16 +57,26 @@ var styles = _reactNative.StyleSheet.create({
41
57
  justifyContent: 'center',
42
58
  marginRight: 5
43
59
  },
60
+ rotationButton: {
61
+ // Cercle, même couleur que Reset/Confirm (#549433), même hauteur que les autres boutons
62
+ backgroundColor: '#549433',
63
+ width: 56,
64
+ height: 48,
65
+ // Même hauteur approximative que les boutons rectangulaires (padding 10 + texte)
66
+ borderRadius: 28,
67
+ alignItems: 'center',
68
+ justifyContent: 'center'
69
+ },
44
70
  button: {
45
71
  flex: 1,
46
- width: "100%",
47
- padding: 10,
72
+ minHeight: 48,
73
+ // Même hauteur que le bouton de rotation
74
+ paddingVertical: 12,
75
+ paddingHorizontal: 10,
48
76
  alignItems: "center",
49
77
  justifyContent: "center",
50
78
  backgroundColor: "#549433",
51
- borderRadius: 5,
52
- marginBottom: 20,
53
- marginRight: 5
79
+ borderRadius: 5
54
80
  },
55
81
  buttonText: {
56
82
  color: 'white',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-expo-cropper",
3
- "version": "1.2.37",
3
+ "version": "1.2.39",
4
4
  "description": "Recadrage polygonal d'images.",
5
5
  "main": "index.js",
6
6
  "author": "PCS AGRI",
@@ -8,6 +8,7 @@ import {
8
8
  SafeAreaView,
9
9
  Dimensions,
10
10
  Image,
11
+ Platform,
11
12
  } from 'react-native';
12
13
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
13
14
  import { Camera, CameraView } from 'expo-camera';
@@ -51,6 +52,9 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
51
52
  // ✅ CRITICAL FIX: Calculate green frame coordinates relative to camera preview
52
53
  // The green frame should be calculated on the wrapper (as it's visually drawn there)
53
54
  // But we store it with wrapper dimensions so ImageCropper can map it correctly
55
+ //
56
+ // NOTE: Camera capture aspect ratio (typically 4:3) may differ from wrapper aspect ratio (9:16)
57
+ // This is handled in ImageCropper by using "cover" mode to match preview content
54
58
  const calculateGreenFrameCoordinates = () => {
55
59
  const wrapperWidth = cameraWrapperLayout.width;
56
60
  const wrapperHeight = cameraWrapperLayout.height;
@@ -60,11 +64,10 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
60
64
  return null;
61
65
  }
62
66
 
63
- // ✅ Green frame dimensions: 80% width, 80% height (centered)
64
- // The green frame should cover 80% of the image area, centered
65
- const frameWidth = wrapperWidth * 0.80; // 80% width
66
- const frameHeight = wrapperHeight * 0.80; // 80% height
67
- const frameX = (wrapperWidth - frameWidth) / 2; // Centered horizontally
67
+ // ✅ Calculate green frame as percentage of WRAPPER
68
+ const frameWidth = wrapperWidth * 0.85; // 85% of wrapper width
69
+ const frameHeight = wrapperHeight * 0.70; // 70% of wrapper height
70
+ const frameX = (wrapperWidth - frameWidth) / 2; // Centered horizontally
68
71
  const frameY = (wrapperHeight - frameHeight) / 2; // Centered vertically
69
72
 
70
73
  const frameCoords = {
@@ -77,8 +80,8 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
77
80
  // ✅ Store percentages for easier mapping later
78
81
  percentX: (frameX / wrapperWidth) * 100,
79
82
  percentY: (frameY / wrapperHeight) * 100,
80
- percentWidth: 80,
81
- percentHeight: 80
83
+ percentWidth: 85, // 85% of wrapper width
84
+ percentHeight: 70 // 70% of wrapper height
82
85
  };
83
86
 
84
87
  console.log("✅ Green frame coordinates calculated:", frameCoords);
@@ -105,45 +108,85 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
105
108
  // Wait a bit before taking picture (works on iOS)
106
109
  await waitForRender(2);
107
110
 
108
- // ✅ REFACTORISATION : Capture avec qualité maximale et préservation des métadonnées
109
- const photo = await cameraRef.current.takePictureAsync({
110
- quality: 1, // Qualité maximale (0-1, 1 = meilleure qualité)
111
+ // ✅ OPTIMIZED: Capture with maximum quality and native camera ratio
112
+ // Platform-specific optimizations for best quality
113
+ const captureOptions = {
114
+ // Maximum quality (0-1, 1 = best quality, no compression)
115
+ quality: 1,
116
+
117
+ // Disable shutter sound for better UX
111
118
  shutterSound: false,
112
- // skipProcessing: true = Désactiver le traitement automatique pour préserver la qualité pixel-perfect
113
- // IMPORTANT : Cela préserve la résolution originale et évite toute interpolation
119
+
120
+ // CRITICAL: skipProcessing preserves original resolution and avoids interpolation
121
+ // This ensures pixel-perfect quality and prevents premature resizing
114
122
  skipProcessing: true,
115
- // exif: true = Inclure les métadonnées EXIF (orientation, etc.)
116
- // L'orientation sera gérée dans ImageCropper si nécessaire via la fonction de rotation
123
+
124
+ // Include EXIF metadata (orientation, camera settings, etc.)
117
125
  exif: true,
126
+
127
+ // ✅ Platform-specific optimizations
128
+ ...(Platform.OS === 'ios' && {
129
+ // iOS: Use native capture format (typically 4:3 or 16:9 depending on device)
130
+ // No additional processing to preserve quality
131
+ }),
132
+ ...(Platform.OS === 'android' && {
133
+ // Android: Ensure maximum resolution capture
134
+ // skipProcessing already handles this, but we can add Android-specific options if needed
135
+ }),
136
+ };
137
+
138
+ console.log("📸 Capturing photo with maximum quality settings:", {
139
+ platform: Platform.OS,
140
+ options: captureOptions,
141
+ wrapperSize: { width: cameraWrapperLayout.width, height: cameraWrapperLayout.height }
118
142
  });
119
143
 
120
- console.log("Photo captured with maximum quality:", {
144
+ const photo = await cameraRef.current.takePictureAsync(captureOptions);
145
+
146
+ // ✅ Validate captured photo dimensions
147
+ if (!photo.width || !photo.height || photo.width === 0 || photo.height === 0) {
148
+ throw new Error("Invalid photo dimensions received from camera");
149
+ }
150
+
151
+ const capturedAspectRatio = photo.width / photo.height;
152
+ console.log("✅ Photo captured with maximum quality:", {
121
153
  uri: photo.uri,
122
154
  width: photo.width,
123
155
  height: photo.height,
124
- exif: photo.exif ? "present" : "missing"
156
+ aspectRatio: capturedAspectRatio.toFixed(3),
157
+ expectedRatio: "~1.33 (4:3) or ~1.78 (16:9)",
158
+ exif: photo.exif ? "present" : "missing",
159
+ fileSize: photo.uri ? "available" : "unknown"
125
160
  });
126
161
 
127
162
  // ✅ CRITICAL FIX: Use the same green frame coordinates that are used for rendering
128
163
  // Fallback to recalculation if, for some reason, state is not yet set
129
164
  const greenFrameCoords = greenFrame || calculateGreenFrameCoordinates();
130
165
 
131
- // REFACTORISATION : Utiliser directement l'URI de la photo
132
- // L'orientation sera gérée dans ImageCropper si l'utilisateur utilise la fonction de rotation
133
- // skipProcessing: true préserve la qualité mais peut laisser l'orientation EXIF non appliquée
134
- // C'est acceptable car l'utilisateur peut corriger via la rotation dans ImageCropper
135
- const fixedUri = photo.uri;
166
+ if (!greenFrameCoords) {
167
+ throw new Error("Green frame coordinates not available");
168
+ }
136
169
 
137
- // Envoyer l'URI et les coordonnées du green frame à ImageCropper
138
- onPhotoCaptured(fixedUri, {
170
+ // Send photo URI and frame data to ImageCropper
171
+ // The photo maintains its native resolution and aspect ratio
172
+ // ImageCropper will handle display and cropping while preserving quality
173
+ onPhotoCaptured(photo.uri, {
139
174
  greenFrame: greenFrameCoords,
140
- capturedImageSize: { width: photo.width, height: photo.height }
175
+ capturedImageSize: {
176
+ width: photo.width,
177
+ height: photo.height,
178
+ aspectRatio: capturedAspectRatio
179
+ }
141
180
  });
181
+
142
182
  setLoadingBeforeCapture(false);
143
183
  } catch (error) {
144
- console.error("Error capturing photo:", error);
184
+ console.error("Error capturing photo:", error);
145
185
  setLoadingBeforeCapture(false);
146
- Alert.alert("Erreur", "Impossible de capturer la photo. Veuillez réessayer.");
186
+ Alert.alert(
187
+ "Erreur",
188
+ `Impossible de capturer la photo: ${error.message || "Erreur inconnue"}. Veuillez réessayer.`
189
+ );
147
190
  }
148
191
  }
149
192
  };
@@ -169,7 +212,13 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
169
212
  style={styles.camera}
170
213
  facing="back"
171
214
  ref={cameraRef}
172
- onCameraReady={() => setIsReady(true)}
215
+ onCameraReady={() => {
216
+ setIsReady(true);
217
+ console.log("✅ Camera ready - Maximum quality capture enabled");
218
+ }}
219
+ // ✅ Ensure camera uses native aspect ratio (typically 4:3 for most devices)
220
+ // The wrapper constrains the view, but capture uses full sensor resolution
221
+ // This ensures preview matches what will be captured
173
222
  />
174
223
 
175
224
  {/* Loading overlay */}