react-native-expo-cropper 1.0.26 → 1.0.28

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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "react-native-expo-cropper",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "Recadrage polygonal d'images.",
5
- "main": "index.js",
5
+ "main": "src/index.js",
6
6
  "author": "PCS AGRI",
7
7
  "license": "MIT",
8
8
  "keywords": [
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@expo/vector-icons": "^15.0.2",
24
- "expo": "^54.0.0",
24
+ "expo": "54.0.0",
25
25
  "expo-camera": "~17.0.8",
26
26
  "expo-image-manipulator": "~14.0.7",
27
27
  "expo-image-picker": "~17.0.8",
@@ -44,5 +44,18 @@
44
44
  "scripts": {
45
45
  "test": "echo \"Error: no test specified\" && exit 1",
46
46
  "build": "babel src --out-dir dist"
47
+ },
48
+ "devDependencies": {
49
+ "@babel/cli": "^7.28.3",
50
+ "@babel/core": "^7.28.5",
51
+ "@babel/preset-env": "^7.28.5",
52
+ "@babel/preset-react": "^7.28.5"
53
+ },
54
+ "files": [
55
+ "src",
56
+ "README.MD"
57
+ ],
58
+ "publishConfig": {
59
+ "access": "public"
47
60
  }
48
61
  }
@@ -8,7 +8,6 @@ import {
8
8
  SafeAreaView,
9
9
  Dimensions,
10
10
  Image,
11
- Platform,
12
11
  } from 'react-native';
13
12
  import { Camera, CameraView } from 'expo-camera';
14
13
  const { width } = Dimensions.get('window');
@@ -38,9 +37,7 @@ useEffect(() => {
38
37
  setLoadingBeforeCapture(true);
39
38
  }, 1000);
40
39
 
41
- if (Platform.OS === 'android') {
42
- await new Promise(resolve => setTimeout(resolve, 100));
43
- }
40
+ await new Promise(resolve => setTimeout(resolve, 100));
44
41
 
45
42
  const photo = await cameraRef.current.takePictureAsync({
46
43
  quality: 1,
@@ -1,6 +1,6 @@
1
1
  import styles from './ImageCropperStyles';
2
2
  import React, { useState, useRef, useEffect } from 'react';
3
- import { Modal,View, Image, Dimensions, TouchableOpacity, Animated, Platform , Text } from 'react-native';
3
+ import { Modal,View, Image, Dimensions, TouchableOpacity, Animated, Text } from 'react-native';
4
4
  import Svg, { Path, Circle } from 'react-native-svg';
5
5
  import { captureRef } from 'react-native-view-shot';
6
6
  import CustomCamera from './CustomCamera';
@@ -21,11 +21,6 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) =>
21
21
  const [isLoading, setIsLoading] = useState(false);
22
22
  const [showFullScreenCapture, setShowFullScreenCapture] = useState(false);
23
23
  const lastValidPosition = useRef(null);
24
- const [captureRequested, setCaptureRequested] = useState(false);
25
- const [overlayReady, setOverlayReady] = useState(false);
26
-
27
- // Local screen size used for imageMeasure calculation
28
- const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
29
24
 
30
25
 
31
26
 
@@ -40,44 +35,26 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) =>
40
35
  }, [openCameraFirst, initialImage]);
41
36
 
42
37
 
43
- // Measure based strictly on actual layout, not screen ratio
44
- // to avoid mismatches that truncate the selectable bottom area
45
- // (onImageLayout handles measurement updates)
46
-
47
- // Perform capture after UI commits (avoids iOS timer/RAF awaits)
48
- // iOS capture logic using useEffect with overlay readiness
49
38
  useEffect(() => {
50
- if (Platform.OS !== 'ios') return;
51
- if (!captureRequested || !showResult || !overlayReady || !viewRef.current) return;
39
+ if (!image) return;
52
40
 
53
- let cancelled = false;
54
- const doCapture = async () => {
55
- try {
56
- const capturedUri = await captureRef(viewRef.current, {
57
- format: 'png',
58
- quality: 1,
59
- });
60
- const enhancedUri = await enhanceImage(capturedUri ,addheight);
61
- const name = `IMAGE XTK${Date.now()}.png`;
62
- if (!cancelled && onConfirm) {
63
- onConfirm(enhancedUri, name);
64
- }
65
- } catch (error) {
66
- console.error("Erreur lors de la capture :", error);
67
- alert("Erreur lors de la capture !");
68
- } finally {
69
- if (!cancelled) {
70
- setShowResult(false);
71
- setIsLoading(false);
72
- setShowFullScreenCapture(false);
73
- setCaptureRequested(false);
74
- setOverlayReady(false);
75
- }
76
- }
77
- };
78
- doCapture();
79
- return () => { cancelled = true; };
80
- }, [captureRequested, showResult, overlayReady, addheight, onConfirm]);
41
+ Image.getSize(image, (imgWidth, imgHeight) => {
42
+ const screenRatio = SCREEN_WIDTH / SCREEN_HEIGHT;
43
+ const imageRatio = imgWidth / imgHeight;
44
+
45
+ if (imageRatio > screenRatio) {
46
+ imageMeasure.current = {
47
+ width: SCREEN_WIDTH,
48
+ height: SCREEN_WIDTH / imageRatio,
49
+ };
50
+ } else {
51
+ imageMeasure.current = {
52
+ width: SCREEN_HEIGHT * imageRatio,
53
+ height: SCREEN_HEIGHT,
54
+ };
55
+ }
56
+ });
57
+ }, [image]);
81
58
 
