react-native-expo-cropper 1.2.45 → 1.2.47
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/CustomCamera.js +22 -11
- package/dist/ImageCropper.js +28 -14
- package/dist/ImageCropperStyles.js +11 -0
- package/package.json +1 -1
- package/src/CustomCamera.js +3 -0
- package/src/ImageCropper.js +34 -16
package/dist/CustomCamera.js
CHANGED
|
@@ -25,11 +25,14 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
25
25
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
26
26
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
27
27
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Max width for camera preview on large screens (tablets) so it doesn't stretch full width
|
|
29
|
+
var CAMERA_PREVIEW_MAX_WIDTH = 500;
|
|
30
30
|
function CustomCamera(_ref) {
|
|
31
31
|
var onPhotoCaptured = _ref.onPhotoCaptured,
|
|
32
32
|
onFrameCalculated = _ref.onFrameCalculated;
|
|
33
|
+
var _useWindowDimensions = (0, _reactNative.useWindowDimensions)(),
|
|
34
|
+
windowWidth = _useWindowDimensions.width;
|
|
35
|
+
var cameraPreviewWidth = Math.min(windowWidth, CAMERA_PREVIEW_MAX_WIDTH);
|
|
33
36
|
var _useState = (0, _react.useState)(false),
|
|
34
37
|
_useState2 = _slicedToArray(_useState, 2),
|
|
35
38
|
isReady = _useState2[0],
|
|
@@ -42,22 +45,28 @@ function CustomCamera(_ref) {
|
|
|
42
45
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
43
46
|
hasPermission = _useState6[0],
|
|
44
47
|
setHasPermission = _useState6[1];
|
|
48
|
+
// Force a fresh native CameraView instance per "open" to avoid stale/cached preview.
|
|
49
|
+
var _useState7 = (0, _react.useState)(function () {
|
|
50
|
+
return "".concat(Date.now(), "-").concat(Math.random().toString(16).slice(2));
|
|
51
|
+
}),
|
|
52
|
+
_useState8 = _slicedToArray(_useState7, 1),
|
|
53
|
+
cameraViewMountKey = _useState8[0];
|
|
45
54
|
var cameraRef = (0, _react.useRef)(null);
|
|
46
55
|
var cameraWrapperRef = (0, _react.useRef)(null);
|
|
47
56
|
var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
48
|
-
var
|
|
57
|
+
var _useState9 = (0, _react.useState)({
|
|
49
58
|
width: 0,
|
|
50
59
|
height: 0,
|
|
51
60
|
x: 0,
|
|
52
61
|
y: 0
|
|
53
62
|
}),
|
|
54
|
-
_useState8 = _slicedToArray(_useState7, 2),
|
|
55
|
-
cameraWrapperLayout = _useState8[0],
|
|
56
|
-
setCameraWrapperLayout = _useState8[1];
|
|
57
|
-
var _useState9 = (0, _react.useState)(null),
|
|
58
63
|
_useState0 = _slicedToArray(_useState9, 2),
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
cameraWrapperLayout = _useState0[0],
|
|
65
|
+
setCameraWrapperLayout = _useState0[1];
|
|
66
|
+
var _useState1 = (0, _react.useState)(null),
|
|
67
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
68
|
+
greenFrame = _useState10[0],
|
|
69
|
+
setGreenFrame = _useState10[1];
|
|
61
70
|
(0, _react.useEffect)(function () {
|
|
62
71
|
_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
63
72
|
var _yield$Camera$request, status;
|
|
@@ -249,7 +258,9 @@ function CustomCamera(_ref) {
|
|
|
249
258
|
return /*#__PURE__*/_react["default"].createElement(_reactNative.SafeAreaView, {
|
|
250
259
|
style: styles.outerContainer
|
|
251
260
|
}, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
252
|
-
style: styles.cameraWrapper,
|
|
261
|
+
style: [styles.cameraWrapper, {
|
|
262
|
+
width: cameraPreviewWidth
|
|
263
|
+
}],
|
|
253
264
|
ref: cameraWrapperRef,
|
|
254
265
|
onLayout: function onLayout(e) {
|
|
255
266
|
var layout = e.nativeEvent.layout;
|
|
@@ -262,6 +273,7 @@ function CustomCamera(_ref) {
|
|
|
262
273
|
console.log("Camera wrapper layout updated:", layout);
|
|
263
274
|
}
|
|
264
275
|
}, /*#__PURE__*/_react["default"].createElement(_expoCamera.CameraView, {
|
|
276
|
+
key: cameraViewMountKey,
|
|
265
277
|
style: styles.camera,
|
|
266
278
|
facing: "back",
|
|
267
279
|
ref: cameraRef,
|
|
@@ -310,7 +322,6 @@ var styles = _reactNative.StyleSheet.create({
|
|
|
310
322
|
alignItems: 'center'
|
|
311
323
|
},
|
|
312
324
|
cameraWrapper: {
|
|
313
|
-
width: width,
|
|
314
325
|
aspectRatio: 9 / 16,
|
|
315
326
|
borderRadius: 30,
|
|
316
327
|
overflow: 'hidden',
|
package/dist/ImageCropper.js
CHANGED
|
@@ -36,6 +36,9 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
36
36
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
37
37
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
38
38
|
var PRIMARY_GREEN = '#198754';
|
|
39
|
+
|
|
40
|
+
// Max width for crop preview on large screens (tablets) - must match CustomCamera.js
|
|
41
|
+
var CAMERA_PREVIEW_MAX_WIDTH = 500;
|
|
39
42
|
var ImageCropper = function ImageCropper(_ref) {
|
|
40
43
|
var _cameraFrameData$curr3;
|
|
41
44
|
var onConfirm = _ref.onConfirm,
|
|
@@ -43,6 +46,9 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
43
46
|
initialImage = _ref.initialImage,
|
|
44
47
|
addheight = _ref.addheight,
|
|
45
48
|
rotationLabel = _ref.rotationLabel;
|
|
49
|
+
var _useWindowDimensions = (0, _reactNative.useWindowDimensions)(),
|
|
50
|
+
windowWidth = _useWindowDimensions.width;
|
|
51
|
+
var cameraPreviewWidth = Math.min(windowWidth, CAMERA_PREVIEW_MAX_WIDTH);
|
|
46
52
|
var _useState = (0, _react.useState)(null),
|
|
47
53
|
_useState2 = _slicedToArray(_useState, 2),
|
|
48
54
|
image = _useState2[0],
|
|
@@ -247,6 +253,7 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
247
253
|
var rotationInProgressRef = (0, _react.useRef)(false); // block duplicate taps immediately
|
|
248
254
|
var lastValidPosition = (0, _react.useRef)(null);
|
|
249
255
|
var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
256
|
+
var openCameraTimeoutRef = (0, _react.useRef)(null);
|
|
250
257
|
|
|
251
258
|
// ✅ NEW ARCH: mobile does NOT export the final crop.
|
|
252
259
|
|
|
@@ -255,8 +262,17 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
255
262
|
var enableRotation = true; // rotation resets crop box so it is re-initialized on rotated image.
|
|
256
263
|
|
|
257
264
|
(0, _react.useEffect)(function () {
|
|
265
|
+
// If a previous open was scheduled, cancel it.
|
|
266
|
+
if (openCameraTimeoutRef.current) {
|
|
267
|
+
clearTimeout(openCameraTimeoutRef.current);
|
|
268
|
+
openCameraTimeoutRef.current = null;
|
|
269
|
+
}
|
|
258
270
|
if (openCameraFirst) {
|
|
259
|
-
|
|
271
|
+
// Delay opening the camera so the prior session can fully release.
|
|
272
|
+
openCameraTimeoutRef.current = setTimeout(function () {
|
|
273
|
+
setShowCustomCamera(true);
|
|
274
|
+
openCameraTimeoutRef.current = null;
|
|
275
|
+
}, 800);
|
|
260
276
|
} else if (initialImage) {
|
|
261
277
|
setImage(initialImage);
|
|
262
278
|
sourceImageUri.current = initialImage;
|
|
@@ -270,6 +286,12 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
270
286
|
hasInitializedCropBox.current = false;
|
|
271
287
|
imageSource.current = null;
|
|
272
288
|
}
|
|
289
|
+
return function () {
|
|
290
|
+
if (openCameraTimeoutRef.current) {
|
|
291
|
+
clearTimeout(openCameraTimeoutRef.current);
|
|
292
|
+
openCameraTimeoutRef.current = null;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
273
295
|
}, [openCameraFirst, initialImage]);
|
|
274
296
|
|
|
275
297
|
// ✅ REFACTORISATION : Stocker uniquement les dimensions originales (pas de calcul théorique)
|
|
@@ -1322,17 +1344,9 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
1322
1344
|
return setShowCustomCamera(false);
|
|
1323
1345
|
}
|
|
1324
1346
|
}) : /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, image && /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
1325
|
-
style: {
|
|
1326
|
-
width:
|
|
1327
|
-
|
|
1328
|
-
borderRadius: 30,
|
|
1329
|
-
overflow: 'hidden',
|
|
1330
|
-
alignItems: 'center',
|
|
1331
|
-
justifyContent: 'center',
|
|
1332
|
-
position: 'relative',
|
|
1333
|
-
backgroundColor: 'black',
|
|
1334
|
-
marginBottom: 0 // ✅ Les boutons sont maintenant en position absolue en bas
|
|
1335
|
-
},
|
|
1347
|
+
style: [_ImageCropperStyles["default"].commonWrapper, {
|
|
1348
|
+
width: cameraPreviewWidth
|
|
1349
|
+
}],
|
|
1336
1350
|
ref: commonWrapperRef,
|
|
1337
1351
|
onLayout: onCommonWrapperLayout
|
|
1338
1352
|
}, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
@@ -1413,8 +1427,8 @@ var ImageCropper = function ImageCropper(_ref) {
|
|
|
1413
1427
|
pointerEvents: "none"
|
|
1414
1428
|
}, function () {
|
|
1415
1429
|
// ✅ Use wrapper dimensions for SVG path (wrapper coordinates)
|
|
1416
|
-
var wrapperWidth = commonWrapperLayout.current.width ||
|
|
1417
|
-
var wrapperHeight = commonWrapperLayout.current.height ||
|
|
1430
|
+
var wrapperWidth = commonWrapperLayout.current.width || cameraPreviewWidth;
|
|
1431
|
+
var wrapperHeight = commonWrapperLayout.current.height || cameraPreviewWidth * 16 / 9;
|
|
1418
1432
|
return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_reactNativeSvg.Path, {
|
|
1419
1433
|
d: "M 0 0 H ".concat(wrapperWidth, " V ").concat(wrapperHeight, " H 0 Z ").concat(createPath()),
|
|
1420
1434
|
fill: showResult ? 'white' : 'rgba(0, 0, 0, 0.8)',
|
|
@@ -21,6 +21,17 @@ var styles = _reactNative.StyleSheet.create({
|
|
|
21
21
|
// ✅ Start from top, allow content to flow down
|
|
22
22
|
backgroundColor: DEEP_BLACK
|
|
23
23
|
},
|
|
24
|
+
// Wrapper for crop preview (9/16, same as CustomCamera); width set dynamically for tablet
|
|
25
|
+
commonWrapper: {
|
|
26
|
+
aspectRatio: 9 / 16,
|
|
27
|
+
borderRadius: 30,
|
|
28
|
+
overflow: 'hidden',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
position: 'relative',
|
|
32
|
+
backgroundColor: 'black',
|
|
33
|
+
marginBottom: 0
|
|
34
|
+
},
|
|
24
35
|
buttonContainer: {
|
|
25
36
|
position: 'absolute',
|
|
26
37
|
bottom: 50,
|
package/package.json
CHANGED
package/src/CustomCamera.js
CHANGED
|
@@ -22,6 +22,8 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
|
|
|
22
22
|
const [isReady, setIsReady] = useState(false);
|
|
23
23
|
const [loadingBeforeCapture, setLoadingBeforeCapture] = useState(false);
|
|
24
24
|
const [hasPermission, setHasPermission] = useState(null);
|
|
25
|
+
// Force a fresh native CameraView instance per "open" to avoid stale/cached preview.
|
|
26
|
+
const [cameraViewMountKey] = useState(() => `${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
25
27
|
const cameraRef = useRef(null);
|
|
26
28
|
const cameraWrapperRef = useRef(null);
|
|
27
29
|
const insets = useSafeAreaInsets();
|
|
@@ -213,6 +215,7 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
|
|
|
213
215
|
}}
|
|
214
216
|
>
|
|
215
217
|
<CameraView
|
|
218
|
+
key={cameraViewMountKey}
|
|
216
219
|
style={styles.camera}
|
|
217
220
|
facing="back"
|
|
218
221
|
ref={cameraRef}
|
package/src/ImageCropper.js
CHANGED
|
@@ -141,6 +141,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
|
|
|
141
141
|
const rotationInProgressRef = useRef(false); // block duplicate taps immediately
|
|
142
142
|
const lastValidPosition = useRef(null);
|
|
143
143
|
const insets = useSafeAreaInsets();
|
|
144
|
+
const openCameraTimeoutRef = useRef(null);
|
|
144
145
|
|
|
145
146
|
// ✅ NEW ARCH: mobile does NOT export the final crop.
|
|
146
147
|
|
|
@@ -153,22 +154,39 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
|
|
|
153
154
|
|
|
154
155
|
|
|
155
156
|
useEffect(() => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
// If a previous open was scheduled, cancel it.
|
|
158
|
+
if (openCameraTimeoutRef.current) {
|
|
159
|
+
clearTimeout(openCameraTimeoutRef.current);
|
|
160
|
+
openCameraTimeoutRef.current = null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (openCameraFirst) {
|
|
164
|
+
// Delay opening the camera so the prior session can fully release.
|
|
165
|
+
openCameraTimeoutRef.current = setTimeout(() => {
|
|
166
|
+
setShowCustomCamera(true);
|
|
167
|
+
openCameraTimeoutRef.current = null;
|
|
168
|
+
}, 800);
|
|
169
|
+
} else if (initialImage) {
|
|
170
|
+
setImage(initialImage);
|
|
171
|
+
sourceImageUri.current = initialImage;
|
|
172
|
+
// ✅ CRITICAL: Reset points when loading a new image from gallery
|
|
173
|
+
// This ensures the crop box will be automatically initialized
|
|
174
|
+
setPoints([]);
|
|
175
|
+
rotationAngle.current = 0;
|
|
176
|
+
// Clear camera frame data for gallery images
|
|
177
|
+
cameraFrameData.current = null;
|
|
178
|
+
// ✅ CRITICAL: Reset initialization guard for new image
|
|
179
|
+
hasInitializedCropBox.current = false;
|
|
180
|
+
imageSource.current = null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return () => {
|
|
184
|
+
if (openCameraTimeoutRef.current) {
|
|
185
|
+
clearTimeout(openCameraTimeoutRef.current);
|
|
186
|
+
openCameraTimeoutRef.current = null;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}, [openCameraFirst, initialImage]);
|
|
172
190
|
|
|
173
191
|
|
|
174
192
|
// ✅ REFACTORISATION : Stocker uniquement les dimensions originales (pas de calcul théorique)
|