الدرس 18 · الوحدة 8: التفاعل مع الحساسات والأجهزة الذكية باستخدام APIs
الكاميرا: التقاط صورة وعرضها
التقاط الصور وعرضها باحترام الخصوصيّة. ⏱ نحو 40 دقيقة.
بعد هذا الدرس ستستطيع أن:
- تلتقط إطارًا من الكاميرا وتعرضه.
- تجمع اللقطات في معرضٍ بالتحديث الدالّيّ.
- تراعي الإذن والخصوصيّة والطاقة.
١الكاميرا في التطبيقات
الكاميرا من أقوى المستشعرات: تصوير، مسح رموز QR، التعرّف على المستندات. تدفّقها «إطاراتٌ» متتالية، ونلتقط منها لقطة عند الطلب.
٢التقاط وعرض
نشترك في الكاميرا؛ كل «التقاط» يعطي إطارًا نعرضه. نحفظ آخر لقطةٍ في حالة:
import { useState, useEffect } from 'react';import { View, Text, StyleSheet } from 'react-native';import SmartDevice from 'smartdevice'; export default function App() { const [shot, setShot] = useState(null); useEffect(() => { const stop = SmartDevice.on('camera', (c) => { if (c.active) setShot(c.frame); // التقطت لقطة }); return stop; }, []); return ( <View style={styles.box}> <View style={styles.viewfinder}> <Text style={styles.frame}>{shot || 'اضغط «التقط»'}</Text> </View> <Text style={styles.cap}>{shot ? 'آخر لقطة: ' + shot : 'لا توجد صورة بعد'}</Text> </View> );} const styles = StyleSheet.create({ box: { padding: 16, alignItems: 'center' }, viewfinder: { width: 220, height: 150, backgroundColor: '#111', borderRadius: 14, alignItems: 'center', justifyContent: 'center' }, frame: { color: '#fff', fontSize: 30 }, cap: { marginTop: 12, color: '#555' },});
كل ضغطةٍ على «التقط» تبدّل الإطار. في الواقع يكون الإطار صورةً؛ هنا رمزٌ تمثيليّ.
٣معرض اللقطات
نجمع اللقطات في قائمةٍ (ألبوم). كل التقاطٍ يُضاف إلى المصفوفة:
import { useState, useEffect } from 'react';import { View, Text, FlatList, StyleSheet } from 'react-native';import SmartDevice from 'smartdevice'; export default function App() { const [album, setAlbum] = useState([]); useEffect(() => { return SmartDevice.on('camera', (c) => { if (c.active) setAlbum((prev) => [{ id: String(c.ts), frame: c.frame }, ...prev]); }); }, []); return ( <View style={{ padding: 12 }}> <Text style={styles.h}>الألبوم ({album.length})</Text> <FlatList horizontal data={album} keyExtractor={(it) => it.id} renderItem={({ item }) => <Text style={styles.thumb}>{item.frame}</Text>} ListEmptyComponent={<Text style={styles.empty}>التقط صورًا لتظهر هنا</Text>} /> </View> );} const styles = StyleSheet.create({ h: { fontWeight: 'bold', fontSize: 16, marginBottom: 8 }, thumb: { fontSize: 34, padding: 8, backgroundColor: '#FFF3E0', borderRadius: 10, marginEnd: 8 }, empty: { color: '#888' },});
استخدمنا شكل التحديث الدالّيّ setAlbum(prev => …) لأن الجديد يعتمد على السابق.
توقّع وتحقّق
الكاميرا تُستخدم في:
قبل استخدام الكاميرا:
لإضافة عنصرٍ يعتمد على السابق نستخدم:
تدفّق الكاميرا عبارة عن:
الخلاصة
- الكاميرا تدفّق إطارات؛ نلتقط لقطةً عند الطلب.
- نجمع اللقطات بـ
setState(prev => [جديد, ...prev]). - اطلب الإذن، وأوقف الكاميرا بعد الاستخدام.
تحدٍّ برمجي: زرّ التقاط
متقدماعرض آخر لقطةٍ من الكاميرا.
المطلوب:
- حالة
shotتبدأ فارغة. - اشترك في «camera» بـ
SmartDevice.onواحفظc.frameعندc.active. <Text testID="shot">يعرض اللقطة أو «—».
👀 اعرض الناتج المطلوب (وجرّبه)
✓ التحقق الآلي من حلّك:
- الكود يعمل بلا أخطاء في Console
- يبدأ فارغًا «—»
- اشترك في camera
- حفظ الإطار frame
💡 تلميح 1
اشترك:
SmartDevice.on('camera', c => …).💡 تلميح 2
قبل أيّ التقاطٍ تكون اللقطة فارغة فيظهر «—».
حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import { useState, useEffect } from 'react';import { View, Text, StyleSheet } from 'react-native';import SmartDevice from 'smartdevice'; export default function App() { const [shot, setShot] = useState(null); useEffect(() => { return SmartDevice.on('camera', (c) => { if (c.active) setShot(c.frame); }); }, []); return ( <View style={{ padding: 20 }}> <Text testID="shot" style={styles.s}>{shot || '—'}</Text> </View> );} const styles = StyleSheet.create({ s: { fontSize: 30 } });