תחילת העבודה עם אנימציות שמונעות גלילה ב-CSS

1. לפני שמתחילים

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

הגרסה החדשה של Chrome 115 תומכת בקבוצה של מחלקות JavaScript ומאפייני CSS שמאפשרים ליצור בקלות אנימציות המבוססות על גלילה. ממשקי ה-API החדשים האלה פועלים בשילוב עם ממשקי ה-API הקיימים של אנימציות אינטרנט ואנימציות של CSS.

בשיעור הזה ב-Codelab אפשר ללמוד איך ליצור אנימציות שמונעות גלילה באמצעות CSS. השלמת ה-Codelab הזו תכיר לכם את מאפייני ה-CSS החדשים הרבים שקיימים בתכונה המלהיבה הזו, כמו scroll-timeline, view-timeline, animation-timeline ו-animation-range.

מה תלמדו

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

למה תזדקק?

אחד משילובי המכשירים הבאים:

  • גרסה עדכנית של Chrome (גרסה 115 ואילך) ב-ChromeOS, ב-macOS או ב-Windows עם 'תכונות ניסיוניות של פלטפורמת האינטרנט' התכונה הניסיונית מוגדרת כמופעלת.
  • הבנה בסיסית של HTML
  • הבנה בסיסית של CSS, במיוחד אנימציות ב-CSS

2. להגדרה

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

  1. פותחים כרטיסייה חדשה בדפדפן ועוברים אל https://github.com/googlechromelabs/io23-scroll-driven-animations-codelab.
  2. משכפלים את המאגר.
  3. פותחים את הקוד בסביבת הפיתוח המשולבת (IDE) המועדפת.
  4. מריצים את הפקודה npm install כדי להתקין את יחסי התלות.
  5. הריצו את npm start ובקרו בכתובת http://localhost:3000/.
  6. לחלופין, אם NPM לא מותקן, פותחים את הקובץ src/index.html ב-Chrome.

3. מידע על צירי זמן לאנימציה

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

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

  • ציר הזמן להתקדמות הגלילה
  • הצגת ציר הזמן של ההתקדמות

ב-CSS, אפשר לצרף את צירי הזמן האלה לאנימציה באמצעות המאפיין animation-timeline. כדאי לראות מה המשמעות של צירי הזמן החדשים ומה ההבדל ביניהם.

ציר הזמן להתקדמות הגלילה

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

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

הצגת ציר הזמן של ההתקדמות

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

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

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

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

הטווחים האפשריים של 'הצגת ציר הזמן' שאפשר לטרגט הם cover, contain, entry, exit, entry-crossing ו-exit-crossing. על הטווחים האלה נסביר בהמשך ב-Codelab הזה, אבל אם אתם יודעים עוד, אפשר להשתמש בכלי שנמצא בכתובת https://goo.gle/view-timeline-range-tool כדי לראות מה כל טווח מייצג.

4. יצירת אפקט לרקע של פרלקס

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

כדי ליישם את זה, יש שני שלבים:

  1. יוצרים אנימציה שזזה את המיקום של תמונת הרקע.
  2. קישור האנימציה להתקדמות הגלילה של המסמך.

יצירת האנימציה

  1. כדי ליצור את האנימציה, צריך להשתמש בקבוצה רגילה של תמונות מפתח. בהודעה, מזיזים את מיקום הרקע מ-0% לאורך ל-100%.

src/styles.css

@keyframes move-background {
  from {
    background-position: 50% 0%;
  }
  to {
    background-position: 50% 100%;
  }
}
  1. עכשיו מחברים את תמונות המפתח הבאות לרכיב הגוף:

src/styles.css

body {
  animation: 1s linear move-background;
}

באמצעות הקוד הזה, האנימציה move-background מתווספת לרכיב הגוף. נכס animation-duration שלו מוגדר לשנייה אחת, והוא משתמש בהכאה ל-linear.

