אינדיקטור התקדמות הגלילה הוא דפוס נפוץ של ממשק משתמש שמעביר באופן חזותי את התקדמות המשתמש במסמך או במאגר שניתן לגלול בו. כשהמשתמש גולל, רכיב חזותי מתעדכן כדי לשקף את המיקום שלו, וכך מספק תחושה ברורה ואינטואיטיבית לגבי כמות התוכן שהוא כבר צפה בה וכמה תוכן נשאר.
איך מטמיעים
כדי ליצור אינדיקטור להתקדמות הגלילה, צריך שני דברים:
- רכיב שישמש כסרגל ההתקדמות. הרכיב הזה הוא בדרך כלל
position: fixedאוposition: absolute, כדי שהוא יישאר גלוי כשהמשתמש גולל. - אנימציה שמקושרת למיקום הגלילה.
כך אפשר לעשות את זה:
- קודם כל, יוצרים רכיב HTML שישמש כסרגל ההתקדמות. אפשר לעצב את הרכיב הזה לפי הטעם שלכם.
- בשלב הבא, מגדירים ב-CSS אנימציה של
@keyframesשמשנה את הגודל של סרגל ההתקדמות. גישה נפוצה היא לשנות את קנה המידה של הרכיב מ-scaleX(0)ל-scaleX(1). - לבסוף, מחילים את האנימציה הזו על רכיב סרגל ההתקדמות ומגדירים את
animation-timelineשלו לציר זמן של גלילה. כך הדפדפן יודע להפעיל את האנימציה על סמך מיקום הגלילה של רכיב הגלילה הקרוב ביותר ברמה גבוהה יותר בהיררכיה.
קוד לדוגמה
הקוד הזה מגדיל את הרכיב #progress בגלילה באמצעות ציר זמן אנונימי של גלילה, שנוצר על ידי הפונקציה scroll().
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: scroll();
}
}
}
בגלל המיקום שלו ב-DOM, הפונקציה scroll() תעקוב אחרי רכיב הגלילה הקרוב ביותר ברמה גבוהה יותר בהיררכיה בכיוון block, שכאן הוא רכיב הגלילה ברמה הבסיסית (root).
<body>
<!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
<div id="progress" aria-hidden="true"></div>
</body>
הקוד הזה מגדיל את הרכיב #progress בגלילה באמצעות ציר זמן של גלילה עם שם, שנוצר על ידי המאפיין scroll-timeline.
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
:root {
scroll-timeline: --tl block;
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: --tl;
}
}
}
שיטות מומלצות
כשמשתמשים באנימציות מבוססות-גלילה, חשוב לפעול לפי כמה שיטות מומלצות כדי להבטיח חוויה חלקה ונגישה:
- מומלץ לכלול זיהוי תכונות: לא כל הדפדפנים תומכים באנימציות מבוססות-גלילה. כדאי להשתמש ב-
@supports (animation-timeline: scroll())כדי לבדוק אם יש תמיכה, ולספק חלופה לדפדפנים שלא תומכים בזה.- אל תשתמשו בחבילה
scroll-timeline-polyfillכאסטרטגיית גיבוי, כי היא לא כוללת את כל התכונות ויש בה הרבה בעיות ידועות. - אם האנימציה נחשבת רק לקישוט, כדאי לבחור בשיפור הדרגתי (progressive enhancement) ולא לספק חלופה.
- אל תשתמשו בחבילה
- מומלץ להסיר רכיבים דקורטיביים בלבד מתהליכי הקריאה של טכנולוגיה מסייעת: צריך להחיל את
aria-hidden="true"על אינדיקטורים חזותיים בלבד של גלילה כדי לוודא שקוראי מסך לא נתקלים בצמתים ריקים ללא שם. - חשוב לכבד את העדפות המשתמשים: חלק מהמשתמשים מעדיפים פחות תנועה באינטרנט. כדי להשבית או לצמצם את האנימציות למשתמשים האלה, צריך להשתמש בשאילתת המדיה
prefers-reduced-motion. - מומלץ לנסות להנפיש רק מאפייני CSS עם ביצועים גבוהים: כדי שהאנימציות יהיו חלקות ככל האפשר, כדאי להנפיש רק מאפיינים שאפשר לטפל בהם בשרשור ה-compositor של הדפדפן, כמו
transformו-opacity. הנפשה של מאפיינים אחרים כמוwidthאוheightעלולה לגרום לבעיות בביצועים. - מומלץ להשתמש בסדר ההצהרה הנכון: כשמשתמשים במאפיין המקוצר
animation, צריך להצהיר עלanimation-timelineאחריו כדי למנוע מהמאפיין המקוצר לאפס את ציר הזמן.
כשמשתמשים בפונקציה scroll() כדי ליצור אנימציה שמופעלת בגלילה:
- אופציונלי: אם לא מטרגטים את רכיב הגלילה הקרוב ביותר, צריך לציין במפורש באיזה רכיב גלילה רוצים להשתמש באמצעות
scroll(root)אוscroll(self).- אם
root,nearestאוselfלא מספיקים, צריך להשתמש בציר זמן של גלילה עם שם.
- אם
- אופציונלי: ציינו במפורש את הציר שצריך לעקוב אחריו. אם אתם לא מטרגטים את הציר
blockשמוגדר כברירת מחדל (למשל, בסרגל גלילה אופקי), ציינו במפורש את הציר שצריך לעקוב אחריו באמצעותscroll(block)אוscroll(inline).
כשמשתמשים במאפיין scroll-timeline כדי ליצור אנימציה שמופעלת בגלילה:
- מומלץ להשתמש ב-
<dashed-ident>של CSS בשם. - אופציונלי: מציינים במפורש את הציר למעקב: כשלא מטרגטים את ציר ברירת המחדל
block(למשל, בסרגל גלילה אופקי), מציינים במפורש את הציר למעקב באמצעותscroll-timeline-axis. - חשוב לוודא שהיקף החיפוש פועל: כשהרכיב שמצהיר על
scroll-timelineהוא לא רכיב אב בעץ שטוח של הרכיב המונפש, צריך להגדיל את החשיפה של השם שלscroll-timelineבאמצעותtimeline-scopeברכיב אב משותף.
אסטרטגיות חלופיות
בדפדפנים שלא תומכים באנימציות מבוססות-גלילה, אפשר להשתמש ב-fallback כדי ליצור מחדש את האפקטים החזותיים. החלופות בדרך כלל נוצרות באמצעות מאזין גלילה (לאפקטים של ScrollTimeline) או IntersectionObserver API (לאפקטים של ViewTimeline).
בדפדפנים עם תמיכה מובנית באנימציות מבוססות-גלילה, תמיד צריך להשתמש בהטמעה המקורית של CSS כי היא יעילה יותר.
חשוב לזכור שלא כל אפקט אפשר ליצור מחדש באמצעות גישת החלופות.
במקרה הספציפי הזה, הסקריפט הבא מחיל את החלופה לדפדפנים שלא תומכים באנימציות מבוססות-גלילה. הוא משתמש במאזין גלילה כדי לעקוב אחרי מיקום הגלילה של רכיב הבסיס, ומעדכן את המאפיין transform של סרגל ההתקדמות בהתאם.
<script>
if (!CSS.supports('animation-timeline', 'scroll()')) {
const progress = document.querySelector('#progress');
window.addEventListener('scroll', () => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progressPercentage = (scrolled / scrollable);
progress.style.transform = `scaleX(${progressPercentage})`;
});
}
</script>