Skip to content

Latest commit

 

History

History
1122 lines (905 loc) · 53.8 KB

react-native-image-crop-picker.md

File metadata and controls

1122 lines (905 loc) · 53.8 KB

模板版本:v0.3.0

react-native-image-crop-picker

本项目基于 [email protected] 开发。

Version Package Name Repository Release
<= 0.40.3-0.0.14@deprecated @react-native-oh-tpl/react-native-image-crop-picker Github(deprecated) Github Releases(deprecated)
>= 0.40.4 @react-native-ohos/react-native-image-crop-picker Gitee Gitee Releases

1.安装与使用

进入到工程目录并输入以下命令:

npm

npm install @react-native-ohos/react-native-image-crop-picker

yarn

yarn add @react-native-ohos/react-native-image-crop-picker

下面的代码展示了这个库的基本使用场景:

[!TIP] 使用时 import 的库名不变。

import ImagePicker from 'react-native-image-crop-picker';
import { openPicker } from 'react-native-image-crop-picker';
import React from 'react';
import { Text, StyleSheet, TextInput, View, Button, ScrollView, Switch } from 'react-native';

const ImageCropPickDemo = () => {
    const TAG: string = 'ImageCropPickerTurboModule';
    const [maxFiles, setMaxFiles] = React.useState('');
    const [imageQuality, setImageQuality] = React.useState('');
    const [imagePath, setImagePath] = React.useState('');
    const [clearImagePath, setClearImagePath] = React.useState('');
    const [cropperTitle, setCropperTitle] = React.useState('');
    const [chooseText, setChooseText] = React.useState('');
    const [chooseColor, setChooseColor] = React.useState('');
    const [cancelText, setCancelText] = React.useState('');
    const [cancelColor, setCancelColor] = React.useState('');
    const [cropperRotate, setCropperRotate] = React.useState(false);
    const [showCropGuidelines, setShowCropGuidelines] = React.useState(true);
    const [showCropFrame, setShowCropFrame] = React.useState(true);
    const [multiple, setMultiple] = React.useState(false);
    const [includeExif, setIncludeExif] = React.useState(false);
    const [avoidEmptySpace, setAvoidEmptySpace] = React.useState(false);
    const [writeTempFile, setTempFile] = React.useState(true);
    const [includeBase64, setBase64] = React.useState(false);
    const [freeStyleCropEnabled, setFreeStyleCropEnabled] = React.useState(false);
    const [forceJpg, setForceJpg] = React.useState(false);
    const [showsSelectedCount, setShowsSelectedCount] = React.useState(true);
    const [selectedButton, setSelectedButton] = React.useState('any');
    const [useFrontCamera, setUseFrontCamera] = React.useState(false);
    const [croppingCamera, setCroppingCamera] = React.useState(false);
    const [writeTempFileCamera, setTempFileCamera] = React.useState(true);
    const [includeBase64Camera, setBase64Camera] = React.useState(false);
    const [includeExifCamera, setIncludeExifCamera] = React.useState(false);
    const [avoidEmptySpaceCamera, setAvoidEmptySpaceCamera] = React.useState(false);
    const [freeStyleCropEnabledCamera, setFreeStyleCropEnabledCamera] = React.useState(false);
    const [forceJpgCamera, setForceJpgCamera] = React.useState(false);
    const [mediaTypeCamera, setMediaTypeCamera] = React.useState('any');
    const [imageQualityCamera, setImageQualityCamera] = React.useState('');
    const [cropperTitleCamera, setCropperTitleCamera] = React.useState('');
    const [chooseTextCamera, setChooseTextCamera] = React.useState('');
    const [chooseColorCamera, setChooseColorCamera] = React.useState('');
    const [cancelTextCamera, setCancelTextCamera] = React.useState('');
    const [cancelColorCamera, setCancelColorCamera] = React.useState('');
    const [cropperRotateCamera, setCropperRotateCamera] = React.useState(false);
    const [showCropGuidelinesCamera, setShowCropGuidelinesCamera] = React.useState(true);
    const [showCropFrameCamera, setShowCropFrameCamera] = React.useState(true);
    const [writeTempFileCropper, setTempFileCropper] = React.useState(true);
    const [forceJpgCropper, setForceJpgCropper] = React.useState(false);
    const [includeBase64Cropper, setBase64Cropper] = React.useState(false);
    const [includeExifCropper, setIncludeExifCropper] = React.useState(false);
    const [avoidEmptySpaceCropper, setAvoidEmptySpaceCropper] = React.useState(false);
    const [freeStyleCropEnabledCropper, setFreeStyleCropEnabledCropper] = React.useState(false);
    const [imageQualityCropper, setimageQualityCropper] = React.useState('');

    const handleButtonPress = (buttonName) => {
        setSelectedButton(buttonName);
    };

    const handleMediaType = (buttonName) => {
        setMediaTypeCamera(buttonName);
    };

    return (
        <ScrollView style={styles.container}>
            <Text style={styles.title}>相机、图库、裁剪功能:</Text>
            <View style={styles.container}>

                <View style={styles.TextInputBox}>

                    <Text style={styles.inputLable}>multiple:</Text>
                    <Button
                        title={`${multiple}`}
                        onPress={() => multiple ? setMultiple(false) : setMultiple(true)}
                    />

                    <Text style={styles.inputLable}>writeTempFile:</Text>
                    <Button
                        title={`${writeTempFile}`}
                        onPress={() => writeTempFile ? setTempFile(false) : setTempFile(true)}
                    />
                </View>

                <View style={styles.TextInputBox}>

                    <Text style={styles.inputLable}>includeBase64:</Text>
                    <Button
                        title={`${includeBase64}`}
                        onPress={() => includeBase64 ? setBase64(false) : setBase64(true)}
                    />

                    <Text style={styles.inputLable}>includeExif:</Text>
                    <Button
                        title={`${includeExif}`}
                        onPress={() => includeExif ? setIncludeExif(false) : setIncludeExif(true)}
                    />

                </View>

                <View style={styles.TextInputBox}>

                    <Text style={styles.inputLable}>avoidEmptySpaceAroundImage :</Text>
                    <Button
                        title={`${avoidEmptySpace}`}
                        onPress={() => avoidEmptySpace ? setAvoidEmptySpace(false) : setAvoidEmptySpace(true)}
                    />

                </View>

                <View style={styles.TextInputBox}>

                    <Text style={styles.inputLable}>freeStyleCropEnabled :</Text>
                    <Button
                        title={`${freeStyleCropEnabled}`}
                        onPress={() => freeStyleCropEnabled ? setFreeStyleCropEnabled(false) : setFreeStyleCropEnabled(true)}
                    />

                </View>

                <View style={styles.TextInputBox}>

                    <Text style={styles.inputLable}>forceJpg:</Text>
                    <Button
                        title={`${forceJpg}`}
                        onPress={() => forceJpg ? setForceJpg(false) : setForceJpg(true)}
                    />

                    <Text style={styles.inputLable}>showsSelectedCount:</Text>
                    <Button
                        title={`${showsSelectedCount}`}
                        onPress={() => showsSelectedCount ? setShowsSelectedCount(false) : setShowsSelectedCount(true)}
                    />

                </View>


                <View style={styles.TextInputBox}>
                    <Text style={styles.inputLable}>mediaType:</Text>
                    <Button
                        title='photo'
                        onPress={() => handleButtonPress('photo')}
                        accessibilityState={{ selected: selectedButton === 'photo' }}
                    />
                    <Button
                        title='video'
                        onPress={() => handleButtonPress('video')}
                        accessibilityState={{ selected: selectedButton === 'video' }}
                    />
                    <Button
                        title='any'
                        onPress={() => handleButtonPress('any')}
                        accessibilityState={{ selected: selectedButton === 'any' }}
                    />
                </View>
                <View style={styles.TextInputBox}>
                     <Text style={{color:'red'}}>mediaType is {selectedButton}</Text>
                </View>

                <View style={styles.TextInputBox}>
                    <Text style={styles.inputLable}>minFiles:</Text>
                    <Text style={styles.inputLable}>1</Text>
                </View>

                <View style={styles.TextInputBox}>
                    <Text style={styles.inputLable}>maxFiles:</Text>
                    <TextInput
                       keyboardType="numeric"
                       maxLength={1}
                       style={styles.numberInput}
                       onChangeText={setMaxFiles}
                       value={maxFiles}
                    />
                    <Text style={styles.lableType}>(number)</Text>
                  </View>

                <View style={styles.TextInputBox}>
                    <Text style={styles.inputLable}>compressImageQuality:</Text>
                    <TextInput
                        style={styles.numberInput}
                        onChangeText={setImageQuality}
                        value={imageQuality}
                    />
                    <Text style={styles.lableType}>(0.1 到 1)</Text>
                </View>

                <View >
                    <View style={styles.buttonSty}>
                        <Button
                            title='openPicker(打开图库)'
                            onPress={() => {
                                openPicker({
                                    multiple: multiple,
                                    writeTempFile: writeTempFile,
                                    includeBase64: includeBase64,
                                    includeExif: includeExif,
                                    avoidEmptySpaceAroundImage: avoidEmptySpace,
                                    freeStyleCropEnabled: freeStyleCropEnabled,
                                    forceJpg: forceJpg,
                                    showsSelectedCount: showsSelectedCount,
                                    mediaType: selectedButton,
                                    minFiles: 1,
                                    maxFiles: maxFiles,
                                    compressImageQuality: imageQuality,
                                }).then(image => {
                                    console.log(TAG + ' openPicker result ' + JSON.stringify(image))
                                });
                            }}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>cropping:</Text>
                        <Button
                            title={`${croppingCamera}`}
                            onPress={() => croppingCamera ? setCroppingCamera(false) : setCroppingCamera(true)}
                        />

                        <Text style={styles.inputLable}>writeTempFile:</Text>
                        <Button
                            title={`${writeTempFileCamera}`}
                            onPress={() => writeTempFileCamera ? setTempFileCamera(false) : setTempFileCamera(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>includeBase64:</Text>
                        <Button
                            title={`${includeBase64Camera}`}
                            onPress={() => includeBase64Camera ? setBase64Camera(false) : setBase64Camera(true)}
                        />

                        <Text style={styles.inputLable}>includeExif:</Text>
                        <Button
                            title={`${includeExifCamera}`}
                            onPress={() => includeExifCamera ? setIncludeExifCamera(false) : setIncludeExifCamera(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>avoidEmptySpaceAroundImage :</Text>
                        <Button
                            title={`${avoidEmptySpaceCamera}`}
                            onPress={() => avoidEmptySpaceCamera ? setAvoidEmptySpaceCamera(false) : setAvoidEmptySpaceCamera(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>freeStyleCropEnabled :</Text>
                        <Button
                            title={`${freeStyleCropEnabledCamera}`}
                            onPress={() => freeStyleCropEnabledCamera ? setFreeStyleCropEnabledCamera(false) : setFreeStyleCropEnabledCamera(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>useFrontCamera:</Text>
                        <Button
                            title={`${useFrontCamera}`}
                            onPress={() => useFrontCamera ? setUseFrontCamera(false) : setUseFrontCamera(true)}
                        />

                        <Text style={styles.inputLable}>forceJpg:</Text>
                        <Button
                            title={`${forceJpgCamera}`}
                            onPress={() => forceJpgCamera ? setForceJpgCamera(false) : setForceJpgCamera(true)}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.inputLable}>mediaType:</Text>
                        <Button
                            title='photo'
                            onPress={() => handleMediaType('photo')}
                            accessibilityState={{ selected: mediaTypeCamera === 'photo' }}
                        />
                        <Button
                            title='video'
                            onPress={() => handleMediaType('video')}
                            accessibilityState={{ selected: mediaTypeCamera === 'video' }}
                        />
                        <Button
                            title='any'
                            onPress={() => handleMediaType('any')}
                            accessibilityState={{ selected: mediaTypeCamera === 'any' }}
                        />
                    </View>
                    <View style={styles.TextInputBox}>
                        <Text style={{ color: 'red' }}>mediaType is {mediaTypeCamera}</Text>
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.inputLable}>compressImageQuality:</Text>
                        <TextInput
                            style={styles.numberInput}
                            onChangeText={setImageQualityCamera}
                            value={imageQualityCamera}
                        />
                        <Text style={styles.lableType}>(0.1 到 1)</Text>
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperToolbarTitle:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={5}
                            onChangeText={(value) => setCropperTitleCamera(value)}
                            value={cropperTitleCamera}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperChooseText:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={5}
                            onChangeText={(value) => setChooseTextCamera(value)}
                            value={chooseTextCamera}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperChooseColor:例如 #FF0000</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={7}
                            onChangeText={(value) => setChooseColorCamera(value)}
                            value={chooseColorCamera}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperCancelText:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={5}
                            onChangeText={(value) => setCancelTextCamera(value)}
                            value={cancelTextCamera}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperCancelColor:例如 #FF0000</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={7}
                            onChangeText={(value) => setCancelColorCamera(value)}
                            value={cancelColorCamera}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>cropperRotateButtonsHidden:</Text>
                        <Button
                            title={`${cropperRotateCamera}`}
                            onPress={() => cropperRotateCamera ? setCropperRotateCamera(false) : setCropperRotateCamera(true)}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>showCropGuidelines:</Text>
                        <Button
                            title={`${showCropGuidelinesCamera}`}
                            onPress={() => showCropGuidelinesCamera ? setShowCropGuidelinesCamera(false) : setShowCropGuidelinesCamera(true)}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>showCropFrame:</Text>
                        <Button
                            title={`${showCropFrameCamera}`}
                            onPress={() => showCropFrameCamera ? setShowCropFrameCamera(false) : setShowCropFrameCamera(true)}
                        />
                    </View>

                    <View style={styles.buttonSty}>
                        <Button
                            title="openCamera (打开相机)"
                            onPress={() => {
                                ImagePicker.openCamera({
                                    cropping: croppingCamera,
                                    writeTempFile: writeTempFileCamera,
                                    includeBase64: includeBase64Camera,
                                    includeExif: includeExifCamera,
                                    avoidEmptySpaceAroundImage: avoidEmptySpaceCamera,
                                    freeStyleCropEnabled: freeStyleCropEnabledCamera,
                                    useFrontCamera: useFrontCamera,
                                    forceJpg: forceJpgCamera,
                                    mediaType: mediaTypeCamera,
                                    compressImageQuality: imageQualityCamera,
                                    cropperToolbarTitle: cropperTitleCamera,
                                    cropperChooseText: chooseTextCamera,
                                    cropperChooseColor: chooseColorCamera,
                                    cropperCancelText: cancelTextCamera,
                                    cropperCancelColor: cancelColorCamera,
                                    cropperRotateButtonsHidden: cropperRotateCamera,
                                    showCropGuidelines: showCropGuidelinesCamera,
                                    showCropFrame: showCropFrameCamera,
                                }).then(image => {
                                    console.log(TAG + ' openCamera result ' + JSON.stringify(image))
                                });
                            }}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>请输入需要裁剪的图片地址:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            onChangeText={(value) => setImagePath(value)}
                            value={imagePath}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>writeTempFile:</Text>
                        <Button
                            title={`${writeTempFileCropper}`}
                            onPress={() => writeTempFileCropper ? setTempFileCropper(false) : setTempFileCropper(true)}
                        />

                        <Text style={styles.inputLable}>forceJpg:</Text>
                        <Button
                            title={`${forceJpgCropper}`}
                            onPress={() => forceJpgCropper ? setForceJpgCropper(false) : setForceJpgCropper(true)}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>includeBase64:</Text>
                        <Button
                            title={`${includeBase64Cropper}`}
                            onPress={() => includeBase64Cropper ? setBase64Cropper(false) : setBase64Cropper(true)}
                        />

                        <Text style={styles.inputLable}>includeExif:</Text>
                        <Button
                            title={`${includeExifCropper}`}
                            onPress={() => includeExifCropper ? setIncludeExifCropper(false) : setIncludeExifCropper(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>avoidEmptySpaceAroundImage :</Text>
                        <Button
                            title={`${avoidEmptySpaceCropper}`}
                            onPress={() => avoidEmptySpaceCropper ? setAvoidEmptySpaceCropper(false) : setAvoidEmptySpaceCropper(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>freeStyleCropEnabled :</Text>
                        <Button
                            title={`${freeStyleCropEnabledCropper}`}
                            onPress={() => freeStyleCropEnabledCropper ? setFreeStyleCropEnabledCropper(false) : setFreeStyleCropEnabledCropper(true)}
                        />

                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.inputLable}>compressImageQuality:</Text>
                        <TextInput
                            style={styles.numberInput}
                            onChangeText={setimageQualityCropper}
                            value={imageQualityCropper}
                        />
                        <Text style={styles.lableType}>(0.1 到 1)</Text>
                    </View>


                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperToolbarTitle:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={5}
                            onChangeText={(value) => setCropperTitle(value)}
                            value={cropperTitle}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperChooseText:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={5}
                            onChangeText={(value) => setChooseText(value)}
                            value={chooseText}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperChooseColor:例如 #FF0000</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={7}
                            onChangeText={(value) => setChooseColor(value)}
                            value={chooseColor}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperCancelText:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={5}
                            onChangeText={(value) => setCancelText(value)}
                            value={cancelText}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>cropperCancelColor:例如 #FF0000</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            maxLength={7}
                            onChangeText={(value) => setCancelColor(value)}
                            value={cancelColor}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>cropperRotateButtonsHidden:</Text>
                        <Button
                            title={`${cropperRotate}`}
                            onPress={() => cropperRotate ? setCropperRotate(false) : setCropperRotate(true)}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>showCropGuidelines:</Text>
                        <Button
                            title={`${showCropGuidelines}`}
                            onPress={() => showCropGuidelines ? setShowCropGuidelines(false) : setShowCropGuidelines(true)}
                        />
                    </View>

                    <View style={styles.TextInputBox}>

                        <Text style={styles.inputLable}>showCropFrame:</Text>
                        <Button
                            title={`${showCropFrame}`}
                            onPress={() => showCropFrame ? setShowCropFrame(false) : setShowCropFrame(true)}
                        />
                    </View>



                    <View style={styles.buttonSty}>
                        <Button
                            title='openCropper(打开裁剪)'
                            onPress={() => {

                                ImagePicker.openCropper({
                                    path: imagePath,
                                    width: 300,
                                    height: 400,
                                    writeTempFile: writeTempFileCropper,
                                    includeBase64: includeBase64Cropper,
                                    includeExif: includeExifCropper,
                                    avoidEmptySpaceAroundImage: avoidEmptySpaceCropper,
                                    freeStyleCropEnabled: freeStyleCropEnabledCropper,
                                    compressImageQuality: imageQualityCropper,
                                    forceJpg: forceJpgCropper,
                                    cropperToolbarTitle: cropperTitle,
                                    cropperChooseText: chooseText,
                                    cropperChooseColor: chooseColor,
                                    cropperCancelText: cancelText,
                                    cropperCancelColor: cancelColor,
                                    cropperRotateButtonsHidden: cropperRotate,
                                    showCropGuidelines: showCropGuidelines,
                                    showCropFrame: showCropFrame,
                                }).then((image => {
                                    console.log(TAG + ' openCropper result ' + JSON.stringify(image))
                                }))
                            }}
                        />
                    </View>

                </View>
            </View>

                <Text style={styles.title}>清除文件:</Text>

                <View style={styles.buttonBox}>
                    <View style={styles.buttonSty}>
                        <Button
                            title='clean (清除所有文件)'
                            onPress={() => {
                                ImagePicker.clean({}).then(image => {
                                    console.log(TAG + ' clean result ' + JSON.stringify(image))
                                });
                            }}
                        />
                    </View>

                    <View style={styles.TextInputBox}>
                        <Text style={styles.textLable}>请输入需要清除图片地址:</Text>
                    </View>

                    <View style={styles.TextInputBox}>

                        <TextInput
                            style={styles.textInput}
                            onChangeText={(value) => setClearImagePath(value)}
                            value={clearImagePath}
                        />
                    </View>

                    <View style={styles.buttonSty}>
                        <Button
                            title='cleanSingle (清除单个文件)'
                            onPress={() => {
                                console.log(TAG + " cleanSingle path " + clearImagePath)
                                ImagePicker.cleanSingle('/data/storage/el2/base/haps/entry/temp/rn_image_crop_picker_lib_temp_' + clearImagePath).then(image => {

                                })
                            }}
                        />
                    </View>

                    <View style={styles.emptyView}></View>

                </View>

        </ScrollView>
    );
}
const styles = StyleSheet.create({
    container: {
    },
    TextInputBox: {
        flexDirection: 'row',
        alignItems: 'center',
        margin: 10,
    },
    textLable: {
        width: '100%'
    },
    emptyView: {
        width: 50,
        height: 500
    },
    inputLable: {
        width: 'auto'
    },
    lableType: {
        width: '18%'
    },
    numberInput: {
        width: 50,
        height: 30,
        color: 'black',
        borderColor: 'gray',
        borderWidth: 1
    },
    textInput: {
        width: '50%',
        height: 36,
        color: 'black',
        borderColor: 'gray',
        bordeWidth: 1
    },
    switchType: {
        width: 60,
        height: 36
    },
    buttonBox: {
        marginTop: 20,
    },
    buttonSty: {
        marginTop: 0,
        marginRight: 60,
        marginBottom: 20,
        marginLeft: 60,
        textAlign: 'center'
    },
    title: {
        fontWeight: '500',
        fontSize: 20,
        marginTop: 10,
    }
});
export default ImageCropPickDemo;

2. Manual Link

此步骤为手动配置原生依赖项的指导。

首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony

2.1. Overrides RN SDK

为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 oh-package.json5 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。

关于该字段的作用请阅读官方说明

{
  "overrides": {
    "@rnoh/react-native-openharmony": "^0.72.38" // ohpm 在线版本
    // "@rnoh/react-native-openharmony" : "./react_native_openharmony.har" // 指向本地 har 包的路径
    // "@rnoh/react-native-openharmony" : "./react_native_openharmony" // 指向源码路径
  }
}

2.2. 引入原生端代码

目前有两种方法:

  • 通过 har 包引入(推荐)
  • 直接链接源码。

方法一:通过 har 包引入

[!TIP] har 包位于三方库安装路径的 harmony 文件夹下。

打开 entry/oh-package.json5,添加以下依赖

"dependencies": {
    "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
    "@react-native-ohos/react-native-image-crop-picker": "file:../../node_modules/@react-native-ohos/react-native-image-crop-picker/harmony/image_crop_picker.har"
  }

点击右上角的 sync 按钮

或者在终端执行:

cd entry
ohpm install

方法二:直接链接源码

[!TIP] 如需使用直接链接源码,请参考直接链接源码说明

2.3. 配置 CMakeLists 和引入 ImageCropPickerPackage

[!TIP] 版本 0.40.4 及以上需要

打开 entry/src/main/cpp/CMakeLists.txt,添加:

project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")

add_subdirectory("${RNOH_CPP_DIR}" ./rn)

# RNOH_BEGIN: manual_package_linking_1
add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package)
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-image-crop-picker/src/main/cpp" ./image-crop-picker)
# RNOH_END: manual_package_linking_1

add_library(rnoh_app SHARED
    ${GENERATED_CPP_FILES}
+  ${IMAGE_CROP_PICKER_CPP_FILES}
    "./PackageProvider.cpp"
    "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)

target_link_libraries(rnoh_app PUBLIC rnoh)

# RNOH_BEGIN: manual_package_linking_2
target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
+ target_link_libraries(rnoh_app PUBLIC rnoh_image_crop_picker)
# RNOH_END: manual_package_linking_2

打开 entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
#include "SamplePackage.h"
+ #include "ImageCropPickerPackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
      std::make_shared<SamplePackage>(ctx),
+     std::make_shared<ImageCropPickerPackage>(ctx),
    };
}

2.4. 在 ArkTs 侧引入 ImageCropPickerPackage

打开 entry/src/main/ets/RNPackagesFactory.ts,添加:

  ...
+ import { ImageCropPickerPackage } from '@react-native-ohos/react-native-image-crop-picker/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SamplePackage(ctx),
+   new ImageCropPickerPackage(ctx),
  ];
}

2.5. 配置Entry

(1)在 entry/src/main/ets/entryability 下创建 ImageEditAbility.ets

import UIAbility from '@ohos.app.ability.UIAbility'
import window from '@ohos.window'

const TAG = 'ImageEditAbility';

export default class ImageEditAbility extends UIAbility {

  onWindowStageCreate(windowStage: window.WindowStage) {
    this.setWindowOrientation(windowStage, window.Orientation.PORTRAIT)
    windowStage.loadContent('pages/ImageEdit', (err, data) => {
      if (err.code) {
        console.info(TAG,'Failed to load the content. Cause: %{public}s',
          JSON.stringify(err) ?? '')
        return;
      }
      console.info(TAG,'Succeeded in loading the content')
    });
    try {
      windowStage.getMainWindowSync().setWindowLayoutFullScreen(true, (err)=>{
        if (err.code) {
          console.error('Failed to enable the full-screen mode. Cause: ' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in enabling the full-screen mode.');
      })
    } catch (exception) {
      console.error('Failed to set the system bar to be invisible. Cause: ' + JSON.stringify(exception));
    }
  }

  setWindowOrientation(stage: window.WindowStage, orientation: window.Orientation): void {
    console.info(TAG,"into setWindowOrientation :")
    if (!stage || !orientation) {
      return;
    }
    stage.getMainWindow().then(windowInstance => {
      windowInstance.setPreferredOrientation(orientation);
    })
  }

  onBackground() {
    this.context.terminateSelf();
  }
}

(2)在 entry/src/main/module.json5 注册 ImageEditAbility

"abilities":[{
        "name": "ImageEditAbility",
        "srcEntry": "./ets/entryability/ImageEditAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "removeMissionAfterTerminate": true,

}
...
]

(3)在 entry/src/main/ets/pages 下创建 ImageEdit.ets

import { ImageEditInfo } from '@react-native-ohos/react-native-image-crop-picker';

@Entry
@Component
struct ImageEdit {

 build() {
  Row(){
   Column(){
    ImageEditInfo();
   }
   .width('100%')
  }
  .height('100%')
 }
}

(4)在 entry/src/main/resources/base/profile/main_pages.json 添加配置

{
 "src": [
  "pages/Index",
  "pages/ImageEdit"
 ]
}

2.6. 运行

点击右上角的 sync 按钮

或者在终端执行:

cd entry
ohpm install

然后编译、运行即可。

3. 约束与限制

3.1. 兼容性

要使用此库,需要使用正确的 React-Native 和 RNOH 版本。另外,还需要使用配套的 DevEco Studio 和 手机 ROM。

请到三方库相应的 Releases 发布地址查看 Release 配套的版本信息:@react-native-ohos/react-native-image-crop-picker Releases

4. API

[!TIP] "Platform"列表示该属性在原三方库上支持的平台。

[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

Name Description Type Required Platform HarmonyOS Support
openPicker Call single image picker with cropping function no iOS/Android yes
clean Module is creating tmp images which are going to be cleaned up automatically somewhere in the future. If you want to force cleanup, you can use clean to clean all tmp files, or cleanSingle(path) to clean single tmp file. function no iOS/Android yes
openCropper Crop image and rotate, function no iOS/Android yes
cleanSingle Delete a single cache file function no iOS/Android yes
openCamera Select from camera function no iOS/Android yes

5. 属性

[!TIP] "Platform"列表示该属性在原三方库上支持的平台。

[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

cropData

Name Type Description Required Platform HarmonyOS Support
cropping bool (default false) Enable or disable cropping no All yes
width number Width of result image when used with cropping option no All yes
height number Height of result image when used with cropping option no All yes
multiple bool (default false) Enable or disable multiple image selection no All yes
writeTempFile (iOS only) bool (default true) When set to false, does not write temporary files for the selected images. This is useful to improve performance when you are retrieving file contents with the includeBase64 option and don't need to read files from disk. no iOS yes
includeBase64 bool (default false) When set to true, the image file content will be available as a base64-encoded string in the data property. Hint: To use this string as an image source, use it like: <Image source={{uri: data:image.mime;base64,𝑖𝑚𝑎𝑔𝑒.𝑚𝑖𝑚𝑒;𝑏𝑎𝑠𝑒64,{image.data}}} /> no All yes
includeExif bool (default false) Include image exif data in the response no All yes
avoidEmptySpaceAroundImage (iOS only) bool (default true) When set to true, the image will always fill the mask space. no iOS no
cropperActiveWidgetColor (Android only) string (default "#424242") When cropping image, determines ActiveWidget color. no Android no
cropperStatusBarColor (Android only) string (default #424242) When cropping image, determines the color of StatusBar. no Android no
cropperToolbarColor (Android only) string (default #424242) When cropping image, determines the color of Toolbar. no Android no
cropperToolbarWidgetColor (Android only) string (default darker orange) When cropping image, determines the color of Toolbar text and buttons. no Android no
freeStyleCropEnabled bool (default false) Enables user to apply custom rectangle area for cropping no All yes
cropperToolbarTitle string (default Edit Photo) When cropping image, determines the title of Toolbar. no All yes
cropperCircleOverlay bool (default false) Enable or disable circular cropping mask. no All no
disableCropperColorSetters (Android only) bool (default false) When cropping image, disables the color setters for cropping library. no Android no
minFiles (iOS only) number (default 1) Min number of files to select when using multiple option no iOS no
maxFiles (iOS only) number (default 5) Max number of files to select when using multiple option no iOS yes
waitAnimationEnd (iOS only) bool (default true) Promise will resolve/reject once ViewController completion block is called no iOS no
smartAlbums (iOS only) array (supported values) (default ['UserLibrary', 'PhotoStream', 'Panoramas', 'Videos', 'Bursts']) List of smart albums to choose from no iOS no
useFrontCamera bool (default false) Whether to default to the front/'selfie' camera when opened. Please note that not all Android devices handle this parameter, see issue #1058 no All yes
compressVideoPreset (iOS only) string (default MediumQuality) Choose which preset will be used for video compression no iOS no
compressImageMaxWidth number (default none) Compress image with maximum width no All no
compressImageMaxHeight number (default none) Compress image with maximum height no All no
compressImageQuality number (default 1 (Android)/0.8 (iOS)) Compress image with quality (from 0 to 1, where 1 is best quality). On iOS, values larger than 0.8 don't produce a noticeable quality increase in most images, while a value of 0.8 will reduce the file size by about half or less compared to a value of 1. no All yes
loadingLabelText (iOS only) string (default "Processing assets...") Text displayed while photo is loading in picker no iOS no
mediaType string (default any) Accepted mediaType for image selection, can be one of: 'photo', 'video', or 'any' no All yes
showsSelectedCount (iOS only) bool (default true) Whether to show the number of selected assets no iOS no
sortOrder (iOS only) string (default 'none', supported values: 'asc', 'desc', 'none') Applies a sort order on the creation date on how media is displayed within the albums/detail photo views when opening the image picker no iOS no
forceJpg (iOS only) bool (default false) Whether to convert photos to JPG. This will also convert any Live Photo into its JPG representation no iOS yes
showCropGuidelines (Android only) bool (default true) Whether to show the 3x3 grid on top of the image during cropping no Android yes
showCropFrame (Android only) bool (default true) Whether to show crop frame during cropping no Android yes
hideBottomControls (Android only) bool (default false) Whether to display bottom controls no Android no
enableRotationGesture (Android only) bool (default false) Whether to enable rotating the image by hand gesture no Android yes
cropperChooseText (iOS only) string (default choose) Choose button text no iOS yes
cropperChooseColor (iOS only) string (default #FFCC00) HEX format color for the Choose button. Default color is controlled by TOCropViewController. no iOS yes
cropperCancelText (iOS only) string (default Cancel) Cancel button text no iOS yes
cropperCancelColor (iOS only) string (default tint iOS color ) HEX format color for the Cancel button. Default value is the default tint iOS color controlled by TOCropViewController no iOS yes
cropperRotateButtonsHidden (iOS only) bool (default false) Enable or disable cropper rotate buttons no iOS yes

6. 遗留问题

  • react-native-image-crop-picker 图像将始终填充蒙版空间 #4
  • Android Demo中 ActiveWidget 改变颜色 #5
  • Android Demo中 改变状态栏颜色 #6
  • Android Demo中 改变工具栏颜色 #7
  • 裁剪图像时,禁用裁剪库的颜色设置器 #8
  • 裁剪图像时,确定工具栏文本和按钮的颜色 #9
  • 调用ViewController“completion”块,Promise将解析/拒绝, HarmonyOS 不支持 #10
  • iOS支持智能相册列表 #11
  • iOS视频压缩的预设 #12
  • iOS智能相册排序 #13
  • Android Demo 设置是否显示底部控件 #14
  • 使用multiple选项时无法设置最小文件数 #39
  • 使用multiple选项时无法设置是否显示选中的资产数量 #40
  • photoAccessHelper选取完成之后没有loading过渡动画效果 #45
  • @ohos.multimedia.image无法进行圆形效果裁切 #46
  • @ohos.multimedia.image中PackingOption无法设置宽高属性 #47

7. 其他

8. 开源协议

本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。