פיתוח אינטרנט לכמה נקודות מגע

מבוא

במכשירים ניידים כמו סמארטפונים וטאבלטים יש בדרך כלל מסך קיבולי עם חיישן מגע, כדי לתעד אינטראקציות שמתרחשות באמצעות אצבעות המשתמש. ככל שהאינטרנט לנייד מתפתח כדי לאפשר אפליקציות מתוחכמות יותר ויותר, מפתחי אתרים צריכים דרך להתמודד עם האירועים האלה. לדוגמה, כמעט בכל משחק מהיר השחקן צריך ללחוץ על כמה לחצנים בו-זמנית, ובמסגרת מסך מגע, המשמעות היא מגע בכמה מקומות בו-זמנית.

Apple השיקה את Touch events API ל-iOS 2.0. מערכת Android מתקרבת לתקן הזה בפועל ומצמצמת את הפער. לאחרונה קבוצת עבודה של W3C התאספה כדי לעבוד על המפרט הזה של אירועי מגע.

במאמר הזה אתעמק ב-API לאירועי מגע שמסופק על ידי מכשירי iOS ו-Android, וגם את Chrome למחשב בחומרה שתומכת במגע, ואבחן אילו סוגי אפליקציות אפשר לפתח, נציג כמה שיטות מומלצות ואסביר טכניקות שימושיות שיעזרו לך לפתח אפליקציות מבוססות מגע.

אירועי מגע

במפרט מתוארים שלושה אירועי מגע בסיסיים, והם מיושמים באופן נרחב במכשירים ניידים:

  • touchstart: אצבע מוצבת על רכיב DOM.
  • touchmove: גרירה של אצבע לאורך רכיב DOM.
  • touchend: האצבע מוציאים מרכיב DOM.

כל אירוע מגע כולל שלוש רשימות של נגיעות:

  • touches: רשימה של כל האצבעות שנמצאות כרגע על המסך.
  • targetTouches: רשימת אצבעות על רכיב ה-DOM הנוכחי.
  • changedTouches: רשימה של האצבעות שמעורבות באירוע הנוכחי. לדוגמה: באירוע של קצה מגע, זו תהיה האצבע שהוסרה.

הרשימות האלה מורכבות מאובייקטים שמכילים פרטי מגע:

  • identifier: מספר שמזהה באופן ייחודי את האצבע הנוכחית בסשן המגע.
  • target: רכיב ה-DOM שהיה היעד של הפעולה.
  • קואורדינטות של לקוח/דף/מסך: המיקום במסך שבו התרחשה הפעולה.
  • קואורדינטות רדיוס ו-rotationAngle: מתארות את האליפסה שמתקרבת לצורת האצבע.

אפליקציות שתומכות במגע

האירועים touchstart,‏ touchmove ו-touchend מספקים קבוצת תכונות עשירה מספיק כדי לתמוך כמעט בכל סוג של אינטראקציה מבוססת-מגע – כולל כל התנועות הרגילות של מגע בכמה נקודות, כמו צמצום באמצעות צביטה, סיבוב וכו'.

קטע הקוד הזה מאפשר לגרור רכיב DOM באמצעות מגע של אצבע אחת:

var obj = document.getElementById('id');
obj.addEventListener('touchmove', function(event) {
  // If there's exactly one finger inside this element
  if (event.targetTouches.length == 1) {
    var touch = event.targetTouches[0];
    // Place element where the finger is
    obj.style.left = touch.pageX + 'px';
    obj.style.top = touch.pageY + 'px';
  }
}, false);

בהמשך מופיעה דוגמה שמציגה את כל הנגיעות הנוכחיות במסך. כדאי לעשות את זה כדי לקבל תחושה לגבי מהירות התגובה של המכשיר.

מעקב אחרי האצבעות.
// Setup canvas and expose context via ctx variable
canvas.addEventListener('touchmove', function(event) {
  for (var i = 0; i < event.touches.length; i++) {
    var touch = event.touches[i];
    ctx.beginPath();
    ctx.arc(touch.pageX, touch.pageY, 20, 0, 2*Math.PI, true);
    ctx.fill();
    ctx.stroke();
  }
}, false);

הדגמות

כבר יש כמה הדגמות מעניינות של מגע רב-משתמש, כמו הדגמה של ציור על לוח של Paul Irish ואחרים.

ציור בצילום מסך

וגם Browser Ninja, הדגמה טכנית שמהווה עותק של Fruit Ninja באמצעות טרנספורמציות ומעברים של CSS3, וכן בדף הקנבס:

נינג&#39;ה דפדפנים

שיטות מומלצות

מניעת הזום

הגדרות ברירת המחדל לא עובדות טוב מאוד במסכים עם תמיכה במגע רב-משתמש, כי לרוב התנועות והמחוות משויכות להתנהגות הדפדפן, כמו גלילה ושינוי מרחק התצוגה.