82
59
 
83
60
  const initializeCropBox = () => {
@@ -194,8 +171,8 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) =>
194
171
  />
195
172
  ) : (
196
173
  <>
197
- {!showResult && !image && (
198
- <View pointerEvents="box-none" style={styles.centerButtonsContainer}>
174
+ {!showResult && (
175
+ <View style={image ? styles.buttonContainer : styles.centerButtonsContainer}>
199
176
 
200
177
  {image && (
201
178
  <TouchableOpacity style={styles.button} onPress={handleReset}>
@@ -206,39 +183,34 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) =>
206
183
  <TouchableOpacity
207
184
  style={styles.button}
208
185
  onPress={async () => {
209
- setIsLoading(true);
210
- setShowResult(true);
211
- setOverlayReady(false);
212
- setCaptureRequested(true);
213
-
214
- // Android capture logic using requestAnimationFrame
215
- if (Platform.OS === 'android') {
216
- try {
217
- await new Promise((resolve) => requestAnimationFrame(resolve));
218
- const capturedUri = await captureRef(viewRef.current, {
219
- format: 'png',
220
- quality: 1,
221
- });
222
- const enhancedUri = await enhanceImage(capturedUri, addheight);
223
- const name = `IMAGE XTK${Date.now()}.png`;
224
-
225
- if (onConfirm) {
226
- onConfirm(enhancedUri, name);
227
- }
186
+ // setShowFullScreenCapture(true);
187
+ setIsLoading(true);
188
+ setShowResult(true);
189
+ try {
190
+ await new Promise((resolve) => requestAnimationFrame(resolve));
191
+ const capturedUri = await captureRef(viewRef, {
192
+ format: 'png',
193
+ quality: 1,
194
+ });
195
+
228
196
 
229
- setShowResult(false);
230
- setIsLoading(false);
231
- setShowFullScreenCapture(false);
232
- setCaptureRequested(false);
233
- setOverlayReady(false);
234
- } catch (error) {
235
- console.error("Erreur lors de la capture :", error);
236
- alert("Erreur lors de la capture !");
237
- setIsLoading(false);
238
- }
197
+ const enhancedUri = await enhanceImage(capturedUri ,addheight);
198
+ const name = `IMAGE XTK${Date.now()}.png`;
199
+
200
+
201
+ if (onConfirm) {
202
+ onConfirm(enhancedUri, name);
239
203
  }
240
- }}
241
- >
204
+ } catch (error) {
205
+ console.error("Erreur lors de la capture :", error);
206
+ alert("Erreur lors de la capture !");
207
+ } finally {
208
+ setShowResult(false);
209
+ setIsLoading(false);
210
+ setShowFullScreenCapture(false);
211
+ }
212
+ }}
213
+ >
242
214
  <Text style={styles.buttonText}>Confirm</Text>
243
215
  </TouchableOpacity>
244
216
  )}
@@ -258,14 +230,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) =>
258
230
  onResponderRelease={handleRelease}
259
231
  >
260
232
  <Image source={{ uri: image }} style={styles.image} onLayout={onImageLayout} />
261
- <Svg
262
- pointerEvents="none"
263
- key={showResult ? 'mask' : 'edit'}
264
- style={styles.overlay}
265
- onLayout={() => {
266
- if (showResult) setOverlayReady(true);
267
- }}
268
- >
233
+ <Svg style={styles.overlay}>
269
234
  <Path
270
235
  d={`M 0 0 H ${imageMeasure.current.width} V ${imageMeasure.current.height} H 0 Z ${createPath()}`}
271
236
  fill={showResult ? 'white' : 'rgba(255, 255, 255, 0.8)'}
@@ -278,50 +243,6 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage ,addheight}) =>
278
243
  <Circle key={index} cx={point.x} cy={point.y} r={10} fill="white" />
279
244
  ))}
280
245
  </Svg>
