מבוא
ציור הרכיבים של אתר או אפליקציה יכול להיות יקר מאוד, ויכולה להיות לכך השפעה שלילית על ביצועי זמן הריצה שלנו. במאמר הזה נסקור במהירות מה יכול לגרום לציור בדפדפן, ואיך אפשר למנוע ציורים מיותרים.
ציור: סיור מהיר במיוחד
אחת מהמשימות העיקריות שדפדפן צריך לבצע היא המרת ה-DOM וה-CSS לתמונה של פיקסלים במסך, והוא עושה זאת באמצעות תהליך מורכב למדי. הוא מתחיל בקריאת תגי העיצוב ומשם נוצר עץ DOM. הוא מבצע פעולה דומה עם ה-CSS, וממנו יוצר את ה-CSSOM. לאחר מכן, ה-DOM וה-CSSOM משולבים, ובסופו של דבר מגיעים למבנה שממנו אפשר להתחיל לצייר כמה פיקסלים.
תהליך הציור עצמו מעניין. ב-Chrome שמשלב עץ של DOM ו-CSS, הוא עובר רסטר על ידי תוכנה מסוימת שנקראת Skia. אם שיחקתם פעם עם האלמנט canvas
, ממשק ה-API של Skia ייראה לכם מוכר מאוד. יש בו הרבה פונקציות בסגנון moveTo
ו-lineTo
, וגם כמה פונקציות מתקדמות יותר. בעיקרון, כל הרכיבים שצריך לצייר מתמצתים באוסף של קריאות ל-Skia שאפשר לבצע, והפלט הוא קבוצה של בייטים. קובצי ה-bitmap האלה מועלים ל-GPU, והוא עוזר בהרכבתם יחד כדי ליצור את התמונה הסופית במסך.
העיקרון הוא שעומס העבודה של Skia מושפע ישירות מהסגנונות שאתם מחילים על הרכיבים. אם אתם משתמשים בסגנונות כבדים מבחינה אלגוריתמית, ל-Skia תהיה יותר עבודה. Colt McAnlis כתב מאמר על האופן שבו CSS משפיע על משקל העיבוד של דף, מומלץ לקרוא אותו כדי לקבל תובנות נוספות.
עם זאת, ביצוע פעולות צביעה לוקח זמן, ואם לא נצמצם את הזמן הזה, נחרוג מתקציב הפריימים של 16 אלפיות השנייה. המשתמשים יבחינו שפספסנו פריימים ויראו את זה כתנועה קטועה, שבסופו של דבר פוגעת בחוויית המשתמש באפליקציה. אנחנו לא רוצים שזה יקרה, לכן נבדוק אילו דברים גורמים לצורך בפעולות צביעה, ומה אפשר לעשות בקשר לזה.
גלילה
בכל פעם שגוללים למעלה או למטה בדפדפן, צריך לצבוע מחדש את התוכן כדי שהוא יופיע במסך. אם הכל ילך כשורה, זה יהיה רק אזור קטן, אבל גם אם זה המצב, אפשר להחיל סגנונות מורכבים על הרכיבים שצריך לצייר. לכן, גם אם יש לכם אזור קטן לצביעה, זה לא אומר שהצביעה תתבצע במהירות.
כדי לראות אילו אזורים נצבעו מחדש, אפשר להשתמש בתכונה 'הצגת מלבני צבע' בכלי הפיתוח של Chrome (פשוט לוחצים על סמל גלגל השיניים הקטנה בפינה השמאלית התחתונה). לאחר מכן, כשכלי הפיתוח פתוחים, פשוט יוצרים אינטראקציה עם הדף ותראו מלבנים מהבהבים שמציינים איפה ומתי Chrome צייר חלק מהדף.
ביצועי הגלילה חיוניים להצלחת האתר. משתמשים שמים לב מאוד כשהגלילה באתר או באפליקציה לא נוחה, והם לא אוהבים את זה. לכן, יש לנו אינטרס מובהק לשמור על זמן עיבוד קצר במהלך גלילה כדי שהמשתמשים לא יראו תנודות.
כתבתי בעבר מאמר על ביצועי גלילה, כך שאפשר לעיין בו כדי לקבל מידע נוסף על הפרטים הספציפיים של ביצועי גלילה.
אינטראקציות
אינטראקציות הן סיבה נוספת לפעולות צביעה: מעברים עם העכבר מעל, קליקים, מגע וגרירה. בכל פעם שהמשתמש מבצע אחת מהאינטראקציות האלה, למשל העברת העכבר מעל הרכיב, Chrome יצטרך לצבוע מחדש את הרכיב שהושפע. ובדומה לגלילה, אם יש צורך בצבעים גדולים ומורכבים, תראו ירידה בקצב הפריימים.
כולם רוצים אנימציות אינטראקציה נעימות וחלקות, לכן שוב נצטרך לבדוק אם הסגנונות שמשתנים באנימציה שלנו גוזלים יותר מדי זמן.
שילוב לא מוצלח
מה קורה אם גוללים ומזיזים את העכבר בו-זמנית? יש לי אפשרות מאוד "לפעול" בטעות עם רכיב מסוים בזמן הגלילה על פניו, וכך לגרום לצבע יקר. כתוצאה מכך, יכול להיות שאחרוג מתקציב הפריימים שלי של כ-16.7 אלפיות השנייה (הזמן שאנחנו צריכים להישאר מתחתיו כדי להגיע ל-60 פריימים לשנייה). יצרתי הדגמה כדי להראות לך בדיוק למה התכוונתי. כשאתם גוללים ומזיזים את העכבר, אתם אמורים לראות את האפקטים של העכבר מעל לתמונה, אבל נבדוק מה מוצג בכלים למפתחים של Chrome:
בתמונה שלמעלה אפשר לראות שכלי הפיתוח רושמים את ציור הצבע כשמציבים את סמן העכבר מעל אחת הבלוקים. למדתי על כמה סגנונות כבדים במיוחד בהדגמה שלי כדי להבהיר את העניין, ולכן אני מרחיב את תקציב הפריים שלי ומדי פעם. הדבר האחרון שאני רוצה הוא לבצע את עבודת הציור הזו ללא צורך, במיוחד במהלך גלילה כשיש עבודה אחרת שצריך לבצע.
אז איך אפשר למנוע את זה? למרבה המזל, קל מאוד ליישם את התיקון. הטריק הוא לצרף טיפול scroll
שישבית את האפקטים של העברת העכבר מעל הרכיב, ולהגדיר טיימר להפעלת האפקטים מחדש. המשמעות היא שאנחנו מתחייבים שבזמן הגלילה לא יהיה צורך לבצע צבעי אינטראקציה יקרים. אחרי שתפסיקו לנסוע למשך זמן מספיק, נפעיל אותם שוב.
זה הקוד:
// Used to track the enabling of hover effects
var enableTimer = 0;
/*
* Listen for a scroll and use that to remove
* the possibility of hover effects
*/
window.addEventListener('scroll', function() {
clearTimeout(enableTimer);
removeHoverClass();
// enable after 1 second, choose your own value here!
enableTimer = setTimeout(addHoverClass, 1000);
}, false);
/**
* Removes the hover class from the body. Hover styles
* are reliant on this class being present
*/
function removeHoverClass() {
document.body.classList.remove('hover');
}
/**
* Adds the hover class to the body. Hover styles
* are reliant on this class being present
*/
function addHoverClass() {
document.body.classList.add('hover');
}
כפי שאפשר לראות, אנחנו משתמשים בכיתה בגוף כדי לעקוב אחרי האפקטים של העכבר מעל לרכיב, ולבדוק אם הם 'מותרים' או לא. הסגנונות הבסיסיים מסתמכים על נוכחות הכיתה הזו:
/* Expect the hover class to be on the body
before doing any hover effects */
.hover .block:hover {
…
}
זהו, סיימתם.
סיכום
ביצועי הרינדור חיוניים כדי שהמשתמשים ייהנו מהאפליקציה, ותמיד כדאי לשאוף לשמור על עומס העבודה של הציור מתחת ל-16ms. כדי לעשות זאת, כדאי לשלב את DevTools במהלך תהליך הפיתוח כדי לזהות צווארי בקבוק ולתקן אותם כשהם מופיעים.
אינטראקציות לא מכוונות, במיוחד עם אלמנטים שמכילים הרבה צבע, יכולות להיות יקרות מאוד וקטנות את ביצועי העיבוד. כמו שראיתם, אנחנו יכולים להשתמש בקטע קוד קטן כדי לפתור את הבעיה.
כדאי לבדוק את האתרים והאפליקציות שלכם, האם הם יכולים להסדיר את השימוש בהם עם קצת הגנה מצבע?