כותרת שמתכווצת בגלילה היא דפוס נפוץ בממשק המשתמש, שבו רכיב כותרת קבוע בחלק העליון של הדף עובר בצורה חלקה לגודל קטן יותר כשהמשתמש גולל למטה. האפקט הזה משמש לעיתים קרובות כדי להגדיל את שטח המסך לתוכן הראשי, תוך שמירה על נגישות של רכיבי ניווט או גורמי ייחודיות חיוניים. באמצעות אנימציות מבוססות-גלילה ב-CSS, אפשר להשיג את האפקט הזה בצורה הצהרתית וביצועית, על ידי קישור של אנימציה למיקום הגלילה של המסמך.
איך מטמיעים
כך יוצרים כותרת שמתכווצת בגלילה:
יוצרים כותרת עליונה קבועה: מתחילים עם רכיב של כותרת עליונה שקבוע בחלק העליון של הדף ומוגדר לו גובה מראש.
<header>HEADER</header>header { position: fixed; height: 200px; top: 0; left: 0; right: 0; }מגדירים את אנימציית הכיווץ: יוצרים אנימציית CSS שמשנה את הגובה של הכותרת.
@keyframes shrink { to { height: 50px; } }החלת האנימציה וציר הזמן של הגלילה: מצמידים את האנימציה לכותרת ומשתמשים בפונקציה
scroll()כדי לקשר אותה למיקום הגלילה במסמך.header { animation: shrink auto linear both; animation-timeline: scroll(block root); }מגדירים את
animation-range: משתמשים במאפייןanimation-rangeכדי לציין את מרחק הגלילה שבו האנימציה אמורה להתרחש. לדוגמה, כדי לכווץ את הכותרת ב-150 הפיקסלים הראשונים של הגלילה, צריך להשתמש ב-animation-range: 0px 150px;.header { animation-range: 0px 150px; }
טיפ: כדי למנוע מצב שבו הכותרת מסתירה את התוכן שמופיע אחריה, מוסיפים padding-top ל-body (או למאגר התוכן הראשי) ששווה לגובה הראשוני של הכותרת.
טיפ: כדי לוודא שהתוכן של הדף נגלל בסנכרון עם הכותרת המתכווצת, מגדירים את animation-range-end להפרש בין הגודל ההתחלתי לגודל הסופי. כך האנימציה מסתיימת בדיוק כשהכותרת מגיעה לגודל הסופי שלה. בהדגמה הזו, הכותרת מתכווצת מ-200px ל-50px, ולכן הערך של animation-range-end מוגדר ל-150px.
קוד לדוגמה
@keyframes shrink {
to {
height: 50px;
}
}
header {
animation: shrink auto linear both;
animation-timeline: scroll(block root);
animation-range: 0px 150px;
}
שיטות מומלצות
כשמשתמשים באנימציות מבוססות-גלילה, חשוב לפעול לפי כמה שיטות מומלצות כדי להבטיח חוויה חלקה ונגישה:
- מומלץ לכלול זיהוי תכונות: לא כל הדפדפנים תומכים באנימציות מבוססות-גלילה. כדאי להשתמש ב-
@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%))כדי לבדוק אם יש תמיכה, ולספק חלופה לדפדפנים שלא תומכים בזה.- חובה לכלול כאן את הבדיקה של
(animation-range: 0% 100%)כדי לסנן דפדפנים עם תמיכה חלקית בלבד. - אל תשתמשו בחבילה
scroll-timeline-polyfillכאסטרטגיית גיבוי, כי היא לא כוללת את כל התכונות ויש בה הרבה בעיות ידועות. - אם האנימציה נחשבת רק לקישוט, כדאי לבחור בשיפור הדרגתי (progressive enhancement) ולא לספק חלופה.
- חובה לכלול כאן את הבדיקה של
- חשוב לכבד את העדפות המשתמשים: חלק מהמשתמשים מעדיפים פחות תנועה באינטרנט. כדי להשבית או לצמצם את האנימציות למשתמשים האלה, צריך להשתמש בשאילתת המדיה
prefers-reduced-motion. - מומלץ לנסות להנפיש רק מאפייני CSS עם ביצועים גבוהים: כדי שהאנימציות יהיו חלקות ככל האפשר, כדאי להנפיש רק מאפיינים שאפשר לטפל בהם בשרשור ה-compositor של הדפדפן, כמו
transformו-opacity. הנפשה של מאפיינים אחרים כמוwidthאוheightעלולה לגרום לבעיות בביצועים. - כן: משתמשים בסדר ההצהרה הנכון. כשמשתמשים במאפיין המקוצר
animation, מצהירים עלanimation-timelineו-animation-rangeאחריו כדי למנוע מהמאפיין המקוצר לאפס את ציר הזמן.
כשמשתמשים בפונקציה scroll() כדי ליצור אנימציה שמופעלת בגלילה:
- אופציונלי: אם לא מטרגטים את רכיב הגלילה הקרוב ביותר, צריך לציין במפורש באיזה רכיב גלילה רוצים להשתמש באמצעות
scroll(root)אוscroll(self).- אם
root,nearestאוselfלא מספיקים, צריך להשתמש בציר זמן של גלילה עם שם.
- אם
- אופציונלי: ציינו במפורש את הציר שצריך לעקוב אחריו. אם אתם לא מטרגטים את הציר
blockשמוגדר כברירת מחדל (למשל, בסרגל גלילה אופקי), ציינו במפורש את הציר שצריך לעקוב אחריו באמצעותscroll(block)אוscroll(inline).
במקרה הספציפי הזה:
- הרכיב שמוסיפים לו אנימציה לא יכול להיות
position: staticאוposition: relativeכשמשתמשים באחוזים ב-animation-range.- הסיבה לכך היא שהרכיבים האלה נחשבים כ"בתוך הזרימה". כיווץ הרכיבים האלה בזמן הגלילה יקטין את מרחק הגלילה הכולל, וכך ישפיע על הערך המחושב של — לדוגמה —
10%לתוך הגלילה.
- הסיבה לכך היא שהרכיבים האלה נחשבים כ"בתוך הזרימה". כיווץ הרכיבים האלה בזמן הגלילה יקטין את מרחק הגלילה הכולל, וכך ישפיע על הערך המחושב של — לדוגמה —
תמיכה בדפדפנים ושיטות חלופיות
לכן, בדרך כלל נדרשת אסטרטגיית גיבוי.
בדפדפנים שלא תומכים באנימציות מבוססות-גלילה, אפשר להשתמש ב-fallback כדי ליצור מחדש את האפקטים החזותיים. החלופות בדרך כלל נוצרות באמצעות מאזין גלילה (לאפקטים של ScrollTimeline) או IntersectionObserver API (לאפקטים של ViewTimeline).
בדפדפנים עם תמיכה מובנית באנימציות מבוססות-גלילה, תמיד צריך להשתמש בהטמעה המקורית של CSS כי היא יעילה יותר.
חשוב לזכור שלא כל אפקט אפשר ליצור מחדש באמצעות גישת החלופות.
במקרה הספציפי הזה, הסקריפט הבא מחיל את החלופה לדפדפנים שלא תומכים באנימציות מבוססות-גלילה. הוא משתמש ב-scroll listener כדי לעקוב אחרי מיקום הגלילה של המסמך לאורך מרחק של 150px ומעדכן את גובה הכותרת בהתאם.
// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: scroll()) and (animation-range: 0% 100%)')) {
const header = document.querySelector('header');
const initialHeight = 200;
const finalHeight = 50;
const scrollDistance = 150;
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
const scrollPercent = Math.min(1, scrollY / scrollDistance);
const newHeight = initialHeight - (initialHeight - finalHeight) * scrollPercent;
header.style.height = `${newHeight}px`;
});
}