אפשר להשתמש ב-Generic Sensor API כדי לקבל גישה לחיישנים במכשיר כמו מד תאוצה, ג'ירוסקופ ומגנטומטר.
כיום, נתוני חיישנים משמשים באפליקציות רבות שספציפיות לפלטפורמה, כדי לאפשר תרחישי שימוש כמו משחקים סוחפים, מעקב אחר כושר גופני ומציאות רבודה או מדומה. האם לא יהיה נהדר לגשר על הפער בין אפליקציות ספציפיות לפלטפורמה לבין אפליקציות אינטרנט? מזינים את Generic Sensor API, לאינטרנט!
מה זה Generic Sensor API?
Generic Sensor API הוא קבוצה של ממשקים שחושפים מכשירי חיישנים לפלטפורמת האינטרנט. ממשק ה-API מורכב מממשק הבסיס Sensor ומקבוצה של מחלקות חיישנים קונקרטיות שנבנו על גביו. ממשק בסיסי מפשט את תהליך ההטמעה וההגדרה של מחלקות החיישנים הקונקרטיות. לדוגמה, אפשר לעיין במחלקה Gyroscope. הוא קטן מאוד! הפונקציונליות הבסיסית מוגדרת על ידי ממשק הבסיס, ו-Gyroscope רק מרחיב אותה עם שלושה מאפיינים שמייצגים מהירות זוויתית.
חלק ממחלקות החיישנים מתקשרות עם חיישני חומרה בפועל, כמו למשל מחלקות מד התאוצה או הג'ירוסקופ. החיישנים האלה נקראים חיישנים ברמה נמוכה. חיישנים אחרים, שנקראים חיישני מיזוג, ממזגים נתונים מכמה חיישנים ברמה נמוכה כדי לחשוף מידע שסקריפט יצטרך לחשב בדרך אחרת. לדוגמה, חיישן AbsoluteOrientation מספק מטריצת סיבוב של 4x4 שמוכנה לשימוש על סמך הנתונים שהתקבלו ממד התאוצה, מהג'ירוסקופ ומהמגנטומטר.
יכול להיות שאתם חושבים שפלטפורמת האינטרנט כבר מספקת נתוני חיישנים, ואתם צודקים לגמרי! לדוגמה, האירועים DeviceMotion ו-DeviceOrientation חושפים נתונים של חיישני תנועה. אז למה אנחנו צריכים API חדש?
בהשוואה לממשקי המשתמש הקיימים, Generic Sensor API מספק מספר רב של יתרונות:
- Generic Sensor API הוא מסגרת חיישנים שאפשר להרחיב בקלות באמצעות מחלקות חיישנים חדשות, וכל אחת מהמחלקות האלה תשמור על הממשק הגנרי. אפשר להשתמש מחדש בקוד הלקוח שנכתב עבור סוג חיישן אחד בשביל סוג אחר, עם שינויים מינוריים בלבד.
- אפשר להגדיר את החיישן. לדוגמה, אפשר להגדיר את תדירות הדגימה שמתאימה לצרכים של האפליקציה.
- אפשר לזהות אם חיישן זמין בפלטפורמה.
- לנתוני החיישן יש חותמות זמן ברמת דיוק גבוהה, שמאפשרות סנכרון טוב יותר עם פעילויות אחרות באפליקציה.
- מודלים של נתוני חיישנים ומערכות קואורדינטות מוגדרים בצורה ברורה, וכך ספקי דפדפנים יכולים להטמיע פתרונות שניתנים להפעלה הדדית.
- ממשקי Generic Sensor לא קשורים ל-DOM (כלומר הם לא אובייקטים מסוג
navigatorולא מסוגwindow), וזה מאפשר להשתמש ב-API בעתיד ב-service workers או להטמיע אותו בסביבות זמן ריצה של JavaScript ללא ממשק משתמש, כמו מכשירים מוטמעים. - היבטים של אבטחה ופרטיות הם בראש סדר העדיפויות של Generic Sensor API, והוא מספק אבטחה טובה בהרבה בהשוואה לממשקי API ישנים יותר של חיישנים. יש שילוב עם Permissions API.
- סנכרון אוטומטי עם קואורדינטות המסך זמין ב-
Accelerometer, Gyroscope, LinearAccelerationSensor,AbsoluteOrientationSensor, RelativeOrientationSensorו-Magnetometer.
ממשקי API גנריים של חיישנים שזמינים
בזמן כתיבת המאמר הזה, יש כמה חיישנים שאפשר להתנסות איתם.
חיישני תנועה:
AccelerometerGyroscopeLinearAccelerationSensorAbsoluteOrientationSensorRelativeOrientationSensorGravitySensor
חיישנים סביבתיים:
-
AmbientLightSensor(מאחורי התכונה הניסיונית#enable-generic-sensor-extra-classesב-Chromium). -
Magnetometer(מאחורי התכונה הניסיונית#enable-generic-sensor-extra-classesב-Chromium).
זיהוי תכונות
זיהוי תכונות של ממשקי API של חומרה הוא מסובך, כי צריך לזהות גם אם הדפדפן תומך בממשק הרלוונטי, וגם אם במכשיר יש את החיישן המתאים. קל לבדוק אם הדפדפן תומך בממשק. (צריך להחליף את Accelerometer בכל אחד מהממשקים האחרים שצוינו למעלה).
if ('Accelerometer' in window) {
// The `Accelerometer` interface is supported by the browser.
// Does the device have an accelerometer, though?
}
כדי לקבל תוצאה משמעותית של זיהוי תכונות, צריך לנסות להתחבר גם לחיישן. הדוגמה הזו ממחישה איך עושים את זה.
let accelerometer = null;
try {
accelerometer = new Accelerometer({ frequency: 10 });
accelerometer.onerror = (event) => {
// Handle runtime errors.
if (event.error.name === 'NotAllowedError') {
console.log('Permission to access sensor was denied.');
} else if (event.error.name === 'NotReadableError') {
console.log('Cannot connect to the sensor.');
}
};
accelerometer.onreading = (e) => {
console.log(e);
};
accelerometer.start();
} catch (error) {
// Handle construction errors.
if (error.name === 'SecurityError') {
console.log('Sensor construction was blocked by the Permissions Policy.');
} else if (error.name === 'ReferenceError') {
console.log('Sensor is not supported by the User Agent.');
} else {
throw error;
}
}
פוליפיל
לדפדפנים שלא תומכים ב-Generic Sensor API, יש polyfill. ה-polyfill מאפשר לכם לטעון רק את ההטמעות הרלוונטיות של החיישנים.
// Import the objects you need.
import { Gyroscope, AbsoluteOrientationSensor } from './src/motion-sensors.js';
// And they're ready for use!
const gyroscope = new Gyroscope({ frequency: 15 });
const orientation = new AbsoluteOrientationSensor({ frequency: 60 });
מהם כל החיישנים האלה? איך אפשר להשתמש בהם?
אולי צריך לתת הסבר קצר על חיישנים. אם אתם מכירים את הנושא של חיישנים, אתם יכולים לדלג ישר אל הקטע על תרגול כתיבת קוד. אחרת, נבחן כל חיישן נתמך בפירוט.
מד תאוצה וחיישן תאוצה לינארית
חיישן Accelerometer מודד את התאוצה של מכשיר שמארח את החיישן בשלושה צירים (X, Y ו-Z). החיישן הזה הוא חיישן אינרציאלי, כלומר כשהמכשיר נמצא בנפילה חופשית ליניארית, התאוצה הכוללת שנמדדת תהיה 0 m/s2, וכשהמכשיר מונח שטוח על שולחן, התאוצה בכיוון מעלה (ציר Z) תהיה שווה לכוח הכבידה של כדור הארץ, כלומר g ≈ +9.8 m/s2, כי החיישן מודד את הכוח של השולחן שדוחף את המכשיר כלפי מעלה. אם דוחפים את המכשיר ימינה, התאוצה בציר X תהיה חיובית, או שלילית אם המכשיר מואץ מימין לשמאל.
אפשר להשתמש במדי תאוצה למטרות כמו ספירת צעדים, חישת תנועה או קביעת הכיוון של המכשיר. לעתים קרובות, מדידות של מד התאוצה משולבות עם נתונים ממקורות אחרים כדי ליצור חיישני מיזוג, כמו חיישני כיוון.
החיישן LinearAccelerationSensor מודד את התאוצה שמופעלת על המכשיר שמארח את החיישן, לא כולל את כוח המשיכה. כשהמכשיר במצב מנוחה, למשל כשהוא מונח שטוח על השולחן, החיישן ימדוד תאוצה של ≈ 0 m/s2 בשלושה צירים.
חיישן כוח המשיכה
המשתמשים כבר יכולים להפיק קריאות דומות לאלה של חיישן הכבידה באופן ידני, על ידי בדיקה ידנית של הקריאות של Accelerometer ושל LinearAccelerometer, אבל זה יכול להיות מסורבל ותלוי בדיוק של הערכים שמספקים החיישנים האלה. פלטפורמות כמו Android יכולות לספק קריאות של כוח המשיכה כחלק ממערכת ההפעלה, וזה אמור להיות זול יותר מבחינת חישובים, לספק ערכים מדויקים יותר בהתאם לחומרה של המשתמש, וקל יותר לשימוש מבחינת ארגונומיה של ה-API. הפונקציה
GravitySensor מחזירה את ההשפעה של התאוצה לאורך הצירים X, Y ו-Z של המכשיר כתוצאה מכוח המשיכה.
ג'ירוסקופ
חיישן Gyroscope מודד מהירות זוויתית ברדיאנים לשנייה סביב הצירים המקומיים X, Y ו-Z של המכשיר. לרוב המכשירים לצרכנים יש גירוסקופים מכניים (MEMS), שהם חיישני אינרציה שמודדים את קצב הסיבוב על סמך כוח קוריוליס אינרציאלי. ג'ירוסקופים של MEMS נוטים לסטיות שנגרמות מרגישות הכבידה של החיישן, שמעוותת את המערכת המכנית הפנימית של החיישן. ג'ירוסקופים מבצעים תנודות בתדרים גבוהים יחסית, למשל, עשרות קילוהרץ, ולכן יכול להיות שהוא יצרוך יותר חשמל בהשוואה לחיישנים אחרים.
חיישני כיוון
AbsoluteOrientationSensor הוא חיישן משולב שמודד את הסיבוב של מכשיר ביחס למערכת הקואורדינטות של כדור הארץ, ואילו RelativeOrientationSensor מספק נתונים שמייצגים את הסיבוב של מכשיר שמארח חיישני תנועה ביחס למערכת קואורדינטות קבועה.
כל מסגרות ה-JavaScript המודרניות לתלת-ממד תומכות בקוורטניונים ובמטריצות סיבוב לייצוג סיבוב. עם זאת, אם משתמשים ב-WebGL ישירות, לאובייקט OrientationSensor יש גם את המאפיין quaternion וגם את השיטה populateMatrix().
הנה כמה דוגמאות:
let torusGeometry = new THREE.TorusGeometry(7, 1.6, 4, 3, 6.3);
let material = new THREE.MeshBasicMaterial({ color: 0x0071c5 });
let torus = new THREE.Mesh(torusGeometry, material);
scene.add(torus);
// Update mesh rotation using quaternion.
const sensorAbs = new AbsoluteOrientationSensor();
sensorAbs.onreading = () => torus.quaternion.fromArray(sensorAbs.quaternion);
sensorAbs.start();
// Update mesh rotation using rotation matrix.
const sensorRel = new RelativeOrientationSensor();
let rotationMatrix = new Float32Array(16);
sensor_rel.onreading = () => {
sensorRel.populateMatrix(rotationMatrix);
torus.matrix.fromArray(rotationMatrix);
};
sensorRel.start();
const mesh = new BABYLON.Mesh.CreateCylinder('mesh', 0.9, 0.3, 0.6, 9, 1, scene);
const sensorRel = new RelativeOrientationSensor({ frequency: 30 });
sensorRel.onreading = () => mesh.rotationQuaternion.FromArray(sensorRel.quaternion);
sensorRel.start();
// Initialize sensor and update model matrix when new reading is available.
let modMatrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
const sensorAbs = new AbsoluteOrientationSensor({ frequency: 60 });
sensorAbs.onreading = () => sensorAbs.populateMatrix(modMatrix);
sensorAbs.start();
// Somewhere in rendering code, update vertex shader attribute for the model
gl.uniformMatrix4fv(modMatrixAttr, false, modMatrix);
חיישני אוריינטציה מאפשרים מגוון תרחישי שימוש, כמו משחקים סוחפים, מציאות רבודה ומציאות מדומה.
מידע נוסף על חיישני תנועה, תרחישי שימוש מתקדמים ודרישות זמין במסמך הסבר על חיישני תנועה.
סנכרון עם קואורדינטות המסך
כברירת מחדל, קריאות של חיישנים מרחביים מחושבות במערכת קואורדינטות מקומית שקשורה למכשיר ולא לוקחת בחשבון את כיוון המסך.
עם זאת, בהרבה תרחישי שימוש כמו משחקים או מציאות רבודה ומדומה, נדרש לפתור את קריאות החיישנים במערכת קואורדינטות שקשורה לכיוון המסך.
בעבר, היה צריך להטמיע ב-JavaScript מיפוי מחדש של קריאות חיישנים לקואורדינטות מסך. הגישה הזו לא יעילה, והיא גם מגדילה באופן משמעותי את המורכבות של קוד אפליקציית האינטרנט. אפליקציית האינטרנט צריכה לעקוב אחרי שינויים בכיוון המסך ולבצע טרנספורמציות של קואורדינטות עבור קריאות החיישן, וזה לא דבר פשוט לעשות עבור זוויות אוילר או קווטרניונים.
ה-API של חיישן כללי מספק פתרון פשוט ואמין הרבה יותר. אפשר להגדיר את מערכת הקואורדינטות המקומית לכל המחלקות של חיישני המיקום שהוגדרו: Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensor ו-Magnetometer. המשתמש מגדיר אם הקריאות שיוחזרו יתבססו על קואורדינטות של המכשיר או של המסך, על ידי העברת האפשרות referenceFrame אל בנאי אובייקט החיישן.
// Sensor readings are resolved in the Device coordinate system by default.
// Alternatively, could be RelativeOrientationSensor({referenceFrame: "device"}).
const sensorRelDevice = new RelativeOrientationSensor();
// Sensor readings are resolved in the Screen coordinate system. No manual remapping is required!
const sensorRelScreen = new RelativeOrientationSensor({ referenceFrame: 'screen' });
קדימה, כותבים קוד!
ממשק Generic Sensor API הוא פשוט מאוד וקל לשימוש. לממשק Sensor יש שיטות start() ו-stop() לשליטה במצב החיישן, וכמה handlers של אירועים לקבלת התראות על הפעלת החיישן, שגיאות וקריאות חדשות שזמינות. בדרך כלל, מחלקות החיישנים הקונקרטיות מוסיפות את מאפייני הקריאה הספציפיים שלהן למחלקת הבסיס.
סביבת פיתוח
במהלך הפיתוח תוכלו להשתמש בחיישנים דרך localhost. אם אתם מפתחים לאפליקציות לנייד, אתם צריכים להגדיר העברת פורטים לשרת המקומי שלכם, ואז אתם מוכנים להתחיל!
כשהקוד מוכן, פורסים אותו בשרת שתומך ב-HTTPS. דפי GitHub מוצגים באמצעות HTTPS, ולכן הם מקום מצוין לשתף בו את ההדגמות שלכם.
סיבוב של מודל תלת-ממדי
בדוגמה הפשוטה הזו, אנחנו משתמשים בנתונים מחיישן כיוון מוחלט כדי לשנות את קווטרניון הסיבוב של מודל תלת-ממדי. model הוא מופע של מחלקת Object3D של three.js עם מאפיין quaternion. קטע הקוד הבא מהדגמה של טלפון עם חיישן כיוון ממחיש איך אפשר להשתמש בחיישן הכיוון המוחלט כדי לסובב מודל תלת-ממדי.
function initSensor() {
sensor = new AbsoluteOrientationSensor({ frequency: 60 });
sensor.onreading = () => model.quaternion.fromArray(sensor.quaternion);
sensor.onerror = (event) => {
if (event.error.name == 'NotReadableError') {
console.log('Sensor is not available.');
}
};
sensor.start();
}
הכיוון של המכשיר ישתקף בסיבוב תלת-ממדי של model בסצנת WebGL.
מד ניקוב
קטע הקוד הבא נלקח מהדמו של מד האגרופים, וממחיש איך אפשר להשתמש בחיישן התאוצה הליניארית כדי לחשב את המהירות המקסימלית של מכשיר, בהנחה שהוא נמצא במצב מנוחה בהתחלה.
this.maxSpeed = 0;
this.vx = 0;
this.ax = 0;
this.t = 0;
/* … */
this.accel.onreading = () => {
let dt = (this.accel.timestamp - this.t) * 0.001; // In seconds.
this.vx += ((this.accel.x + this.ax) / 2) * dt;
let speed = Math.abs(this.vx);
if (this.maxSpeed < speed) {
this.maxSpeed = speed;
}
this.t = this.accel.timestamp;
this.ax = this.accel.x;
};
המהירות הנוכחית מחושבת כקירוב לאינטגרל של פונקציית התאוצה.
ניפוי באגים ושינוי מברירת המחדל של חיישנים באמצעות כלי הפיתוח ל-Chrome
במקרים מסוימים, לא צריך מכשיר פיזי כדי לשחק עם Generic Sensor API. בכלי הפיתוח ל-Chrome יש תמיכה מצוינת בהדמיה של כיוון המכשיר.
פרטיות ואבטחה
קריאות החיישנים הן נתונים רגישים שיכולים להיות חשופים להתקפות שונות מדפי אינטרנט זדוניים. הטמעות של ממשקי Generic Sensor API כוללות כמה מגבלות כדי לצמצם את הסיכונים האפשריים לאבטחה ולפרטיות. מפתחים שמתכוונים להשתמש ב-API צריכים לקחת בחשבון את המגבלות האלה, ולכן נפרט אותן בקצרה.
רק HTTPS
Generic Sensor API היא תכונה עוצמתית, ולכן הדפדפן מאפשר להשתמש בה רק בהקשרים מאובטחים. בפועל, זה אומר שכדי להשתמש ב-Generic Sensor API, תצטרכו לגשת לדף באמצעות HTTPS. במהלך הפיתוח אפשר לעשות זאת באמצעות http://localhost, אבל בסביבת הייצור צריך להשתמש ב-HTTPS בשרת. באוסף המאמרים בנושא בטיחות ואבטחה מפורטות שיטות מומלצות והנחיות.
שילוב של מדיניות ההרשאות
השילוב של מדיניות ההרשאות ב-Generic Sensor API שולט בגישה לנתוני החיישנים של פריים.
כברירת מחדל, אפשר ליצור אובייקטים של Sensor רק בתוך מסגרת ראשית או מסגרות משנה מאותו מקור, וכך למנוע קריאה לא מורשית של נתוני חיישנים מ-iframe ממקורות שונים. אפשר לשנות את התנהגות ברירת המחדל הזו על ידי הפעלה או השבתה מפורשת של התכונות המקבילות שמוגדרות על ידי מדיניות.
בדוגמה שלמטה מוצגת הענקת גישה לנתוני מד התאוצה ל-iframe חוצה מקורות, כלומר עכשיו אפשר ליצור שם אובייקטים מסוג Accelerometer או LinearAccelerationSensor.
<iframe src="https://third-party.com" allow="accelerometer" />
יכול להיות שיהיה עיכוב בהעברת נתוני החיישן
הגישה לקריאות של חיישנים אפשרית רק מדף אינטרנט גלוי, כלומר כשהמשתמש מקיים אינטראקציה עם הדף. בנוסף, נתוני החיישן לא יסופקו למסגרת ההורה אם המיקוד של המשתמש ישתנה למסגרת משנה חוצת-מקורות. הפעולה הזו מונעת מהמסגרת הראשית להסיק את קלט המשתמש.
מה השלב הבא?
יש קבוצה של סוגי חיישנים שכבר צוינו ויוטמעו בעתיד הקרוב, כמו חיישן אור רגיש לסביבה או חיישן קירבה. עם זאת, הודות ליכולת ההרחבה הגדולה של מסגרת Generic Sensor, אנחנו יכולים לצפות להופעה של עוד סוגים חדשים שמייצגים סוגים שונים של חיישנים.
תחום חשוב נוסף לעבודה עתידית הוא שיפור של Generic Sensor API עצמו. מפרט Generic Sensor הוא כרגע המלצה אפשרית, כלומר עדיין יש זמן לבצע תיקונים ולהוסיף פונקציונליות חדשה שמפתחים צריכים.
אתם יכולים לעזור!
מפרטי החיישן הגיעו לרמת הבשלות Candidate Recommendation, ולכן נשמח לקבל משוב ממפתחי אתרים ומפתחי דפדפנים. נשמח לדעת אילו תכונות כדאי להוסיף או אם יש משהו שתרצו לשנות ב-API הנוכחי.
אתם מוזמנים לשלוח בעיות שקשורות למפרט וגם באגים בהטמעה של Chrome.
משאבים
- פרויקטים לדוגמה: https://w3c.github.io/generic-sensor-demos/
- מפרט Generic Sensor API: https://w3c.github.io/sensors/
- בעיות במפרט: https://github.com/w3c/sensors/issues
- רשימת תפוצה של קבוצת העבודה ב-W3C: public-device-apis@w3.org
- סטטוס התכונות של Chrome: https://www.chromestatus.com/feature/5698781827825664
- באגים בהטמעה: http://crbug.com?q=component:Blink>Sensor
תודות
המאמר הזה נבדק על ידי Joe Medley ו-Kayce Basques.