אמ;לק
- ב-Chrome 60 הפחתנו את תדירות האירועים כדי לצמצם את התנודות בפריימים, וכך לשפר את העקביות של תזמון הפריימים.
- השיטה
getCoalescedEvents()
, שהוצגה ב-Chrome 58, מספקת את אותו עושר של פרטי אירועים שתמיד היו זמינים.
חשוב לספק חוויית משתמש חלקה באינטרנט. חשוב לקצר את הזמן שחולף בין קבלת אירוע קלט לבין העדכון בפועל של התצוגה החזותית. בגרסאות האחרונות של Chrome, הפחתנו את זמן האחזור של הקלט במכשירים האלה.
כדי לשפר את הביצועים והפעולה חלקה, ב-Chrome 60 אנחנו מבצעים שינוי שגורם לאירועים האלה להתרחש בתדירות נמוכה יותר, תוך הגדלת רמת הפירוט של המידע שסופק. בדומה למה שקרה כשהושקה גרסת Jelly Bean והוצג Choreographer שמיישר את הקלט ב-Android, אנחנו משיקים קלט שמוכן לפריימים באינטרנט בכל הפלטפורמות.
אבל לפעמים צריך יותר אירועים. לכן, ב-Chrome 58 הטמענו שיטה שנקראת getCoalescedEvents()
, שמאפשרת לאפליקציה לאחזר את הנתיב המלא של הסמן גם כשהיא מקבלת פחות אירועים.
קודם נדבר על תדירות האירועים.
הפחתת תדירות האירועים
לפני שנמשיך, חשוב להבין כמה דברים בסיסיים: מסכי מגע שולחים קלט ב-60-120Hz, ומכשירי עכבר שולחים קלט בדרך כלל ב-100Hz (אבל יכולים לשלוח קלט גם ב-2000Hz). עם זאת, קצב הרענון הרגיל של צג הוא 60Hz. מה המשמעות של זה בפועל? כלומר, אנחנו מקבלים קלט בקצב גבוה יותר מזה שבו אנחנו מעדכנים את המסך בפועל. בואו נסתכל על ציר זמן של ביצועים מ-devtools עבור אפליקציית ציור פשוטה על קנבס.
בתמונה שבהמשך, כשהקלט עם requestAnimationFrame()
מיושר מושבת, אפשר לראות כמה בלוקים של עיבוד לכל פריים עם זמן פריים לא עקבי.
הקטעים הצהובים הקטנים מציינים בדיקות להיטים של דברים כמו היעד של אירוע ה-DOM, שליחת האירוע, הפעלת JavaScript, עדכון הצומת שמוצג מעליהם ואולי חישוב מחדש של הפריסה והסגנונות.
אז למה אנחנו מבצעים עבודה נוספת שלא גורמת לעדכונים חזותיים? אנחנו לא רוצים לבצע עבודה שלא תשפר את חוויית המשתמש בסופו של דבר. החל מ-Chrome 60, צינור עיבוד הנתונים להזנת נתונים יאחר את שליחת האירועים המתמשכים (wheel
, mousewheel
, touchmove
, pointermove
, mousemove
) וישלח אותם ממש לפני שמתבצעת הקריאה החוזרת (callback) של requestAnimationFrame()
. בתמונה שבהמשך (כשהתכונה מופעלת) אפשר לראות זמן אחיד יותר ליצירת פריימים וזמן קצר יותר לעיבוד אירועים.
הרצנו ניסוי שבו התכונה הזו מופעלת בערוצי Canary ו-Dev, וגילינו שאנחנו מבצעים 35% פחות בדיקות להיטים, וכך הליבה מוכנה לפעול בתדירות גבוהה יותר.
הערה חשובה למפתחי אתרים: כל אירוע נפרד (כמו keydown
, keyup
, mouseup
, mousedown
, touchstart
, touchend
) שמתרחש יישלח מיד יחד עם אירועים בהמתנה, תוך שמירה על הסדר היחסי. כשהתכונה הזו מופעלת, הרבה מהעבודה מוטמעת בתהליך הרגיל של מחזור האירועים, וכך מתקבל מרווח קלט עקבי. כך אירועים רציפים יהיו תואמים לאירועים מסוג scroll
ו-resize
שכבר שודרגו לזרימת הלולאה של האירועים ב-Chrome.
גילינו שרוב האפליקציות שמשתמשות באירועים כאלה לא משתמשות בתדירות הגבוהה יותר. ב-Android כבר יש התאמה של אירועים במשך כמה שנים, כך שזה לא משהו חדש, אבל יכול להיות שבאתרים יהיו פחות אירועים מפורטים בפלטפורמות למחשב. תמיד הייתה בעיה עם חוטים ראשיים לא סדירים שגורמים לתנודות בזרימת הקלט. כלומר, יכול להיות שתבחינו בקפיצות במיקום בכל פעם שהאפליקציה מבצעת פעולה, כך שלא תוכלו לדעת איך הסמן הגיע מנקודה אחת לנקודה אחרת.
השיטה getCoalescedEvents()
כמו שאמרתי, יש תרחישים נדירים שבהם האפליקציה מעדיפה לדעת את הנתיב המלא של הסמן. כדי לפתור את הבעיה של קפיצות גדולות וצמצום התדירות של האירועים, השקנו ב-Chrome 58 תוסף לאירועי סמן שנקרא getCoalescedEvents()
. בהמשך מופיעה דוגמה לאופן שבו תנודות בשרשור הראשי מוסתרות מהאפליקציה אם משתמשים ב-API הזה.
במקום לקבל אירוע יחיד, תוכלו לגשת למערך האירועים ההיסטוריים שגרמו לאירוע. ל-Android, ל-iOS ול-Windows יש ממשקי API דומים מאוד ב-SDKs המקומיים שלהם, ואנחנו חושפים ממשק API דומה לאינטרנט.
בדרך כלל, אפליקציית ציור עשויה לצייר נקודה על סמך ההיסטים באירוע:
window.addEventListener("pointermove", function(event) {
drawPoint(event.pageX, event.pageY);
});
אפשר לשנות בקלות את הקוד הזה כך שישתמש במערך האירועים:
window.addEventListener("pointermove", function(event) {
var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
for (let e of events) {
drawPoint(e.pageX, e.pageY);
}
});
שימו לב שלא כל המאפיינים באירועים המאוחדים מאוכלסים. מאחר שהאירועים המאוחדים לא נשלחים באמת, אלא רק נוסעים, הם לא עוברים בדיקת היטים. לשדות מסוימים, כמו currentTarget
ו-eventPhase
, יהיו ערכי ברירת מחדל. קריאה לשיטות שקשורות לטיפול באירועים, כמו stopPropagation()
או preventDefault()
, לא תשפיע על האירוע ההורה.