כדי להשבית את האפשרות להגדלת התצוגה, מגדירים את אזור התצוגה כך שלא יהיה ניתן לשנות את הגודל שלו על ידי המשתמש באמצעות המטא תג הבא:

<meta name="viewport" 
  content="width=device-width, initial-scale=1.0, user-scalable=no>

למידע נוסף על הגדרת אזור התצוגה, ניתן לעיין במאמר הזה בפורמט HTML5 לנייד.

מניעת גלילה

במכשירים ניידים מסוימים יש התנהגויות ברירת מחדל ל-touchmove, כמו האפקט הקלאסי של iOS overscroll, שגורם לתצוגה לחזור למצב הקודם כשהגלילה חורגת מגבולות התוכן. הדבר מביא לבלבול באפליקציות רבות עם תמיכה במגע רב-משתמש, ואפשר להשבית אותו בקלות:

document.body.addEventListener('touchmove', function(event) {
  event.preventDefault();
}, false); 

עיבוד בקפידה

אם אתם כותבים אפליקציה עם תמיכה במגע בכמה אצבעות, שכוללת תנועות מורכבות עם כמה אצבעות, חשוב להיזהר בתגובה שלכם לאירועי מגע, כי אתם תידרשו לטפל בהרבה אירועים כאלה בו-זמנית. נבחן את הדוגמה בקטע הקודם, שבה מצוירים כל נקודות המגע במסך. תוכלו לשרטט ברגע שיש קלט מגע:

canvas.addEventListener('touchmove', function(event) {
  renderTouches(event.touches);
}, false);

אבל הטכניקה הזו לא מתאימה למספר גדול של אצבעות על המסך. במקום זאת, אפשר לעקוב אחרי כל האצבעות ולבצע רינדור בלולאה כדי לשפר את הביצועים בצורה משמעותית:

var touches = []
canvas.addEventListener('touchmove', function(event) {
  touches = event.touches;
}, false);

// Setup a 60fps timer
timer = setInterval(function() {
  renderTouches(touches);
}, 15);

שימוש ב-targetTouches וב-changedTouches

חשוב לזכור ש-event.touches הוא מערך של כל האצבעות שנוגעות במסך, ולא רק אלה שנמצאות ביעד של רכיב ה-DOM. יכול להיות שיהיה שימושי יותר להשתמש ב-event.targetTouches או ב-event.changedTouches במקום זאת.

לסיום, מכיוון שאתם מפתחים לנייד, חשוב להכיר את השיטות המומלצות הכלליות לנייד. השיטות האלה מפורטות במאמר של אריק ביילמן, וגם במסמך הזה של W3C.

תמיכה במכשיר

לצערנו, ההטמעות של אירועי מגע משתנות מאוד ברמת השלמות והאיכות. כתבתי סקריפט אבחון שמוצגים בו פרטים בסיסיים על הטמעת API למגע, כולל האירועים הנתמכים והרזולוציה של הפעלת touchmove. בדקתי את Android 2.3.3 בחומרה של Nexus One ושל Nexus S, Android 3.0.1 ב-Xoom ו-iOS 4.2 ב-iPad וב-iPhone.

בקצרה, כל הדפדפנים שנבדקו תומכים באירועים touchstart,‏ touchend ו-touchmove.

המפרט כולל שלושה אירועי מגע נוספים, אבל אף דפדפן שנבדק לא תומך בהם:

  • touchenter: אצבע נעה נכנסת לאלמנט DOM.
  • touchleave: אצבע נעה תשאיר רכיב DOM.
  • touchcancel: מגע נקטע (ספציפי להטמעה).

בכל רשימת נגיעות, הדפדפנים שנבדקו מספקים גם את רשימות הנגיעות touches,‏ targetTouches ו-changedTouches. עם זאת, אף אחד מהדפדפנים שנבדקו לא תומך ב-radiusX, ב-radiusY או ב-rotationAngle, שמציינים את הצורה של האצבע שמגעת במסך.

במהלך תנועה במגע, האירועים מופעלים בערך 60 פעמים בשנייה בכל המכשירים שנבדקו.

Android 2.3.3‏ (Nexus)

בדפדפן Android Gingerbread (נבדק ב-Nexus One וב-Nexus S), אין תמיכה במגע בכמה אצבעות. אנחנו מודעים לבעיה הזו.

Android 3.0.1‏ (Xoom)

בדפדפן של Xoom יש תמיכה בסיסית במגע רב-אצבעות, אבל היא פועלת רק ברכיב DOM אחד. הדפדפן לא מגיב בצורה נכונה לשני נגיעות בו-זמנית ברכיבי DOM שונים. במילים אחרות, הקוד הבא יגיב לשתי נגיעות בו-זמנית:

obj1.addEventListener('touchmove', function(event) {
  for (var i = 0; i < event.targetTouches; i++) {
    var touch = event.targetTouches[i];
    console.log('touched ' + touch.identifier);
  }
}, false);