הדרך הקלה ביותר ליצור ציר זמן עם התקדמות הגלילה היא להשתמש בפונקציה scroll(). הפעולה הזו יוצרת ציר זמן אנונימי עם התקדמות הגלילה, שאפשר להגדיר אותו כערך של המאפיין animation-timeline.

הפונקציה scroll() מקבלת גם את הארגומנט <scroller> וגם את הארגומנט <axis>.

הערכים הקבילים לארגומנט <scroller> הם:

  • nearest משתמשת במאגר הגלילה של ישות האב הקרובה ביותר (ברירת מחדל).
  • root משתמש באזור התצוגה של המסמך בתור מאגר הגלילה.
  • self משתמש ברכיב עצמו כמאגר גלילה.

הערכים הקבילים לארגומנט <axis> הם:

  • block משתמשת במדידת ההתקדמות לאורך ציר הבלוקים של מאגר הגלילה (ברירת מחדל).
  • inline משתמשת במדידת ההתקדמות לאורך הציר המוטבע של מאגר הגלילה.
  • y משתמשת במדידת ההתקדמות לאורך ציר ה-Y של מאגר הגלילה.
  • x משתמשת במדידת ההתקדמות לאורך ציר ה-X של מאגר הגלילה.

כדי לקשר את האנימציה לגלילה ברמה הבסיסית (root) בציר הבלוקים, הערכים שיועברו אל scroll() הם root ו-block. יחד, הערך הוא scroll(root block).

  • מגדירים את scroll(root block) כערך המאפיין animation-timeline בגוף.
  • בנוסף, מכיוון שאין הגיון בanimation-duration שמבוטאת בשניות, יש להגדיר את משך הזמן ל-auto. אם לא מציינים ערך של animation-duration, ברירת המחדל שלו היא auto.

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(root block);
}

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

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(nearest block);
}

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

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll();
}

אימות השינויים

אם הכול היה תקין, עכשיו אמורה להיות לך:

אם לא, בודקים את ההסתעפות solution-step-1 של הקוד.

5. יצירת סרגל התקדמות לגלריית התמונות

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

תגי העיצוב של הקרוסלה נראים כך:

src/index.html

<div class="gallery">
  <div class="gallery__scrollcontainer" style="--num-images: 3;">
    <div class="gallery__progress"></div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
  </div>
</div>

תמונות המפתח של סרגל ההתקדמות כבר מוכנות ונראות כך:

src/styles.css

@keyframes adjust-progress {
  from {
    transform: scaleX(calc(1 / var(--num-images)));
  }
  to {
    transform: scaleX(1);
  }
}

יש לצרף את האנימציה הזו אלרכיב gallery__progress עם ציר זמן של התקדמות גלילה. כמו שאפשר לראות בשלב הקודם, אפשר לעשות זאת באמצעות יצירה של ציר זמן אנונימי להתקדמות הגלילה באמצעות הפונקציה scroll():

src/styles.css

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  animation-timeline: scroll(nearest inline);
}

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

כדי לעקוף את הבעיה, צריך ליצור ציר זמן של התקדמות הגלילה ברכיב .gallery__scrollcontainer ולקשר את .gallery__progress אליו עם השם הזה.

כדי ליצור קו זמן של התקדמות גלילה ברכיב מסוים, צריך להגדיר את מאפיין ה-CSS scroll-timeline-name במאגר הגלילה לערך הרצוי. הערך חייב להתחיל ב---.

מכיוון שהגלריה נגללת לרוחב, צריך להגדיר גם את המאפיין scroll-timeline-axis. הערכים המותרים זהים לארגומנט <axis> של scroll().

לסיום, כדי לקשר את האנימציה לציר הזמן של התקדמות הגלילה, צריך להגדיר את המאפיין animation-timeline ברכיב שרוצים להוסיף לאנימציה לערך של המזהה שמשמש את scroll-timeline-name.

  • משנים את הקובץ styles.css כך שיכלול את הפרטים הבאים:

src/styles.css

.gallery__scrollcontainer {
  /* Create the gallery-is-scrolling timeline */
  scroll-timeline-name: --gallery-is-scrolling;
  scroll-timeline-axis: inline;
}

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  /* Set gallery-is-scrolling as the timeline */
  animation-timeline: --gallery-is-scrolling;
}

