למה חלק מהאנימציות איטיות?

דפדפנים מודרניים יכולים להוסיף אנימציה לשני מאפייני CSS בצורה זולה: transform ו-opacity. אם תוסיפו אנימציה לפריט אחר, סביר להניח שלא תגיעו לקצב של 60 פריימים לשנייה (FPS). בפוסט הזה מוסבר למה זה קורה.

ביצועי אנימציה וקצב פריימים

מקובל לחשוב שקצב פריימים של 60FPS הוא היעד כשמפעילים אנימציה באינטרנט. שיעור הפריימים הזה יבטיח שהאנימציות ייראו חלקות. באינטרנט, פריים הוא הזמן שנדרש לביצוע כל העבודה הנדרשת כדי לעדכן את המסך ולצבוע אותו מחדש. אם כל פריים לא יושלם תוך 16.7 אלפיות השנייה (1,000 אלפיות השנייה / 60 ≈ 16.7), המשתמשים יבחינו בהשהיה.

צינור העיבוד ליצירת הגרפיקה

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

  1. Style: חישוב הסגנונות שחלים על הרכיבים.
  2. פריסה: יצירת נתוני הגיאומטריה והמיקום של כל רכיב.
  3. צביעה: מילוי הפיקסלים של כל רכיב בשכבות.
  4. שילוב: ציור השכבות במסך.

ארבעת השלבים האלה נקראים צינור עיבוד הנתונים לעיבוד (rendering) של הדפדפן.

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

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

אנימציה של מאפייני פריסה

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

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

הנפשה של מאפייני צבע

Paint הוא התהליך שבו קובעים באיזה סדר צריך לצייר את האלמנטים במסך. בדרך כלל מדובר במשימה הארוכה ביותר מתוך כל המשימות בצינור עיבוד הנתונים.

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

אנימציה של מאפיינים מורכבים

הרכבה היא התהליך של הפרדת הדף לשכבות, המרת המידע על המראה של הדף לפסיקלים (רסטורציה) והרכבת השכבות יחד כדי ליצור דף (הרכבה).

לכן, המאפיין opacity נכלל ברשימה של דברים שקל ליצור להם אנימציה. כל עוד הנכס הזה נמצא בשכבה משלו, ה-GPU יכול לטפל בשינויים בו במהלך שלב היצירה. דפדפנים ו-WebKit המבוססים על Chromium יוצרים שכבה חדשה לכל רכיב שיש בו מעבר CSS או אנימציה ב-opacity.

מהי שכבה?

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

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

ביצועי CSS לעומת JavaScript

יכול להיות שתתהו: מבחינת ביצועים, עדיף להשתמש ב-CSS או ב-JavaScript ליצירת אנימציות?

אנימציות שמבוססות על CSS ואנימציות אינטרנט (בדפדפנים שתומכים ב-API) מטופלות בדרך כלל בשרשור שנקרא שרשור המאגר. הוא שונה מה-thread הראשי של הדפדפן, שבו מתבצעים עיצוב, פריסה, ציור ו-JavaScript. כלומר, אם הדפדפן מפעיל משימות יקרות ב-thread הראשי, האנימציות האלה ימשיכו לפעול בלי הפרעה.

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

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