Skip to content
Kezdőlap » Fotózás mobilalkalmazással – takePictureAsync

Fotózás mobilalkalmazással – takePictureAsync

Hogyan lehet képet készíteni a kamerával (takePictureAsync)? Van-e különbség iOS és Android között? A kamera használatával foglalkozó bejegyzésben csak annyit csináltunk, hogy bekapcsoltuk a kamerát, de még nem tudunk vele fényképezni.

kép kamerával
A kép innen van, köszi!

useRef hook

Használni fogjuk a useRef hook-ot, aminek különlegessége, hogy megőrzi egy state értékét a képernyő renderelések között. Micsoda szupererő! 🙂

Először is importáljuk a useRef hook-ot a react csomagból:

import { useRef } from "react";

Létrehozunk egy változót:

const cameraRef = useRef();

Kép készítő metódus – takePictureAsync

A takePictureAsync() felel azért, hogy képet készíthessünk. Ügyes „fotósként” elkészíti a fotót és elmenti az alkalmazás cache könyvtárába. Ez fontos, ugyanis még nem mentődik el a kép közvetlenül a mobilra. Az elkészült kép álló vagy fekvő lesz. Ez megegyezik azzal, hogyan tartottuk a telót a fotózás közben. Most meg ne kérdezze senki, hogy ha ferdén tartja a mobilt akkor milyen lesz a kép :).

Méretezés is történik a háttérben.

Maga a kép készítése aszinkron folyamat, vagyis némi időbe telik, nem a pillanat műve. Emiatt a metódus egy promise-t ad vissza. A képet készítő aszinkron függvény így pedig néz ki:

  const snap = async () => {
    if (cameraRef) {
      // ha létezik a cameraRef, akkor készíthetünk képet
      const photo = await cameraRef.current.takePictureAsync();
      console.log(photo);
    }
  }

Van benne egy feltétel, amiben azt vizsgáljuk, hogy létezik-e a cameraRef változó. Ha igen, akkor ez jelenti azt, hogy elkészíthetjük a képet. A photo változó tárolja az elkészült kép objektumot, amiről ennél a pontnál még nem tudjuk, hogy mi. Ezért, ha kiíratjuk a fotózás után a konzolba, az körülbelül így fest Androidon:

Object {
  "height": 3264,
  "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540laszlovarga%252FMealsToGo/Camera/e5e2974a-c7c8-4000-9ab2-144b7ce8a515.jpg",
  "width": 2448,
}

És így iOS-en:

Object {
  "height": 1064,
  "uri": "file:///var/mobile/Containers/Data/Application/4408C9C0-B11E-4F4D-9496-F154741DE9FC/Library/Caches/ExponentExperienceData/%2540laszlovarga%252FMealsToGo/Camera/E3560221-12D3-416B-9622-EB9C299BEDAC.jpg",
  "width": 720,
}

Tehát kaptunk egy kép objektumot, ami egyelőre csupán az eszköz átmenti tárterületén (cache) jelent meg, mint objektum, aminek van height, width és uri tulajdonsága.

Tovább boncolgatva a kép készítő kis függvényünket, a cameraRef.current.takePictureAsync() sorban a current az a kamera aktuális értéke, ami pedig együttműködik a <Camera> komponensnek a fotózáshoz használt ref tulajdonságával:

    <Camera
      <strong>ref={(camera) => (cameraRef.current = camera)}</strong>
      type={Camera.Constants.Type.front}
    >
    </Camera>

Emlékeztető: a Camera komponenst használjuk a kamera bekapcsolásához, ahogyan az itt is olvasható. És ennek a ref tulajdonságában.

„Gomb” a kép elkészítéséhez

Vagyis kell nekünk valami, amivel meg tudjuk hívni a kép készítő függvényt. Ez célszerűen egy gomb, vagy talán inkább az teljes képernyő felületnek kellene lennie az „elsütő”-nek. Magyarul, ha rábökünk a mobil kijelzőjére, miközben a kamera be van kapcsolva, akkor kellene a képet készíteni.

Ehhez használjuk a TouchableOpacity komponenst:

import { TouchableOpacity } from "react-native";

És, ha azt akarjuk, hogy a teljes képernyő érinthető legyen, akkor a <TouchableOpacity>-t a <Camera> komponens köré kell rakni:

<TouchableOpacity>    
    <Camera
      <strong>ref={(camera) => (cameraRef.current = camera)}</strong>
      type={Camera.Constants.Type.front}
    >
    </Camera>
</TouchableOpacity>

iOS-en kipróbróbálva már jönnie kell az exponáló hangnak, és a konzolban is megjelenik a kép objektum. Androidon azonban valamiért nem akar működni. Nem csinálja meg a képet…

Kép készítése Android eszközön

Ahhoz, hogy Androidon is menjen a dolog, egy kicsit át kell a kódot alakítani. Valószínűleg az a baj, hogy az érinthető felület, amire rábökve megcsinálná a képet, az fizikailag alsóbb rétegen van, mint maga a teljes képernyős <Camera> komponens, vagyis kitakarja azt. Így hiába nyomogatjuk a képernyőt, valójában a Camera komponens felületét érjük el és nem a TouchableOpacity felületét.

Ezért a TouchableOpacity-t mégiscsak a Camera komponensen belül helyezzük el. Ezen belül pedig csinálunk egy 100% széles és 100% magas konténert egy elegendően magas z-index értékkel, ami mindenképpen előtérbe helyezi az érinthető felületet, amivel képet lehet csinálni.

A belső konténer, ha mondjuk styled-component csomagot használunk a stílusokhoz::

const InnerSnap = styled.View`
  width: 100%;
  height: 100%;
  z-index: 999;
`;

És a Camera komponens, a TouchableOpacity, valamint az előzőleg legyártott belső konténer így fog rétegződni:

    <Camera
      ref={(camera) => (cameraRef.current = camera)}
      type={Camera.Constants.Type.front}
    >
      <TouchableOpacity onPress={snap}>
        <strong><InnerSnap /></strong>
      </TouchableOpacity>
    </Camera>

Működő példa

Csináltam egy Expo Snack-et a kipróbáláshoz. A konzol úgy hívható elő, hogy az oldal alján levő szürke sávra kell kattintani:

expo snack console

Ezen belül pedig a LOGS fülre:

expo snack console log

Ha nem működik, vagy bármilyen hibát tapasztalsz, akkor dobj egy üzenetet a poszt alatt.