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.

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:

Ezen belül pedig a LOGS fülre:

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