@onekeyfe/react-native-image-crop-picker
v6.7.0-41
Published
Single photo picker and cropper Nitro module for OneKey, replacing react-native-image-crop-picker
Maintainers
Readme
@onekeyfe/react-native-image-crop-picker
Single photo picker and cropper for OneKey, built on Nitro Modules. It replaces react-native-image-crop-picker and keeps the part of its API that OneKey uses: openPicker, openCropper, clean, cleanSingle, and the E_* error codes.
Picking never asks for photo library permission:
iOS picks with
PHPickerViewController, which runs out of process.react-native-image-crop-pickerrequested full photo library access first. Once a user denied it, iOS never showed the prompt again and every lateropenPickercall failed silently (OK-48227). The crop gestures come from a vendored TOCropViewController 3.2.0TOCropView, with the OK-51551 rotation fix that upstream still lacks.Android picks with the system Photo Picker (
ActivityResultContracts.PickVisualMedia, which falls back toACTION_OPEN_DOCUMENTon devices without it). The crop gestures come from uCrop2.2.11-native'sUCropView. Activity results go through the activity'sActivityResultRegistry, so noActivityEventListeneris needed.Web (browsers, Electron and browser extensions) picks with an
<input type="file">and crops with a DOM cropper, with no Nitro or native code. Bundlers select it through thebrowserexport condition (lib/module/index.web.js).
All platforms show the same cropper screen, drawn by this package rather than by TOCropViewController, uCrop or a web cropper library. See Cropper screen.
The behavioral contract and pending acceptance cases are in docs/SPEC.md.
Original selection
const image = await ImageCropPicker.openPicker({ preserveOriginal: true });
// Read the selected file before cleaning it.
await ImageCropPicker.cleanSingle(image.path);Original selection preserves the selected representation without JPEG conversion.
Compression options are ignored unless cropping is enabled. Originals are limited
to 64 MiB. Saving and its authorization are provided by the separate
@onekeyfe/react-native-photo-library; this picker exposes neither saving nor
photo-library permission APIs.
Installation
yarn add @onekeyfe/react-native-image-crop-picker react-native-nitro-modulesTo keep existing react-native-image-crop-picker imports working, install it under that name with an npm alias instead: react-native-image-crop-picker@npm:@onekeyfe/react-native-image-crop-picker.
- iOS: do not also install the
TOCropViewControllerpod. This package compiles its own copy, and the duplicate classes would clash at link time. - Android: uCrop is only published to JitPack. Add
maven { url "https://www.jitpack.io" }to the app'sallprojects.repositories.
Usage
import ImageCropPicker, {
ImageCropPickerError,
} from '@onekeyfe/react-native-image-crop-picker';
try {
const image = await ImageCropPicker.openPicker({
width: 240,
height: 240,
cropping: true,
includeBase64: true,
compressImageQuality: 0.8,
});
// image.path is a file:// URI; image.data is base64 without a data: prefix.
} catch (error) {
if (error instanceof ImageCropPickerError && error.code === 'E_PICKER_CANCELLED') {
return;
}
throw error;
}
const cropped = await ImageCropPicker.openCropper({
path: 'file:///path/to/image.jpg',
width: 480,
height: 800,
});openCropper accepts file:// URIs, absolute paths, http(s):// URLs, data: URIs and, on Android, content:// URIs.
Cropper screen
The cropper is a full-screen page with the same layout and metrics on iOS and Android:
- A 56 pt header with
cropperToolbarTitlecentered and a rotate button on the right. The rotate button turns the image 90° counterclockwise;cropperRotateButtonsHiddenhides it. - The crop area. The crop box keeps 20 pt from every edge, and the image outside it shows the page background at 70% opacity. The rule-of-thirds grid shows while the image is moved, unless
showCropGuidelinesisfalse. - A footer with two capsule buttons,
cropperCancelTextandcropperChooseText, 50 pt tall with 10 pt between them. The confirm button shows a spinner while the image is saved.
On the web the same screen is full screen on windows narrower than 640 px or shorter than 560 px, and a centered 560 × 760 px panel (smaller when the window is) otherwise. The header, crop area, toolbar and footer are a flex column, so the buttons always stay inside the panel. The web screen adds, for mouse and keyboard users:
- A toolbar under the crop area: zoom out, a zoom slider, zoom in, and reset.
- Wheel to zoom around the pointer (trackpad pinch included), double click to zoom in 2× or back out, and two-finger pinch on touch screens.
- Keyboard: arrow keys move the image (Shift for larger steps),
+/-zoom,Rrotates, Enter confirms and Escape cancels. Focus stays inside the cropper and returns to the previous element when it closes.
cropperAppearance sets its colors and fonts. The optional font-family fields
below require the app to provide these Roobert faces and own the applicable
font license; this package contains no font files. Omit the fields to use
platform defaults.
await ImageCropPicker.openPicker({
width: 240,
height: 240,
cropping: true,
cropperToolbarTitle: 'Crop image',
cropperAppearance: {
colorScheme: 'dark',
backgroundColor: '#0f0f0f',
confirmButtonColor: '#ffffffed',
confirmButtonTextColor: '#000000df',
titleFontFamily: 'Roobert-SemiBold',
buttonFontFamily: 'Roobert-Medium',
},
});| Field | Used for |
| --- | --- |
| colorScheme | 'light' or 'dark'. Picks the fallback palette and the status bar style. Defaults to the system appearance. |
| backgroundColor | The page. The area outside the crop box is this color at 70% opacity. |
| titleColor | The title and the crop box border. |
| iconColor | The rotate button. |
| cancelButtonColor, cancelButtonPressedColor, cancelButtonTextColor | The cancel button. |
| confirmButtonColor, confirmButtonPressedColor, confirmButtonTextColor | The confirm button. |
| titleFontFamily, buttonFontFamily | Fonts bundled with the app. iOS looks them up with UIFont(name:), Android with React Native's ReactFontManager. On the web they are CSS font families, drawn at weight 600 and 500. |
| scale | Multiplies every size, for apps that scale their UI. |
Colors are CSS hex strings: #RGB, #RRGGBB or #RRGGBBAA. Anything left out falls back to OneKey's light or dark palette.
Behavior
- Processed results are always JPEG.
widthandheightset the crop aspect ratio, and the cropped image is scaled to exactly that size, so a crop box a pixel off the ratio still yields the requested dimensions. WithfreeStyleCropEnabled, the crop keeps its own aspect ratio and is scaled to fit insidewidth×height. compressImageMaxWidth,compressImageMaxHeightandcompressImageQualityapply after cropping. The default quality is 0.8 on iOS and 1 on Android, as inreact-native-image-crop-picker.- Photos are decoded at most 4096 px on the long side, which keeps very large photos from exhausting memory.
cropRectis reported in the original image's coordinates on iOS. - Results are written to
<tmp>/react-native-image-crop-picker/on iOS and<cache>/react-native-image-crop-picker/on Android.clean()empties that directory. - On the web, results are JPEG blobs:
pathis ablob:URL, andclean()/cleanSingle()revoke the URLs this package created. The default quality is 0.8, as on iOS.cropRectis in the coordinates of the image as the browser decodes it (EXIF orientation applied), rotated with the crop. WithpreserveOriginal,mimecomes from the file's leading bytes rather thanFile.type, and a file in an unrecognized format rejectsE_NO_IMAGE_DATA_FOUND.http(s)images foropenCroppermust allow CORS, or the crop cannot be read back and rejectsE_CANNOT_SAVE_IMAGE.freeStyleCropEnabledis not implemented on the web; the crop box keeps thewidth/heightratio. WithcropperCircleOverlaythe crop box is 1:1 with no frame or grid on every platform, whatever the image or target ratio. - Only one picker or cropper can be open at a time. A second call rejects with
E_PICKER_IN_PROGRESS. - On iOS, cancelling the cropper that
openPickeropened returns to the photo picker. On Android and the web it rejects withE_PICKER_CANCELLED, as does dismissing the web file chooser. - On Android the cropper keeps the calling activity's requested orientation, so a portrait-only app gets a portrait-only cropper.
Not supported
Multiple selection, video, the camera and includeExif are not implemented. mediaType, forceJpg and sortOrder are accepted for source compatibility and ignored.
Error codes
| Code | Meaning |
| --- | --- |
| E_PICKER_CANCELLED | The user closed the picker or the cropper. |
| E_PICKER_IN_PROGRESS | Another picker or cropper is still open. |
| E_ACTIVITY_DOES_NOT_EXIST | Android: no current activity. |
| E_FAILED_TO_SHOW_PICKER | The picker or cropper could not be presented. |
| E_NO_IMAGE_DATA_FOUND | The selected item could not be read as an image. |
| E_CROPPER_IMAGE_NOT_FOUND | openCropper could not load path. |
| E_CANNOT_SAVE_IMAGE | The result could not be written. |
| E_LOW_MEMORY_ERROR | Android: out of memory while processing. |
| E_ERROR_WHILE_CLEANING_FILES | clean or cleanSingle failed. |
| E_UNKNOWN | Any other native error. |
License
MIT. The vendored TOCropViewController in ios/TOCropViewController is MIT licensed by Tim Oliver; see its LICENSE.