281
- </View>
282
- )}
283
- {!showResult && image && (
284
- <View style={styles.buttonContainer}>
285
- <TouchableOpacity style={styles.button} onPress={handleReset}>
286
- <Text style={styles.buttonText}>Reset</Text>
287
- </TouchableOpacity>
288
- <TouchableOpacity
289
- style={styles.button}
290
- onPress={async () => {
291
- setIsLoading(true);
292
- setShowResult(true);
293
- setOverlayReady(false);
294
- setCaptureRequested(true);
295
-
296
- if (Platform.OS === 'android') {
297
- try {
298
- await new Promise((resolve) => requestAnimationFrame(resolve));
299
- const capturedUri = await captureRef(viewRef.current, {
300
- format: 'png',
301
- quality: 1,
302
- });
303
- const enhancedUri = await enhanceImage(capturedUri, addheight);
304
- const name = `IMAGE XTK${Date.now()}.png`;
305
-
306
- if (onConfirm) {
307
- onConfirm(enhancedUri, name);
308
- }
309
-
310
- setShowResult(false);
311
- setIsLoading(false);
312
- setShowFullScreenCapture(false);
313
- setCaptureRequested(false);
314
- setOverlayReady(false);
315
- } catch (error) {
316
- console.error('Erreur lors de la capture :', error);
317
- alert('Erreur lors de la capture !');
318
- setIsLoading(false);
319
- }
320
- }
321
- }}
322
- >
323
- <Text style={styles.buttonText}>Confirm</Text>
324
- </TouchableOpacity>
325
246
  </View>
326
247
  )}
327
248
  </>
@@ -15,22 +15,27 @@ const styles = StyleSheet.create({
15
15
  backgroundColor: DEEP_BLACK,
16
16
  },
17
17
  buttonContainer: {
18
+ position: 'absolute',
19
+ bottom: 50,
20
+ left: 0,
21
+ right: 0,
18
22
  flexDirection: 'row',
19
- justifyContent: 'space-between',
23
+ flexWrap: 'wrap',
24
+ justifyContent: 'center',
20
25
  alignItems: 'center',
21
- paddingHorizontal: 16,
22
- paddingVertical: 12,
23
- width: '100%',
24
- backgroundColor: DEEP_BLACK,
25
- gap: 10,
26
+ paddingHorizontal: 10,
27
+ zIndex: 10,
26
28
  },
27
29
  button: {
28
30
  flex: 1,
29
- paddingVertical: 12,
31
+ width: "100%",
32
+ padding: 10,
30
33
  alignItems: "center",
31
34
  justifyContent: "center",
32
35
  backgroundColor: "#549433",
33
- borderRadius: 8,
36
+ borderRadius: 5,
37
+ marginBottom: 20,
38
+ marginRight:5,
34
39
  },
35
40
  buttonText: {
36
41
  color: 'white',
@@ -45,13 +50,13 @@ const styles = StyleSheet.create({
45
50
  textAlign: 'center',
46
51
  },
47
52
  imageContainer: {
48
- width: IMAGE_WIDTH,
49
- flex: 1,
50
- justifyContent: 'center',
51
- alignItems: 'center',
52
- overflow: 'hidden',
53
- backgroundColor: 'black',
54
- },
53
+ width: IMAGE_WIDTH,
54
+ height: "80%",
55
+ justifyContent: 'center',
56
+ alignItems: 'center',
57
+ overflow: 'hidden',
58
+ backgroundColor: 'black',
59
+ },
55
60
 
56
61
  image: {
57
62
  width: '100%',
package/.babelrc DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "presets": ["@babel/preset-env"]
3
- }
4
-
package/App.js DELETED
@@ -1,27 +0,0 @@
1
- import React from 'react';
2
- import { SafeAreaView, StatusBar, StyleSheet } from 'react-native';
3
- import ImageCropper from './src/ImageCropper';
4
-
5
- const App = () => {
6
- const handleCrop = (uri) => {
7
- console.log('Cropped Image URI:', uri);
8
- };
9
-
10
- return (
11
- <>
12
- <StatusBar backgroundColor="black" barStyle="light-content" />
13
- <SafeAreaView style={styles.container}>
14
- <ImageCropper onCrop={handleCrop} />
15
- </SafeAreaView>
16
- </>
17
- );
18
- };
19
-
20
- const styles = StyleSheet.create({
21
- container: {
22
- flex: 1,
23
- backgroundColor: 'black',
24
- },
25
- });
26
-
27
- export default App;
package/index.js DELETED
@@ -1,6 +0,0 @@
1
- import { registerRootComponent } from 'expo';
2
- import App from './App';
3
-
4
- registerRootComponent(App);
5
-
6
-