אבל:

var objs = [obj1, obj2];
for (var i = 0; i < objs.length; i++) {
  var obj = objs[i];
  obj.addEventListener('touchmove', function(event) {
    if (event.targetTouches.length == 1) {
      console.log('touched ' + event.targetTouches[0].identifier);
    }
  }, false);
}

iOS 4.x‏ (iPad, ‏ iPhone)

במכשירי iOS יש תמיכה מלאה במגע רב-משתמש, הם יכולים לעקוב אחרי כמה אצבעות ומספקים חוויית מגע רספונסיבית מאוד בדפדפן.

כלים למפתחים

בפיתוח לנייד, בדרך כלל קל יותר להתחיל ליצור את אב הטיפוס במחשב ואז לטפל בחלקים הספציפיים לנייד במכשירים שבהם אתם מתכוונים לתמוך. מולטי-טאץ' היא אחת מהתכונות שקשה לבדוק במחשב, כי ברוב המחשבים אין קלט מגע.

הצורך לבדוק בנייד עלול להאריך את מחזור הפיתוח, כי כל שינוי שתבצעו צריך להעביר לשרת ואז לטעון במכשיר. לאחר מכן, כשהאפליקציה פועלת, קשה מאוד לנפות באגים, כי בטאבלטים ובטלפונים חכמים אין כלים לפיתוח אינטרנט.

פתרון לבעיה הזו הוא הדמיה של אירועי מגע במכונת הפיתוח. לגבי מגע יחיד, אפשר לדמות אירועי מגע על סמך אירועי עכבר. אפשר לדמות אירועי מולטי-טאץ' אם יש לכם מכשיר עם קלט מגע, כמו Apple MacBook מודרני.

אירועים של מגע יחיד

אם אתם רוצים לדמות אירועי מגע יחיד במחשב, ב-Chrome יש אמולציה של אירועי מגע בכלי למפתחים. פותחים את הכלים למפתחים, בוחרים בסמל גלגל השיניים של ההגדרות, ואז בוחרים באפשרות 'עקיפות' או 'הדמיה' ומפעילים את האפשרות 'הדמיה של אירועי מגע'.

בדפדפנים אחרים, כדאי לנסות את Phantom Limb, שמאפשר לדמות אירועי מגע בדפים וגם מציג יד ענקית.

יש גם את הפלאגין jQuery‏ Touchable שמאחד אירועי מגע ואירועי עכבר בפלטפורמות שונות.

אירועים של מגע בכמה נקודות

כדי לאפשר לאפליקציית האינטרנט עם התמיכה במגע רב-משתמש לפעול בדפדפן שלכם במשטח המגע עם התמיכה במגע רב-משתמש (כמו Apple MacBook או MagicPad), יצרתי את ה-polyfill של MagicTouch.js. הוא מתעד אירועי מגע ממשטח המגע וממיר אותם לאירועי מגע תואמים לתקן.

  1. מורידים ומתקינים את הפלאגין של npTuioClient NPAPI ב- ~/Library/Internet Plug-Ins/.
  2. מורידים את אפליקציית TongSeng TUIO ל-MagicPad של Mac ומפעילים את השרת.
  3. מורידים את MagicTouch.js, ספריית JavaScript, כדי לבצע סימולציה של אירועי מגע שתואמים למפרט על סמך קריאות חוזרות של npTuioClient.
  4. מוסיפים את הסקריפט magictouch.js ואת הפלאגין npTuioClient לאפליקציה באופן הבא:
<head>
  ...
  <script src="/path/to/magictouch.js"></script>
</head>

<body>
  ...
  <object id="tuio" type="application/x-tuio" style="width: 0px; height: 0px;">
    Touch input plugin failed to load!
  </object>
</body>

יכול להיות שתצטרכו להפעיל את הפלאגין.

הדגמה בזמן אמת עם magictouch.js זמינה בכתובת paulirish.com/demo/multi:

בדקתי את הגישה הזו רק ב-Chrome 10, אבל היא אמורה לפעול בדפדפנים מודרניים אחרים עם שינויים קלים בלבד.

אם במחשב שלכם אין קלט של מגע רב-משתמש, תוכלו לדמות אירועי מגע באמצעות מכשירי מעקב אחרים של TUIO, כמו reacTIVision. למידע נוסף, קראו את דף הפרויקט של TUIO.

שימו לב שהתנועות עשויות להיות זהות לתנועות עם כמה נקודות מגע ברמת מערכת ההפעלה. ב-OS X אפשר להגדיר אירועים ברמת המערכת על ידי מעבר לחלונית ההעדפה של משטח המגע בקטע 'העדפות מערכת'.

ככל שתהיה תמיכה רחבה יותר בתכונות של מגע רב-משתמש בדפדפנים לנייד, אני מקווה לראות אפליקציות אינטרנט חדשות ינצלו את היתרונות המלאים של ממשק ה-API העשיר הזה.