Камера и галерея
Работа с изображениями — частая задача в мобильных приложениях.
Expo Image Picker
npx expo install expo-image-picker
Выбор из галереи
import * as ImagePicker from 'expo-image-picker';
async function pickFromGallery() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
});
if (!result.canceled) {
const asset = result.assets[0];
console.log(asset.uri, asset.width, asset.height);
}
}
Съёмка
async function takePhoto() {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status !== 'granted') return;
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ['images'],
allowsEditing: true,
quality: 0.8,
});
if (!result.canceled) {
setPhoto(result.assets[0].uri);
}
}
Несколько изображений
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsMultipleSelection: true,
selectionLimit: 10,
});
Загрузка на сервер
async function uploadImage(uri: string) {
const formData = new FormData();
formData.append('file', {
uri,
name: 'photo.jpg',
type: 'image/jpeg',
} as unknown as Blob);
const res = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData,
headers: {
Authorization: `Bearer ${token}`,
},
});
return res.json();
}
Не устанавливай Content-Type — fetch сам выставит multipart/form-data с boundary.
Кастомная камера
Для полного контроля — expo-camera:
npx expo install expo-camera
import { CameraView, useCameraPermissions } from 'expo-camera';
import { useRef } from 'react';
function Camera() {
const [permission, requestPermission] = useCameraPermissions();
const ref = useRef<CameraView>(null);
if (!permission?.granted) {
return <Button onPress={requestPermission} title="Разрешить камеру" />;
}
async function takePicture() {
const photo = await ref.current?.takePictureAsync();
console.log(photo?.uri);
}
return (
<View style={{ flex: 1 }}>
<CameraView ref={ref} style={{ flex: 1 }} facing="back" />
<Pressable onPress={takePicture} style={styles.button}>
<Text>Снять</Text>
</Pressable>
</View>
);
}
Видео
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ['videos'],
videoMaxDuration: 60,
quality: 0.8,
});
Превью
function Preview({ uri, onRemove }: { uri: string; onRemove: () => void }) {
return (
<View style={styles.preview}>
<Image source={{ uri }} style={styles.image} />
<Pressable onPress={onRemove} style={styles.remove}>
<Ionicons name="close" size={20} color="#fff" />
</Pressable>
</View>
);
}
Сжатие
import * as ImageManipulator from 'expo-image-manipulator';
const { uri } = await ImageManipulator.manipulateAsync(
originalUri,
[{ resize: { width: 1080 } }],
{ compress: 0.8, format: ImageManipulator.SaveFormat.JPEG }
);
Разрешения
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "Камера для съёмки фото",
"NSPhotoLibraryUsageDescription": "Галерея для выбора фото"
}
}
}
}
Итоги
expo-image-pickerдля галереи и камерыexpo-cameraдля кастомного UIFormDataдля загрузки- Не устанавливай
Content-Type expo-image-manipulatorдля сжатия- Разрешения в
app.json