פרלקסציה ביצועית

Robert Flack
Robert Flack

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

איור של פרלקס.

אמ;לק

  • אין להשתמש באירועי גלילה או ב-background-position כדי ליצור אנימציות של פרלקס.
  • השתמשו בשינויים ב-CSS תלת-ממדי כדי ליצור אפקט פרלקס מדויק יותר.
  • ב-Safari לנייד, יש להשתמש ב-position: sticky כדי לוודא שאפקט הפרלקס מופץ.

אם רוצים למצוא את פתרון הוספת המידע, עוברים אל מאגר דוגמאות הרכיבים של ממשק המשתמש ב-GitHub ולוקחים את JS helper של Parallax! תוכלו לראות הדגמה חיה של גלילת הפרלקס ב מאגר GitHub

מפרקים של בעיות

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

לא טוב: שימוש באירועי גלילה

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

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

לא טוב: מתבצע עדכון של background-position

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

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

CSS בתלת-ממד

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

  • להגדיר רכיב מכיל לגלילה עם overflow-y: scroll (וכנראה overflow-x: hidden).
  • על אותו רכיב, צריך להחיל את הערך perspective ומאפיין perspective-origin מוגדר ל-top left או ל-0 0.
  • בשביל הצאצאים של הרכיב הזה, מחילים תרגום ב-Z ומשנים את קנה המידה שלהם עד לספק תנועת פרלקס מבלי להשפיע על הגודל שלהם על המסך.

שירות ה-CSS בגישה הזו נראה כך:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

ההנחה היא בקטע HTML כזה:

<div class="container">
    <div class="parallax-child"></div>
</div>

כוונון קנה המידה של פרספקטיבה

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

במקרה של הקוד שלמעלה, הפרספקטיבה היא 1px, המרחק של parallax-child בפורמט Z הוא -2px. כלומר, הרכיב יצטרך כדי שיגדל פי 3, אפשר לראות את הערך שמחובר לקוד: scale(3).

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

איך הגישה הזו עובדת

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

עם זאת, החלת ערך פרספקטיבה על רכיב הגלילה מתבלבלת את התהליך הזה. היא משנה את המטריצות שעומדות מאחורי טרנספורמציה של הגלילה. עכשיו, גלילה של 300 פיקסלים עשויה להזיז את הילדים רק ב-150 פיקסלים, בהתאם perspective ו-translateZ ערכים שבחרת. אם לרכיב יש הערך 0 ב-translateZ, הוא נגלל ביחס של 1:1 (כפי שהיה בעבר), אבל זהו צאצא אם נדחפת ב-Z הרחק ממקור הפרספקטיבה, תתבצע גלילה במקום אחר מחיר! התוצאה נטו: תנועת פרלקס. וחשוב מאוד, אנחנו מטפלים בזה חלק ממכונת הגלילה הפנימית של הדפדפן, כלומר אין צורך להאזין לאירועי scroll או לשנות את background-position.

זבוב במשחה: Mobile Safari

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

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

ב-HTML שלמעלה, ה-.parallax-container הוא חדש והוא יפעל ביעילות מיישרים את הערך perspective ואנחנו מאבדים את אפקט הפרלקס. הפתרון, ברוב המקרים, התהליך די פשוט: תוסיפו transform-style: preserve-3d אל הרכיב, וכתוצאה מכך להפיץ אפקטים תלת-ממדיים (כמו נקודת המבט שלנו) ) שהוחלו במעלה העץ.

.parallax-container {
  transform-style: preserve-3d;
}

במקרה של Safari בנייד, הדברים קצת יותר מורכבים. מבחינה טכנית, ההחלה של overflow-y: scroll על רכיב הקונטיינר פועלת, אבל העלות של היכולת להזיז את רכיב הגלילה. הפתרון הוא להוסיף -webkit-overflow-scrolling: touch, אבל הוא גם יטשטש את perspective ולא נגביל את הפרלקס.

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

position: sticky כדי להציל!

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

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

באמצעות החלת position: -webkit-sticky על הרכיב המפוצל, אפשר "היפוך" ביעילות את אפקט ההשטח של -webkit-overflow-scrolling: touch. הדבר מבטיח שהרכיב המרכיב את הפרלקסה מפנה אל ישות אב עם תיבת גלילה, שבמקרה הזה היא .container. לאחר מכן: בדומה לפני כן, .parallax-container מחיל את הערך perspective, שמשנה את היסט הגלילה המחושב ויוצר אפקט פרלקס.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

הפעולה הזו משחזרת את אפקט הפרלקס ל-Mobile Safari, שהוא חדשות מצוינות סיבוב!

אזהרות לגבי מיקום במיקום קבוע

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

  • עם position: sticky, ככל שהרכיב קרוב יותר ל-z=0, הוא נמוך יותר נע.
  • בלי position: sticky, ככל שהרכיב קרוב יותר ל-z=0, כך יותר שהוא זז.

אם כל זה נראה מופשט, צפו בהדגמה הזו של רוברט פלאק. שמדגים איך אלמנטים מתנהגים באופן שונה עם או בלי מיקום קבוע את המיקום שלו. כדי לראות את ההבדל, צריך להתקין את Chrome Canary (גרסה 56) בזמן הכתיבה) או Safari.

צילום מסך של נקודת המבט של Parallax

הדגמה של רוברט פלאק שממחישה איך position: sticky משפיעה על גלילה פרלקסית.

באגים שונים ופתרונות עקיפים

עם זאת, כמו בכל דבר, עדיין יש בליטות ובליטות החלקה:

  • תמיכה לא עקבית. אנחנו עדיין מיישמים את התמיכה ב- ב-Chrome, ב-Edge אין תמיכה מלאה, וב-Firefox יש באגים בציור כאשר 'דביק' משולב עם שינויים בנקודת המבט. בתרחישים כאלה במקרים כאלה, כדאי להוסיף מעט קוד כדי להוסיף רק את הקוד position: sticky גרסה עם קידומת -webkit-) כשצריך אותה, כלומר ב-Mobile Safari בלבד.
  • האפקט לא "רק עובד" ב-Edge. Edge מנסה לטפל בגלילה בשעה ברמת מערכת ההפעלה, וזה בדרך כלל דבר טוב, אבל במקרה הזה היא מונעת בזיהוי השינויים בנקודת המבט במהלך הגלילה. כדי לפתור את הבעיה, אפשר להוסיף רכיב מיקום קבוע, כפי שנראה שהוא מעביר את Edge ל שיטת גלילה שאינה של מערכת ההפעלה, ומבטיחה שהיא מביאה בחשבון שינויים בנקודות מבט.
  • "התוכן של הדף הפך לעצום!" דפדפנים רבים אחראים לעומס כשאנחנו מחליטים מה גודל התוכן של הדף, אבל לצערנו ב-Chrome וב-Safari לא להביא בחשבון את נקודת המבט. ככה. אם יש - נניח - סולם של פי 3 מיושם על רכיב מסוים, ייתכן טוב לראות סרגלי גלילה וכדומה, גם אם הרכיב נמצא בגודל 1x לאחר השינוי perspective הוחל. כדי לעקוף את הבעיה, של רכיבים מהפינה השמאלית התחתונה (עם transform-origin: bottom right), שפועל כי הוא יגרום לאלמנטים גדולים יותר לגדול 'אזור שלילי' (בדרך כלל בפינה השמאלית העליונה) של האזור הניתן לגלילה; ניתן לגלילה אף פעם לא מאפשרים לכם לראות תוכן שנמצא באזור השלילי או לגלול אליו.

סיכום

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

שחקו וספרו לנו איך אתם מתקדמים.