الدرس 19 · الوحدة 9: تكامل التفاعل مع المستشعرات في تطبيقات الأجهزة الذكية
تطبيق «الميزان»: يدمج الإمالة
أول تطبيقٍ متكامل يدمج مستشعر الإمالة. ⏱ نحو 40 دقيقة.
بعد هذا الدرس ستستطيع أن:
- تدمج قراءة مستشعرٍ في تطبيقٍ كامل.
- تحرّك عنصرًا بالإمالة ضمن حدود.
- تفصل منطق القراءة عن العرض.
١من مستشعرٍ إلى تطبيق
تعلّمنا قراءة المستشعرات مفردة. الآن ندمجها في تطبيقٍ كامل. نبدأ بأداة «الميزان» (Level): تستخدم الإمالة لتخبرك هل السطح مستوٍ، بفقاعةٍ تتحرّك كالميزان الحقيقيّ.
٢الفقاعة تتحرّك
نضع فقاعةً داخل دائرة؛ موضعها يتبع الإمالة. حين تقترب من المركز تتحوّل للأخضر. حرّك المنزلقين:
import { View, Text, StyleSheet } from 'react-native';import { useTilt } from 'smartdevice'; export default function App() { const { x, y } = useTilt(); const level = Math.abs(x) <= 3 && Math.abs(y) <= 3; const bx = Math.max(-70, Math.min(70, x * 2)); const by = Math.max(-70, Math.min(70, y * 2)); return ( <View style={styles.wrap}> <View style={styles.dial}> <View style={styles.center} /> <View style={[styles.bubble, { backgroundColor: level ? '#2E7D32' : '#E8820C', transform: [{ translateX: bx }, { translateY: by }] }]} /> </View> <Text testID="status" style={[styles.status, { color: level ? '#2E7D32' : '#E8820C' }]}> {level ? '✅ مستوٍ' : '↕ مائل'} </Text> <Text style={styles.read}>x={x}° y={y}°</Text> </View> );} const styles = StyleSheet.create({ wrap: { padding: 16, alignItems: 'center' }, dial: { width: 180, height: 180, borderRadius: 90, borderWidth: 2, borderColor: '#ccc', alignItems: 'center', justifyContent: 'center', backgroundColor: '#FAFAFA' }, center: { position: 'absolute', width: 60, height: 60, borderRadius: 30, borderWidth: 1, borderColor: '#ddd' }, bubble: { width: 36, height: 36, borderRadius: 18 }, status: { fontSize: 22, fontWeight: 'bold', marginTop: 14 }, read: { color: '#777', marginTop: 4 },});
الفقاعة = عنصرٌ متحرّك بـ transform؛ القيود Math.max/min تبقيها داخل الدائرة.
٣إضافة تثبيت القراءة
تحسينٌ عمليّ: زرُّ «تثبيت» يحفظ آخر قراءةٍ ليقرأها المستخدم بعد رفع الجهاز:
import { useState } from 'react';import { View, Text, Pressable, StyleSheet } from 'react-native';import { useTilt } from 'smartdevice'; export default function App() { const { x, y } = useTilt(); const [held, setHeld] = useState(null); const shown = held || { x, y }; return ( <View style={styles.box}> <Text style={styles.r}>x={shown.x}° y={shown.y}°</Text> <View style={styles.row}> <Pressable style={styles.btn} onPress={() => setHeld({ x, y })}> <Text style={styles.bt}>تثبيت</Text> </Pressable> <Pressable style={styles.btn} onPress={() => setHeld(null)}> <Text style={styles.bt}>مباشر</Text> </Pressable> </View> </View> );} const styles = StyleSheet.create({ box: { padding: 20, alignItems: 'center' }, r: { fontSize: 22, fontWeight: 'bold', color: '#E8820C' }, row: { flexDirection: 'row', gap: 10, marginTop: 14 }, btn: { backgroundColor: '#E8820C', paddingVertical: 8, paddingHorizontal: 16, borderRadius: 8 }, bt: { color: 'white' },});
«تثبيت» يلتقط اللحظة؛ «مباشر» يعود للتتبّع الحيّ.
توقّع وتحقّق
أداة الميزان تعتمد على مستشعر:
لتقييد حركة الفقاعة داخل حدّ:
أفضل بنية للتطبيق:
«تثبيت القراءة» يعني:
الخلاصة
- «الميزان» يدمج الإمالة: فقاعةٌ تتحرّك وحالة استواء.
Math.max/minتقيّد الحركة داخل الحدود.- افصل القراءة عن العرض لبنيةٍ نظيفة.
تحدٍّ برمجي: حالة الاستواء
متوسطاعرض حالة الاستواء من الإمالة.
المطلوب:
- استورد
useTilt. - احسب
level: صحيحٌ إذا كان |x|≤3 و|y|≤3. <Text testID="status">: «مستوٍ» أو «مائل».
👀 اعرض الناتج المطلوب (وجرّبه)
✓ التحقق الآلي من حلّك:
- الكود يعمل بلا أخطاء في Console
- يبدأ «مستوٍ» (الإمالة 0)
- استُخدم Math.abs
- استُخدم useTilt
💡 تلميح 1
الإمالة الابتدائيّة 0 فالحالة «مستوٍ».
💡 تلميح 2
الشرط:
Math.abs(x) <= 3 && Math.abs(y) <= 3.حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import { View, Text } from 'react-native';import { useTilt } from 'smartdevice'; export default function App() { const { x, y } = useTilt(); const level = Math.abs(x) <= 3 && Math.abs(y) <= 3; return ( <View style={{ padding: 24, alignItems: 'center' }}> <Text testID="status" style={{ fontSize: 24, fontWeight: 'bold', color: level ? '#2E7D32' : '#E8820C' }}> {level ? 'مستوٍ' : 'مائل'} </Text> </View> );}