אימות השינויים

אם הכול היה תקין, עכשיו אמורה להיות לך:

אם לא, בודקים את ההסתעפות solution-step-2 של הקוד.

6. יוצרים אנימציה של תמונות הגלריה בזמן הכניסה לתבנית הגלילה ויציאה ממנה

הגדרה של ציר זמן אנונימי להתקדמות

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

כדי ליצור ציר זמן עם התקדמות צפייה, אפשר להשתמש בפונקציה view(). הארגומנטים הקבילים הם <axis> וגם <view-timeline-inset>.

  • הערך של <axis> זהה לערך של ציר הזמן של התקדמות הגלילה ומגדיר אחרי איזה ציר לעקוב.
  • באמצעות <view-timeline-inset>, ניתן לציין היסט (חיובי או שלילי) כדי להתאים את הגבולות כאשר רכיב נחשב להצגה או לא.
  • תמונות המפתח כבר מוכנות, לכן צריך רק לצרף אותן. כדי לעשות זאת, צריך ליצור ציר זמן של צפייה בהתקדמות בכל רכיב .gallery__entry.

src/styles.css

@keyframes animate-in {
  from {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  to {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
}

הגבלת הטווח של ציר זמן עם התקדמות צפייה

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

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

  1. כדי לטרגט רק את הטווח entry של הנושא, אפשר להשתמש במאפיין ה-CSS animation-range כדי להגביל את זמני ההפעלה של האנימציה.

src/styles.css

.gallery__entry {
  animation: linear fade-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry 0% entry 100%;
}

האנימציה רצה מ-entry 0% (הנושא עומד להיכנס לגלילה) עד entry 100% (הנושא נכנס במלואו לגלילה).

הטווחים האפשריים של תצוגת ציר הזמן הם:

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

אפשר להשתמש בכלי שנמצא בכתובת https://goo.gle/view-timeline-range-tool כדי לראות מה מייצג כל טווח ואיך האחוזים משפיעים על מיקומי ההתחלה והסיום.

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

src/styles.css

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry;
}
  • כדי לטשטש את התמונות כשהן יוצאות מפס הגלילה, אפשר לבצע את אותה פעולה כמו האנימציה המונפשת, אבל לטרגט לטווח שונה.

src/styles.css

@keyframes animate-out {
  from {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  to {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in, linear animate-out;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry, exit;
}

תמונות המפתח animate-in יחולו על הטווח entry ועל animate-out תמונות המפתח בטווח exit.

אימות השינויים

אם הכול היה תקין, עכשיו אמורה להיות לך:

אם לא, בודקים את ההסתעפות solution-step-3 של הקוד.

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

הכיסוי לקבוצה אחת של תמונות מפתח

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

הצורה של תמונות המפתח נראית כך:

@keyframes keyframes-name {
  range-name range-offset {
    ...
  }
  range-name range-offset {
    ...
  }
}
  1. אפשר לשלב את תמונות המפתח עמעום הדרגתי באופן הבא:

src/styles.css

@keyframes animate-in-and-out {
  entry 0% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  entry 90% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }

  exit 10% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  exit 100% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}
  1. כשפרטי הטווח נמצאים בתמונות מפתח, כבר לא צריך לציין את animation-range בנפרד. מחברים את תמונות המפתח כמאפיין animation.

src/styles.css

.gallery__entry {
  animation: linear animate-in-and-out both;
  animation-duration: auto;
  animation-timeline: view(inline);
}

אימות השינויים

אם הכול תקין, אמורה להיות אותה תוצאה כמו מהשלב הקודם. אם לא, בודקים את ההסתעפות solution-step-4 של הקוד.

8. מעולה!

סיימת את ה-Codelab הזה ועכשיו ידוע לך איך ליצור צירי זמן להתקדמות בגלילה ולראות את צירי הזמן של ההתקדמות ב-CSS.

מידע נוסף

מקורות: