Skip to content

Commit 4453edc

Browse files
committed
feat: Add onThumbnailReady callback for photo capture
Add support for generating thumbnails during photo capture with an asynchronous callback that fires before the full photo is saved. This enables instant preview functionality and better UX in camera applications. **Changes:** - Add `thumbnailSize` and `onThumbnailReady` options to `TakePhotoOptions` - Add `ThumbnailFile` type for thumbnail metadata - iOS: Extract embedded thumbnail from AVCapturePhoto using ImageIO for maximum performance - Android: Implement memory-efficient downsampling with hardware-accelerated decoding - Add event bridge `onThumbnailReady` for both platforms - Update documentation with usage examples and platform implementation details - Add thumbnail display in example app **Platform implementations:** - iOS: Uses embedded thumbnail from camera capture if available - Android: Uses BitmapFactory.Options.inSampleSize for efficient downsampling without loading full image into memory Both implementations are asynchronous and non-blocking. Tested on iPhone 14, iOS 18.6.2 Android implementation compiles successfully (needs device testing)
1 parent 8e919c8 commit 4453edc

22 files changed

+451
-77
lines changed

bun.lockb

8.38 KB
Binary file not shown.

docs/docs/guides/TAKING_PHOTOS.mdx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,40 @@ const photo = await camera.current.takePhoto({
8181

8282
Note that flash is only available on camera devices where [`hasFlash`](/docs/api/interfaces/CameraDevice#hasflash) is `true`; for example most front cameras don't have a flash.
8383

84+
### Thumbnail Generation
85+
86+
For a better user experience, you can generate a low-resolution thumbnail that loads while the full photo is being processed and saved. This is especially useful for displaying a preview in your UI without waiting for the full-resolution image.
87+
88+
To generate a thumbnail, provide the [`thumbnailSize`](/docs/api/interfaces/TakePhotoOptions#thumbnailsize) and [`onThumbnailReady`](/docs/api/interfaces/TakePhotoOptions#onthumbnailready) options:
89+
90+
```tsx
91+
const photo = await camera.current.takePhoto({
92+
thumbnailSize: { width: 200, height: 200 },
93+
onThumbnailReady: (thumbnail) => {
94+
// Thumbnail is ready! Display it immediately
95+
setThumbnailUri(`file://${thumbnail.path}`)
96+
}
97+
})
98+
99+
// Full photo is now ready
100+
setPhotoUri(`file://${photo.path}`)
101+
```
102+
103+
The `onThumbnailReady` callback is invoked as soon as the thumbnail is generated, which typically happens before the full photo is saved. This allows you to:
104+
- Display a preview to the user immediately
105+
- Show a loading state with the thumbnail while uploading the full image
106+
- Reduce memory usage by rendering thumbnails in lists instead of full photos
107+
108+
**Platform implementations:**
109+
- **iOS**: Uses the embedded thumbnail from the camera capture if available for maximum performance
110+
- **Android**: Uses memory-efficient downsampling with hardware-accelerated decoding (never loads the full image into memory)
111+
112+
Both implementations are optimized for their respective platforms and generate thumbnails asynchronously without blocking photo capture.
113+
114+
:::tip
115+
The thumbnail is stored in a temporary directory just like the main photo. Remember to clean up temporary files when you're done with them.
116+
:::
117+
84118
### Photo Quality Balance
85119

86120
The photo capture pipeline can be configured to prioritize speed over quality, quality over speed or balance both quality and speed using the [`photoQualityBalance`](/docs/api/interfaces/CameraProps#photoQualityBalance) prop.

example/ios/Podfile.lock

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,16 +1768,16 @@ PODS:
17681768
- ReactCommon/turbomodule/core
17691769
- Yoga
17701770
- SocketRocket (0.7.0)
1771-
- VisionCamera (4.6.3):
1772-
- VisionCamera/Core (= 4.6.3)
1773-
- VisionCamera/FrameProcessors (= 4.6.3)
1774-
- VisionCamera/React (= 4.6.3)
1775-
- VisionCamera/Core (4.6.3)
1776-
- VisionCamera/FrameProcessors (4.6.3):
1771+
- VisionCamera (4.8.6):
1772+
- VisionCamera/Core (= 4.8.6)
1773+
- VisionCamera/FrameProcessors (= 4.8.6)
1774+
- VisionCamera/React (= 4.8.6)
1775+
- VisionCamera/Core (4.8.6)
1776+
- VisionCamera/FrameProcessors (4.8.6):
17771777
- React
17781778
- React-callinvoker
17791779
- react-native-worklets-core
1780-
- VisionCamera/React (4.6.3):
1780+
- VisionCamera/React (4.8.6):
17811781
- React-Core
17821782
- VisionCamera/FrameProcessors
17831783
- Yoga (0.0.0)
@@ -2029,77 +2029,77 @@ SPEC CHECKSUMS:
20292029
hermes-engine: ea92f60f37dba025e293cbe4b4a548fd26b610a0
20302030
MMKV: f7d1d5945c8765f97f39c3d121f353d46735d801
20312031
MMKVCore: c04b296010fcb1d1638f2c69405096aac12f6390
2032-
RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740
2032+
RCT-Folly: 34124ae2e667a0e5f0ea378db071d27548124321
20332033
RCTDeprecation: 726d24248aeab6d7180dac71a936bbca6a994ed1
20342034
RCTRequired: a94e7febda6db0345d207e854323c37e3a31d93b
20352035
RCTTypeSafety: 28e24a6e44f5cbf912c66dde6ab7e07d1059a205
20362036
React: c2830fa483b0334bda284e46a8579ebbe0c5447e
20372037
React-callinvoker: 4aecde929540c26b841a4493f70ebf6016691eb8
2038-
React-Core: 9c059899f00d46b5cec3ed79251f77d9c469553d
2039-
React-CoreModules: 9fac2d31803c0ed03e4ddaa17f1481714f8633a5
2040-
React-cxxreact: a979810a3ca4045ceb09407a17563046a7f71494
2038+
React-Core: 32a581847d74ce9b5f51d9d11a4e4d132ad61553
2039+
React-CoreModules: f53e0674e1747fa41c83bc970e82add97b14ad87
2040+
React-cxxreact: 86f3b1692081fd954a0cb27cc90d14674645b64b
20412041
React-debug: 3d21f69d8def0656f8b8ec25c0f05954f4d862c5
2042-
React-defaultsnativemodule: 2fa2bdb7bd03ff9764facc04aa8520ebf14febae
2043-
React-domnativemodule: 986e6fe7569e1383dce452a7b013b6c843a752df
2044-
React-Fabric: 3bc7be9e3a6b7581fc828dc2aa041e107fc8ffb8
2045-
React-FabricComponents: 668e0cb02344c2942e4c8921a643648faa6dc364
2046-
React-FabricImage: 3f44dd25a2b020ed5215d4438a1bb1f3461cd4f1
2042+
React-defaultsnativemodule: 2ed121c5a1edeab09cff382b8d9b538260f07848
2043+
React-domnativemodule: 4393dd5dd7e13dbe42e69ebc791064a616990f91
2044+
React-Fabric: cbf38ceefb1ac6236897abdb538130228e126695
2045+
React-FabricComponents: dd4b01c4a60920d8dc15f3b5594c6fe9e7648a38
2046+
React-FabricImage: 8b13aedfbd20f349b9b3314baf993c71c02995d9
20472047
React-featureflags: ee1abd6f71555604a36cda6476e3c502ca9a48e5
2048-
React-featureflagsnativemodule: 7ccc0cd666c2a6257401dceb7920818ac2b42803
2049-
React-graphics: d7dd9c8d75cad5af19e19911fa370f78f2febd96
2050-
React-hermes: 2069b08e965e48b7f8aa2c0ca0a2f383349ed55d
2051-
React-idlecallbacksnativemodule: e211b2099b6dced97959cb58257bab2b2de4d7ef
2052-
React-ImageManager: ab7a7d17dd0ff1ef1d4e1e88197d1119da9957ce
2053-
React-jserrorhandler: d9e867bb83b868472f3f7601883f0403b3e3942d
2054-
React-jsi: d68f1d516e5120a510afe356647a6a1e1f98f2db
2055-
React-jsiexecutor: 6366a08a0fc01c9b65736f8deacd47c4a397912a
2056-
React-jsinspector: 0ac947411f0c73b34908800cc7a6a31d8f93e1a8
2057-
React-jsitracing: 0e8c0aadb1fcec6b1e4f2a66ee3b0da80f0f8615
2058-
React-logger: d79b704bf215af194f5213a6b7deec50ba8e6a9b
2059-
React-Mapbuffer: b982d5bba94a8bc073bda48f0d27c9b28417fae3
2060-
React-microtasksnativemodule: 2b73e68f0462f3175f98782db08896f8501afd20
2061-
react-native-blur: a1bf334589f44658a58a859b1f3defe28e367fcf
2062-
react-native-cameraroll: b3395629565d1663b4f340c7314e7c04e88c9b3c
2063-
react-native-mmkv: 7d0b6c2a79e73100b933f2947a9c8741d664e18b
2064-
react-native-safe-area-context: 5141f11858b033636f1788b14f32eaba92cee810
2065-
react-native-skia: 8cbbeae25285ade497b646696103832e67ffd64b
2066-
react-native-video: 155a792d16b64f1fb68b1795d805808f34e60488
2067-
react-native-worklets-core: f51430dd07bf5343d4918d28a4bb00fe8f98b982
2048+
React-featureflagsnativemodule: 87b58caf3cd8eca1e53179453789def019af2a65
2049+
React-graphics: f5c4cf3abc5aa083e28fe7a866bd95fb3bbbc1e0
2050+
React-hermes: cad69ee9a53870cc38e5386889aa7ea81c75b6a1
2051+
React-idlecallbacksnativemodule: 445390be0f533797ace18c419eb57110dbfe90d6
2052+
React-ImageManager: cb78d7a24f45f8f9a5a1640b52fce4c9f637f98d
2053+
React-jserrorhandler: dfe9b96e99a93d4f4858bad66d5bc4813a87a21a
2054+
React-jsi: bc1f6073e203fb540edd6d26f926ad041809b443
2055+
React-jsiexecutor: 1e8fc70dd9614c3e9d5c3c876b2ea3cd1d931ee4
2056+
React-jsinspector: 7544a20e9beac390f1b65d9f0040d97cd55dc198
2057+
React-jsitracing: cac972ccc097db399df8044e49add8e5b25cb34a
2058+
React-logger: 80d87daf2f98bf95ab668b79062c1e0c3f0c2f8a
2059+
React-Mapbuffer: acffb35a53a5f474ede09f082ac609b41aafab2e
2060+
React-microtasksnativemodule: 71ca9282bce93b319218d75362c0d646b376eb43
2061+
react-native-blur: 3d5dd1ed2dd810b304ac3bcee9cf7d460757c89b
2062+
react-native-cameraroll: 418588be23947285410dd3fc72f0fd07271d87a3
2063+
react-native-mmkv: f84bf6f48c906911695f9cd16d39d4fb78fcd5a4
2064+
react-native-safe-area-context: 17a482f540310e2209f884fd49472d9e1c0e73d6
2065+
react-native-skia: 08ff502bcedd2ff2a5f5896debc3d7b4c773d312
2066+
react-native-video: f93e7e72f773181440d1acaf6186b369447ff30a
2067+
react-native-worklets-core: f730c01db8ea3d580e322617f4a631206f1905fb
20682068
React-nativeconfig: 8c83d992b9cc7d75b5abe262069eaeea4349f794
2069-
React-NativeModulesApple: 9f7920224a3b0c7d04d77990067ded14cee3c614
2069+
React-NativeModulesApple: 97f606f09fd9840b3868333984d6a0e7bcc425b5
20702070
React-perflogger: 59e1a3182dca2cee7b9f1f7aab204018d46d1914
2071-
React-performancetimeline: a9d05533ff834c6aa1f532e05e571f3fd2e3c1ed
2071+
React-performancetimeline: 3e3f5c5576fe1cc2dd5fcfb1ae2046d5dceda3d7
20722072
React-RCTActionSheet: d80e68d3baa163e4012a47c1f42ddd8bcd9672cc
2073-
React-RCTAnimation: bde981f6bd7f8493696564da9b3bd05721d3b3cc
2074-
React-RCTAppDelegate: 0176615c51476c88212bf3edbafb840d39ea7631
2075-
React-RCTBlob: 520a0382bf8e89b9153d60e3c6293e51615834e9
2076-
React-RCTFabric: c9da097b19b30017a99498b8c66a69c72f3ce689
2077-
React-RCTImage: 90448d2882464af6015ed57c98f463f8748be465
2078-
React-RCTLinking: 1bd95d0a704c271d21d758e0f0388cced768d77d
2079-
React-RCTNetwork: 218af6e63eb9b47935cc5a775b7a1396cf10ff91
2080-
React-RCTSettings: e10b8e42b0fce8a70fbf169de32a2ae03243ef6b
2081-
React-RCTText: e7bf9f4997a1a0b45c052d4ad9a0fe653061cf29
2082-
React-RCTVibration: 5b70b7f11e48d1c57e0d4832c2097478adbabe93
2073+
React-RCTAnimation: 051f0781709c5ed80ba8aa2b421dfb1d72a03162
2074+
React-RCTAppDelegate: 106d225d076988b06aa4834e68d1ab754f40cacf
2075+
React-RCTBlob: 895eaf8bca2e76ee1c95b479235c6ccebe586fc6
2076+
React-RCTFabric: 8d01df202ee9e933f9b5dd44b72ec89a7ac6ee01
2077+
React-RCTImage: b73149c0cd54b641dba2d6250aaf168fee784d9f
2078+
React-RCTLinking: 23e519712285427e50372fbc6e0265d422abf462
2079+
React-RCTNetwork: a5d06d122588031989115f293654b13353753630
2080+
React-RCTSettings: 87d03b5d94e6eadd1e8c1d16a62f790751aafb55
2081+
React-RCTText: 75e9dd39684f4bcd1836134ac2348efaca7437b3
2082+
React-RCTVibration: 033c161fe875e6fa096d0d9733c2e2501682e3d4
20832083
React-rendererconsistency: f620c6e003e3c4593e6349d8242b8aeb3d4633f0
2084-
React-rendererdebug: e697680f4dd117becc5daf9ea9800067abcee91c
2084+
React-rendererdebug: 5be7b834677b2a7a263f4d2545f0d4966cafad82
20852085
React-rncore: c22bd84cc2f38947f0414fab6646db22ff4f80cd
2086-
React-RuntimeApple: de0976836b90b484305638616898cbc665c67c13
2087-
React-RuntimeCore: 3c4a5aa63d9e7a3c17b7fb23f32a72a8bcfccf57
2086+
React-RuntimeApple: 71160e6c02efa07d198b84ef5c3a52a7d9d0399d
2087+
React-RuntimeCore: f88f79ec995c12af56a265d7505c7630733d9d82
20882088
React-runtimeexecutor: ea90d8e3a9e0f4326939858dafc6ab17c031a5d3
2089-
React-RuntimeHermes: c6b0afdf1f493621214eeb6517fb859ce7b21b81
2090-
React-runtimescheduler: 84f0d876d254bce6917a277b3930eb9bc29df6c7
2091-
React-utils: cbe8b8b3d7b2ac282e018e46f0e7b25cdc87c5a0
2092-
ReactCodegen: d81fb52765155a5a3c41465e71ee547ce2bc1b7a
2093-
ReactCommon: 6a952e50c2a4b694731d7682aaa6c79bc156e4ad
2094-
RNGestureHandler: 6dfe7692a191ee224748964127114edf057a1475
2095-
RNReanimated: 5281007b55b4a8e6772de98803a9c925587e9c64
2096-
RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06
2097-
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
2098-
RNVectorIcons: 6382277afab3c54658e9d555ee0faa7a37827136
2089+
React-RuntimeHermes: 49f86328914021f50fd5a5b9756685f5f6d8b4da
2090+
React-runtimescheduler: fed70991b942c6df752a59a22081e45fc811b11c
2091+
React-utils: 02526ea15628a768b8db9517b6017a1785c734d2
2092+
ReactCodegen: 9f910fe2030dfaf3803d227988f56fb60f6cd0e0
2093+
ReactCommon: 36d48f542b4010786d6b2bcee615fe5f906b7105
2094+
RNGestureHandler: d21c9c1cc8b1bb19336d2e1bc48e56882bd13bc6
2095+
RNReanimated: b900d6a958fe030855e80c3e6df7d9dab60975a4
2096+
RNScreens: 28fbd73104cc9719371da9d9aaa3c70d876a8c6b
2097+
RNStaticSafeAreaInsets: 4696b82d3a11ba6f3a790159ddb7290f04abd275
2098+
RNVectorIcons: 182892e7d1a2f27b52d3c627eca5d2665a22ee28
20992099
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
2100-
VisionCamera: 88df4dae7196c93ecd331f105f0e5d7d95702cb3
2101-
Yoga: aa3df615739504eebb91925fc9c58b4922ea9a08
2100+
VisionCamera: 49ac44838511c7db34fcd654f191a542534e07ae
2101+
Yoga: 055f92ad73f8c8600a93f0e25ac0b2344c3b07e6
21022102

21032103
PODFILE CHECKSUM: 2ad84241179871ca890f7c65c855d117862f1a68
21042104

2105-
COCOAPODS: 1.15.2
2105+
COCOAPODS: 1.16.2

example/src/CameraPage.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { GestureResponderEvent } from 'react-native'
44
import { StyleSheet, Text, View } from 'react-native'
55
import type { PinchGestureHandlerGestureEvent } from 'react-native-gesture-handler'
66
import { PinchGestureHandler, TapGestureHandler } from 'react-native-gesture-handler'
7-
import type { CameraProps, CameraRuntimeError, PhotoFile, VideoFile } from 'react-native-vision-camera'
7+
import type { CameraProps, CameraRuntimeError, PhotoFile, ThumbnailFile, VideoFile } from 'react-native-vision-camera'
88
import {
99
runAtTargetFps,
1010
useCameraDevice,
@@ -55,6 +55,7 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
5555
const [enableHdr, setEnableHdr] = useState(false)
5656
const [flash, setFlash] = useState<'off' | 'on'>('off')
5757
const [enableNightMode, setEnableNightMode] = useState(false)
58+
const [thumbnail, setThumbnail] = useState<ThumbnailFile | null>(null)
5859

5960
// camera device settings
6061
const [preferredDevice] = usePreferredCameraDevice()
@@ -112,12 +113,15 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
112113
const onMediaCaptured = useCallback(
113114
(media: PhotoFile | VideoFile, type: 'photo' | 'video') => {
114115
console.log(`Media captured! ${JSON.stringify(media)}`)
116+
console.log(`Thumbnail: ${JSON.stringify(thumbnail)}`)
117+
console.log(new Date())
115118
navigation.navigate('MediaPage', {
116119
path: media.path,
117120
type: type,
121+
thumbnail: thumbnail,
118122
})
119123
},
120-
[navigation],
124+
[navigation, thumbnail],
121125
)
122126
const onFlipCameraPressed = useCallback(() => {
123127
setCameraPosition((p) => (p === 'back' ? 'front' : 'back'))
@@ -178,6 +182,15 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
178182
location.requestPermission()
179183
}, [location])
180184

185+
const onThumbnailReady = useCallback(
186+
(t: ThumbnailFile) => {
187+
console.log(`=============thumbnail Ready=============\n${t.width}x${t.height}`)
188+
console.log(new Date())
189+
setThumbnail(t)
190+
},
191+
[setThumbnail],
192+
)
193+
181194
const frameProcessor = useFrameProcessor((frame) => {
182195
'worklet'
183196

@@ -248,6 +261,7 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
248261
flash={supportsFlash ? flash : 'off'}
249262
enabled={isCameraInitialized && isActive}
250263
setIsPressingButton={setIsPressingButton}
264+
onThumbnailReady={onThumbnailReady}
251265
/>
252266

253267
<StatusBarBlurBackground />

example/src/MediaPage.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const isVideoOnLoadEvent = (event: OnLoadData | OnLoadImage): event is OnLoadDat
3333

3434
type Props = NativeStackScreenProps<Routes, 'MediaPage'>
3535
export function MediaPage({ navigation, route }: Props): React.ReactElement {
36-
const { path, type } = route.params
36+
const { path, type, thumbnail } = route.params
3737
const [hasMediaLoaded, setHasMediaLoaded] = useState(false)
3838
const isForeground = useIsForeground()
3939
const isScreenFocused = useIsFocused()
@@ -85,7 +85,10 @@ export function MediaPage({ navigation, route }: Props): React.ReactElement {
8585
return (
8686
<View style={[styles.container, screenStyle]}>
8787
{type === 'photo' && (
88-
<Image source={source} style={StyleSheet.absoluteFill} resizeMode="cover" onLoadEnd={onMediaLoadEnd} onLoad={onMediaLoad} />
88+
<>
89+
<Image source={source} style={StyleSheet.absoluteFill} resizeMode="cover" onLoadEnd={onMediaLoadEnd} onLoad={onMediaLoad} />
90+
{thumbnail !== null && <Image source={{ uri: `file://${thumbnail.path}` }} style={styles.thumbnail} resizeMode="cover" />}
91+
</>
8992
)}
9093
{type === 'video' && (
9194
<Video
@@ -152,4 +155,14 @@ const styles = StyleSheet.create({
152155
},
153156
textShadowRadius: 1,
154157
},
158+
thumbnail: {
159+
position: 'absolute',
160+
right: SAFE_AREA_PADDING.paddingLeft,
161+
bottom: SAFE_AREA_PADDING.paddingBottom,
162+
width: 75,
163+
height: 120,
164+
borderRadius: 8,
165+
borderWidth: 2,
166+
borderColor: 'white',
167+
},
155168
})

example/src/Routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import type { ThumbnailFile } from 'react-native-vision-camera'
2+
13
export type Routes = {
24
PermissionsPage: undefined
35
CameraPage: undefined
46
CodeScannerPage: undefined
57
MediaPage: {
68
path: string
79
type: 'video' | 'photo'
10+
thumbnail: ThumbnailFile | null
811
}
912
Devices: undefined
1013
}

example/src/views/CaptureButton.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Reanimated, {
1515
useSharedValue,
1616
withRepeat,
1717
} from 'react-native-reanimated'
18-
import type { Camera, PhotoFile, VideoFile } from 'react-native-vision-camera'
18+
import type { Camera, PhotoFile, ThumbnailFile, VideoFile } from 'react-native-vision-camera'
1919
import { CAPTURE_BUTTON_SIZE, SCREEN_HEIGHT, SCREEN_WIDTH } from './../Constants'
2020

2121
const START_RECORDING_DELAY = 200
@@ -24,7 +24,7 @@ const BORDER_WIDTH = CAPTURE_BUTTON_SIZE * 0.1
2424
interface Props extends ViewProps {
2525
camera: React.RefObject<Camera>
2626
onMediaCaptured: (media: PhotoFile | VideoFile, type: 'photo' | 'video') => void
27-
27+
onThumbnailReady: (thumbnail: ThumbnailFile) => void
2828
minZoom: number
2929
maxZoom: number
3030
cameraZoom: Reanimated.SharedValue<number>
@@ -46,6 +46,7 @@ const _CaptureButton: React.FC<Props> = ({
4646
enabled,
4747
setIsPressingButton,
4848
style,
49+
onThumbnailReady,
4950
...props
5051
}): React.ReactElement => {
5152
const pressDownDate = useRef<Date | undefined>(undefined)
@@ -59,15 +60,21 @@ const _CaptureButton: React.FC<Props> = ({
5960
if (camera.current == null) throw new Error('Camera ref is null!')
6061

6162
console.log('Taking photo...')
63+
console.log(new Date());
6264
const photo = await camera.current.takePhoto({
6365
flash: flash,
6466
enableShutterSound: false,
67+
thumbnailSize: {
68+
width: 300,
69+
height: 300,
70+
},
71+
onThumbnailReady,
6572
})
6673
onMediaCaptured(photo, 'photo')
6774
} catch (e) {
6875
console.error('Failed to take photo!', e)
6976
}
70-
}, [camera, flash, onMediaCaptured])
77+
}, [camera, flash, onMediaCaptured, onThumbnailReady])
7178

7279
const onStoppedRecording = useCallback(() => {
7380
isRecording.current = false

0 commit comments

Comments
 (0)