Kamera — expo-camera, QR kod skaner

~15 daq · React Native · Qurilma imkoniyatlari

Muammodan boshlaylik

Do'kon ilovasiga ikki imkoniyat kerak:

  1. QR / shtrix-kod skaneri — mahsulot ustidagi kodni skanerlab, uni katalogda topish
  2. Rasm olish — mijoz qaytariladigan mahsulotning rasmini yuboradi

Veb ilovada kamera bilan ishlash cheklangan va noqulay. Mobil ilovada esa — bu tabiiy imkoniyat.


O'rnatish

npx expo install expo-camera

app.json — ruxsat matni (38-dars):

"plugins": [
  ["expo-camera", { "cameraPermission": "Mahsulot QR kodini skanerlash va rasm olish uchun." }]
]

Oddiy kamera

import { CameraView, useCameraPermissions, CameraType } from 'expo-camera';
import { useRef, useState } from 'react';
import { View, Pressable, Image, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';

export default function Kamera() {
  const [ruxsat, soraRuxsat] = useCameraPermissions();
  const [tomon, setTomon] = useState<CameraType>('back');
  const [rasm, setRasm] = useState<string | null>(null);
  const ref = useRef<CameraView>(null);

  if (!ruxsat) return null;
  if (!ruxsat.granted) return <RuxsatEkran soraRuxsat={soraRuxsat} canAskAgain={ruxsat.canAskAgain} />;

  async function suratOl() {
    const foto = await ref.current?.takePictureAsync({ quality: 0.7 });
    if (foto) setRasm(foto.uri);
  }

  if (rasm) {
    return (
      <View style={{ flex: 1 }}>
        <Image source={{ uri: rasm }} style={{ flex: 1 }} />
        <View style={styles.pastki}>
          <Tugma matn="Qayta olish" turi="ikkinchi" onPress={() => setRasm(null)} />
          <Tugma matn="Yuborish" onPress={() => yuklash(rasm)} />
        </View>
      </View>
    );
  }

  return (
    <View style={{ flex: 1 }}>
      <CameraView ref={ref} style={StyleSheet.absoluteFill} facing={tomon} />
      <View style={styles.boshqaruv}>
        <Pressable onPress={() => setTomon((t) => (t === 'back' ? 'front' : 'back'))} hitSlop={12}>
          <Ionicons name="camera-reverse-outline" size={32} color="white" />
        </Pressable>
        <Pressable onPress={suratOl} style={styles.tushirgich} accessibilityLabel="Surat olish" />
        <View style={{ width: 32 }} />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  boshqaruv: { position: 'absolute', bottom: 48, left: 0, right: 0, flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' },
  tushirgich: { width: 72, height: 72, borderRadius: 36, backgroundColor: 'white', borderWidth: 5, borderColor: 'rgba(255,255,255,0.5)' },
  pastki: { flexDirection: 'row', gap: 12, padding: 16 },
});
  • CameraView — kamera ko'rinishi; boshqaruv tugmalari ustiga position: 'absolute' bilan
  • facing — back / front
  • takePictureAsync({ quality }) — rasm faylining vaqtinchalik uri sini qaytaradi
  • quality: 0.7 — sifat va hajm muvozanati; 1 — juda katta fayl

[!INFO] Kamera emulyatorda cheklangan ishlaydi (Android emulyatori virtual sahna ko'rsatadi, iOS simulatorida kamera umuman yo'q). Haqiqiy telefonda sinang.


QR va shtrix-kod skaneri

import { CameraView, useCameraPermissions, BarcodeScanningResult } from 'expo-camera';
import { useRef, useState } from 'react';
import { router } from 'expo-router';
import * as Haptics from 'expo-haptics';

export default function Skaner() {
  const [ruxsat, soraRuxsat] = useCameraPermissions();
  const qulf = useRef(false);
  const [chiroq, setChiroq] = useState(false);

  if (!ruxsat?.granted) return <RuxsatEkran ruxsat={ruxsat} soraRuxsat={soraRuxsat} />;

  function skanerlandi({ data, type }: BarcodeScanningResult) {
    if (qulf.current) return;           // ⚠️ bir xil kod soniyasiga o'nlab marta keladi
    qulf.current = true;
    Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);

    if (data.startsWith('https://dokon.uz/m/')) {
      const id = data.split('/').pop();
      router.replace({ pathname: '/mahsulot/[id]', params: { id: id! } });
    } else {
      Alert.alert('Kod', `${type}: ${data}`, [{ text: 'OK', onPress: () => { qulf.current = false; } }]);
    }
  }

  return (
    <View style={{ flex: 1, backgroundColor: 'black' }}>
      <CameraView
        style={StyleSheet.absoluteFill}
        facing="back"
        enableTorch={chiroq}
        barcodeScannerSettings={{ barcodeTypes: ['qr', 'ean13', 'ean8', 'code128'] }}
        onBarcodeScanned={skanerlandi}
      />
      {/* Ramka */}
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <View style={{ width: 250, height: 250, borderWidth: 3, borderColor: 'white', borderRadius: 20 }} />
        <Text style={{ color: 'white', marginTop: 16 }}>Kodni ramka ichiga joylang</Text>
      </View>
      <Pressable onPress={() => setChiroq(!chiroq)} style={{ position: 'absolute', bottom: 60, alignSelf: 'center' }}>
        <Ionicons name={chiroq ? 'flash' : 'flash-outline'} size={32} color="white" />
      </Pressable>
    </View>
  );
}

Muhim jihatlar:

  • qulf — onBarcodeScanned kamera kodni ko'rib turgan har kadrda chaqiriladi. Busiz — o'nlab ekran ochiladi. useRef — holat emas, chunki qayta chizish kerak emas va darhol o'zgarishi kerak
  • barcodeTypes — faqat kerakli turlar: tezroq va xatosiz. ean13 — do'kondagi mahsulotlar shtrix-kodi
  • Tebranish (expo-haptics, 43-dars) — "skanerlandi" ni his qilish
  • Chiroq — qorong'i omborda
  • QR dagi ma'lumotga ishonmang — u istalgan matn yoki zararli havola bo'lishi mumkin. Faqat kutilgan formatni qabul qiling

Ekranga qaytganda

Skanerdan mahsulotga push qilib qaytsangiz — qulf hali true. useFocusEffect bilan tiklang (26-dars):

useFocusEffect(useCallback(() => { qulf.current = false; }, []));

Kamera resurslari

Kamera — ko'p batareya va xotira sarflaydi. Tab ichida kamera ekrani bo'lsa va foydalanuvchi boshqa tabga o'tsa — kamera ishlab qolmasligi kerak:

import { useIsFocused } from '@react-navigation/native';

const fokusda = useIsFocused();
return fokusda ? <CameraView ... /> : null;

Hujjat skaneri va boshqa imkoniyatlar

  • Video: CameraView mode="video" + recordAsync(), mikrofon ruxsati bilan
  • Murakkab ishlov (yuz aniqlash, real vaqt filtrlar, matnni tanish) — react-native-vision-camera kutubxonasi; development build talab qiladi

Nima uchun shunday?

Nima uchun kod skanerlash uchun maxsus API — kameradan rasm olib, keyin uni tahlil qilish mumkin emasmi?

Mumkin, lekin sekin va noqulay bo'lardi. onBarcodeScanned kodni native darajada, har bir kadrda real vaqtda aniqlaydi — iOS ning Vision va Android ning ML Kit kabi tizim kutubxonalari yordamida, apparat tezlatishi bilan. Foydalanuvchi telefonni kodga yaqinlashtiradi — va bir zumda natija. Rasm olib, JavaScript da tahlil qilish esa har bir urinish uchun soniyalar va to'g'ri burchakni topish uchun bir necha urinish degani.


Xulosa

Eslab qoling:

  • expo-camera: CameraView, useCameraPermissions
  • facing, takePictureAsync({ quality }), enableTorch
  • Skaner: barcodeScannerSettings.barcodeTypes + onBarcodeScanned
  • useRef qulfi — bitta kod bir marta qayta ishlansin; qaytganda tiklang
  • QR ma'lumotiga ishonmang — formatni tekshiring
  • Fokusda bo'lmaganda kamerani o'chiring (useIsFocused)
  • Haqiqiy telefonda sinang

Keyingi dars

40-dars: Galereya — rasm tanlash va serverga yuklash