react-native-image-stitcher 0.12.0 → 0.14.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/CHANGELOG.md +181 -0
- package/README.md +33 -17
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +33 -5
- package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +73 -1
- package/dist/camera/Camera.d.ts +226 -0
- package/dist/camera/Camera.js +208 -20
- package/dist/camera/CameraView.d.ts +6 -0
- package/dist/camera/CameraView.js +2 -2
- package/dist/camera/CaptureHeader.js +39 -16
- package/dist/camera/CapturePreview.js +13 -1
- package/dist/camera/CaptureThumbnailStrip.d.ts +25 -1
- package/dist/camera/CaptureThumbnailStrip.js +17 -4
- package/dist/camera/PanoramaBandOverlay.d.ts +76 -0
- package/dist/camera/PanoramaBandOverlay.js +90 -33
- package/dist/camera/PanoramaConfirmModal.js +11 -1
- package/dist/camera/selectCaptureDevice.d.ts +93 -0
- package/dist/camera/selectCaptureDevice.js +131 -0
- package/dist/camera/useCapture.d.ts +40 -0
- package/dist/camera/useCapture.js +50 -12
- package/dist/camera/useContentRotation.d.ts +99 -0
- package/dist/camera/useContentRotation.js +124 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.js +6 -5
- package/package.json +1 -1
- package/src/camera/Camera.tsx +546 -32
- package/src/camera/CameraView.tsx +9 -0
- package/src/camera/CaptureHeader.tsx +39 -16
- package/src/camera/CapturePreview.tsx +12 -0
- package/src/camera/CaptureThumbnailStrip.tsx +44 -4
- package/src/camera/PanoramaBandOverlay.tsx +97 -35
- package/src/camera/PanoramaConfirmModal.tsx +10 -0
- package/src/camera/__tests__/bandThumbRotation.test.ts +120 -0
- package/src/camera/__tests__/homeIndicatorEdge.test.ts +116 -0
- package/src/camera/__tests__/selectCaptureDevice.test.ts +177 -0
- package/src/camera/__tests__/useContentRotation.test.ts +89 -0
- package/src/camera/selectCaptureDevice.ts +187 -0
- package/src/camera/useCapture.ts +99 -11
- package/src/camera/useContentRotation.ts +149 -0
- package/src/index.ts +6 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* useContentRotation — returns a CSS transform that rotates control
|
|
4
|
+
* content so labels stay upright relative to device gravity,
|
|
5
|
+
* regardless of whether the OS rotated the framebuffer.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this exists
|
|
8
|
+
*
|
|
9
|
+
* v0.12 anchored `<Camera>`'s bottom controls to the home-indicator
|
|
10
|
+
* edge so they stay in thumb reach on phones in landscape on
|
|
11
|
+
* non-locked iOS hosts. The anchoring works because the OS rotates
|
|
12
|
+
* the framebuffer to match the device, so a JS-bottom view in
|
|
13
|
+
* landscape is the device's actual landscape-bottom edge.
|
|
14
|
+
*
|
|
15
|
+
* On locked-portrait hosts (the most common production
|
|
16
|
+
* configuration) the OS does NOT rotate the framebuffer when the
|
|
17
|
+
* device tilts to landscape. v0.12 still anchored controls to
|
|
18
|
+
* "JS-bottom" — which is now the device's side edge — so the
|
|
19
|
+
* shutter sits where the thumb expects, BUT the labels inside
|
|
20
|
+
* each control (`AR`, `1×`, `0.5×`, the lens chip pills, the gear)
|
|
21
|
+
* render at their JS-portrait baseline, so the user holding the
|
|
22
|
+
* device sideways reads them at 90°.
|
|
23
|
+
*
|
|
24
|
+
* This hook fixes that by applying a `transform: rotate(±90°)` to
|
|
25
|
+
* the control's *content* so it appears upright relative to actual
|
|
26
|
+
* gravity, while the control container itself stays in place.
|
|
27
|
+
*
|
|
28
|
+
* ## How the rotation is computed
|
|
29
|
+
*
|
|
30
|
+
* Two signals:
|
|
31
|
+
* - **Framebuffer rotation** — what rotation has the OS already
|
|
32
|
+
* applied to the JS layout? Read from
|
|
33
|
+
* `useWindowDimensions().width > height` — non-locked +
|
|
34
|
+
* device-landscape is the only case where the OS rotates,
|
|
35
|
+
* and that's exactly when `jsLandscape === true`.
|
|
36
|
+
* - **Device-physical rotation** — what rotation does the device
|
|
37
|
+
* have relative to gravity? Read from `useDeviceOrientation()`
|
|
38
|
+
* (accelerometer-derived).
|
|
39
|
+
*
|
|
40
|
+
* The content rotation we apply is the *difference* between
|
|
41
|
+
* device-physical and framebuffer rotation, so the net rotation
|
|
42
|
+
* (content × framebuffer) equals device-physical → labels are
|
|
43
|
+
* upright in the world.
|
|
44
|
+
*
|
|
45
|
+
* ## Truth table
|
|
46
|
+
*
|
|
47
|
+
* | Host config | Device | jsLandscape | Net rot |
|
|
48
|
+
* |--- |--- |--- |--- |
|
|
49
|
+
* | Locked-portrait | portrait | false | 0° |
|
|
50
|
+
* | Locked-portrait | landscape-left | false | 90° |
|
|
51
|
+
* | Locked-portrait | landscape-right | false | -90° |
|
|
52
|
+
* | Locked-portrait | upside-down | false | 180° |
|
|
53
|
+
* | Non-locked | portrait | false | 0° |
|
|
54
|
+
* | Non-locked | landscape-left | true | 0° |
|
|
55
|
+
* | Non-locked | landscape-right | true | 0° |
|
|
56
|
+
*
|
|
57
|
+
* The 0° case is the common one (locked-portrait + device-portrait
|
|
58
|
+
* OR non-locked + framebuffer-already-rotated); we return an empty
|
|
59
|
+
* style object so React skips the layout work.
|
|
60
|
+
*
|
|
61
|
+
* ## Caveats
|
|
62
|
+
*
|
|
63
|
+
* - Rotation transforms preserve hit-testing in RN 0.84 (verified
|
|
64
|
+
* on iOS + Android), but historical RN versions had bugs in this
|
|
65
|
+
* area. If support for older RN is added, retest pressables.
|
|
66
|
+
* - Containers whose sized layouts depend on un-rotated content
|
|
67
|
+
* (e.g. a 100px-wide pill containing text that's now rotated 90°)
|
|
68
|
+
* may overflow. Fixed-size pills (the lens chip, AR toggle,
|
|
69
|
+
* flash button) are fine; the header title's `flex: 1 + textAlign:
|
|
70
|
+
* center` may need tuning when rotated — see `CaptureHeader`'s
|
|
71
|
+
* own rotation handling.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
import { useWindowDimensions, type ViewStyle } from 'react-native';
|
|
75
|
+
|
|
76
|
+
import {
|
|
77
|
+
useDeviceOrientation,
|
|
78
|
+
type DeviceOrientation,
|
|
79
|
+
} from './useDeviceOrientation';
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
export type ContentRotationDeg = 0 | 90 | -90 | 180;
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Return type for `useContentRotation`. Typed structurally on just
|
|
87
|
+
* the `transform` property so it spreads cleanly into ViewStyle,
|
|
88
|
+
* TextStyle, AND ImageStyle — all three accept identical transform
|
|
89
|
+
* shapes in RN 0.84. Returning the more specific `ViewStyle` would
|
|
90
|
+
* collide with ImageStyle's stricter `overflow` enum at <Image>
|
|
91
|
+
* call sites.
|
|
92
|
+
*/
|
|
93
|
+
export type ContentRotationStyle = {
|
|
94
|
+
transform?: ViewStyle['transform'];
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Pure rotation computation. Exported so tests can exercise the
|
|
100
|
+
* full truth table without booting a React render.
|
|
101
|
+
*/
|
|
102
|
+
export function contentRotationDeg(
|
|
103
|
+
jsLandscape: boolean,
|
|
104
|
+
deviceOrient: DeviceOrientation,
|
|
105
|
+
): ContentRotationDeg {
|
|
106
|
+
// Framebuffer rotation relative to device-physical. Only the
|
|
107
|
+
// non-locked + device-landscape cases see a rotated framebuffer.
|
|
108
|
+
// jsLandscape can briefly be true mid-rotation on devices that
|
|
109
|
+
// aren't a clean landscape orientation; the device-orientation
|
|
110
|
+
// check below catches those and falls through to 0.
|
|
111
|
+
const fbRot: 0 | 90 | -90 =
|
|
112
|
+
!jsLandscape ? 0
|
|
113
|
+
: deviceOrient === 'landscape-left' ? 90
|
|
114
|
+
: deviceOrient === 'landscape-right' ? -90
|
|
115
|
+
: 0;
|
|
116
|
+
|
|
117
|
+
// Device-physical rotation relative to gravity.
|
|
118
|
+
const deviceRot: 0 | 90 | -90 | 180 =
|
|
119
|
+
deviceOrient === 'portrait' ? 0
|
|
120
|
+
: deviceOrient === 'landscape-left' ? 90
|
|
121
|
+
: deviceOrient === 'landscape-right' ? -90
|
|
122
|
+
: 180;
|
|
123
|
+
|
|
124
|
+
// Net rotation we need to apply to content so that
|
|
125
|
+
// content + framebuffer = device-physical (upright in the world).
|
|
126
|
+
// Normalise to [-180, 180] so transform values stay canonical.
|
|
127
|
+
let net = deviceRot - fbRot;
|
|
128
|
+
if (net > 180) net -= 360;
|
|
129
|
+
if (net < -180) net += 360;
|
|
130
|
+
return net as ContentRotationDeg;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Returns the rotation as a ready-to-spread style object. Empty
|
|
136
|
+
* object in the common 0° case so React skips the layout work.
|
|
137
|
+
* Type `ContentRotationStyle` is structurally just `{ transform? }`
|
|
138
|
+
* so call sites can spread it into ViewStyle, TextStyle, or
|
|
139
|
+
* ImageStyle interchangeably.
|
|
140
|
+
*/
|
|
141
|
+
export function useContentRotation(): ContentRotationStyle {
|
|
142
|
+
const orient = useDeviceOrientation();
|
|
143
|
+
const { width, height } = useWindowDimensions();
|
|
144
|
+
const jsLandscape = width > height;
|
|
145
|
+
const deg = contentRotationDeg(jsLandscape, orient);
|
|
146
|
+
return deg === 0
|
|
147
|
+
? {}
|
|
148
|
+
: { transform: [{ rotate: `${deg}deg` }] };
|
|
149
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export type {
|
|
|
30
30
|
CameraCaptureResult,
|
|
31
31
|
CameraErrorCode,
|
|
32
32
|
CaptureSource,
|
|
33
|
+
CaptureSourcesMode,
|
|
33
34
|
CameraLens,
|
|
34
35
|
StitchMode,
|
|
35
36
|
Blender,
|
|
@@ -106,9 +107,12 @@ export type {
|
|
|
106
107
|
} from './camera/CaptureStitchStatsToast';
|
|
107
108
|
export { CaptureThumbnailStrip } from './camera/CaptureThumbnailStrip';
|
|
108
109
|
export type { CaptureThumbnailItem } from './camera/CaptureThumbnailStrip';
|
|
109
|
-
|
|
110
|
+
// v0.13.1 — IncrementalPanGuide (drift marker) and PanoramaGuidance
|
|
111
|
+
// (pan-speed pill) are no longer part of the public API. They remain
|
|
112
|
+
// in the tree as internal-only components but are not exported and not
|
|
113
|
+
// rendered by <Camera> (the `panGuide` / `panoramaGuidance` props were
|
|
114
|
+
// removed). Re-introduce here if a host need resurfaces.
|
|
110
115
|
export { PanoramaBandOverlay } from './camera/PanoramaBandOverlay';
|
|
111
|
-
export { PanoramaGuidance } from './camera/PanoramaGuidance';
|
|
112
116
|
// Settings modal — the modal is in `PanoramaSettingsModal.tsx`, but
|
|
113
117
|
// the type tree + defaults + JS↔native bridge live in dedicated
|
|
114
118
|
// files since v0.4 (F10). The modal is now a thin presentational
|