Search

Scroll Snapping – שליטה בחווית הגלילה בעזרת CSS

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

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

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

CSS Scroll Snap – שימוש בסיסי

כפי שאתם יודעים באפשרותינו לגלול את העמוד או אלמנט כלשהו במספר דרכים: שימוש בעכבר, שימוש במקלדת, או החלקה על מסכי מגע למינהם (touch gestures).

בניגוד לסטנדרט, כלומר לחווית גלילה ליניארית בה הגלילה משקפת ישירות את תדירות המכשיר איתו ביצעת את הגלילה,  scroll snapping מאפשר ״לקפוץ״ (!Snap) לנקודה ספציפית במהלך הגלילה.

לצורך נוחות הכתיבה נתייחס לנקודות כנקודות קפיצה או snap points בלעז.

scroll snapping מתבצע על ידי קביעת התכונה scroll-snap-type על קונטיינר כלשהו (parent) ואת התכונה scroll-type-align על האלמנטים בתוך אותו קונטיינר (child elements).

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

<div class="snap-container">
  <section class="child"></section>
  <section class="child"></section>
  <section class="child"></section>
</div>
.snap-container {
  scroll-snap-type: y mandatory;
}

.child {
  scroll-snap-align: start;
}

כפי שציינו, תכונות ה scroll snapping מיושמות גם על הקונטיינר וגם על האלמנטים הנמצאים בו. ניתן לומר שאופן הפעולה זהה ל Flexbox או ל CSS Grid בהם אלמנט האב הופך להיות flex container או grid container. במקרה זה ניתן לומר שאלמנט האב הופך ל snap container.

לצורך נוחות הכתיבה נתייחס לאותו אלמנט אב כקונטיינר או snap container בלעז.

נסביר כעת על התכונה העיקרית המתייחסת לאלמנט האב בלבד, כלומר לקונטיינר…

התכונה scroll-snap-type

scroll-snap-type קובעת כמה ״קטנוניות״ יהיו אותן נקודות קפיצה, או במילים אחרות באיזו חומרה אלו יאכפו. מעבר לערך הראשון המציין את כיווניות הגלילה, כלומר גלילה אופקית או אנכית (x או y), קיים ערך שני היכול לקבל את הערכים  mendatory או proximity.

״mandatory״ .vs ״proximity״

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

באופן כללי, ניתן לומר ש proximity נכנס לפעולה כאשר משתמש מפסיק לגלול במרחק של פיקסלים מועטים יחסית מה snap point.

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

CSS Demo: scroll-snap-type

scroll-snap-type: none;
scroll-snap-type: x mandatory;
scroll-snap-type: x proximity;
1
2
3

אם אתם תוהים כיצד עיצבנו את פסי הגלילה תנו מבט בפוסט עיצוב פסי גלילה (Scrollbars) עם CSS.

התכונה scroll-snap-align

התכונה scroll-snap-align מתייחסת אך ורק לאלמנטים הנמצאים בתוך הקונטיינר (child elements). היא מאפשרת לכם לקבוע איזה חלק של האלמנט הוא שאמור לקפוץ לקונטיינר. התכונה יכולה לקבל ארבעה ערכים: none, start, center & end.

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

שימו לב! שינוי כיווניות המסמך משפיעה גם כן על תכונה זו. באתרים מימין לשמאל (RTL) הערך start יתייחס לקצה הימני.

בואו נראה דוגמאות שמסבירות ממש טוב על פועלה של תכונה זו (מומלץ בדסקטופ, במובייל יהיה קשה להבין את העקרון).

Heads up: It doesn't look like your browser supports scroll snapping! Check Can I use for current browser support. Maybe try opening this CodePen in a different browser, like Chrome?

CSS Demo: scroll-snap-align

scroll-snap-align: start;
scroll-snap-align: start;
scroll-snap-align: start;
scroll-snap-align: start;
scroll-snap-align: start;
scroll-snap-align: end;
scroll-snap-align: end;
scroll-snap-align: end;
scroll-snap-align: end;
scroll-snap-align: end;
scroll-snap-align: center;
scroll-snap-align: center;
scroll-snap-align: center;
scroll-snap-align: center;
scroll-snap-align: center;
scroll-snap-align: none;
scroll-snap-align: none;
scroll-snap-align: none;
scroll-snap-align: none;
scroll-snap-align: none;

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

.child {
  scroll-snap-align: start end;
}

בואו ניצור דוגמה קצת יותר פרקטית בכדי להבין יכולות.

דוגמה לשימוש ב scroll snap על מסך מלא

בואו ניתן דוגמה פרקטית יותר. תנו מבט ב HTML הבא:

<div class="scroll-container">
  <section>
    <h2>סקשיין 1</h2>
  </section>
  <section>
    <h2>סקשיין 2</h2>
  </section>
  <section>
    <h2>סקשיין 3</h2>
  </section>
  <section>
    <h2>סקשיין 4</h2>
  </section>
</div>

כפי שאתם כבר אמורים לדעת, scroll snapping דורש שתי תכונות CSS עיקריות: הראשונה sroll-snap-type על הקונטיינר – במקרה שלנו האלמנט עם הקלאס scroll-container. והשנייה scroll-snap-align לאלמנטים בתוך הקונטיינר (הילדים הישירים) – במקרה שלנו האלמנטים מסוג section.

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

.scroll-container {
  height: 100vh;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
}

section {
  height: 100vh;
  scroll-snap-align: center;
}

בדוגמה זו אנו קובעים את הכיווניות בתכונה scroll-snap-type ל y (כלומר גלילה אנכית) ואת הערך השני כ mendatory. זה אומר כי ברגע שהמשתמש יפסיק לגלול, מיקום הגלילה תמיד יקפוץ לנקודת הקפיצה הקרובה ביותר.

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

קביעת אנימציות או פיזיקה מדוייקת לאכיפת אותן נקודות קפיצה אינה מתקיימת בתכונה זו אלא נקבעת על ידי ה User Agent (הדפדפן) בו נעשה שימוש.

אם ישנו מצב בו התוכן שלכם עלול להיות גבוה יותר מהקונטיינר (במקרה זה 100vh), אז השימוש ב mendatory יכול להוות בעיה ולגרום לתחלקים מהתוכן להיות חבויים מעל או מתחת לחלק הנראה לעין, ולכן אינו מומלץ.

אך אם אתם יודעים כי התוכן תמיד יתאים ל viewport אז השימוש בערך mendatory יכול ליצור חווית משתמש יותר קונסיסטנטית:

See the Pen
Full Screen scroll-snap Demo
by Roee Yossef (@roeey)
on CodePen.

Intersection Observer API

תסכימו איתי כי עם CSS זה העמוד שלנוכבר מרגיש יותר כאפליקציית native. בכדי לשפר זאת אף יותר אתם יכולים להוסיף מעט אנימציות או transitions מבוססי גלילה. ניתן לעשות זאת על הטמעה של אפקט פרלקס (parallax scrolling effect) או לחילופין, ניתן להשתמש ב Intersection Observer API.

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

הרבה יותר יעיל מספריות המסתמכות על הקשבה תמידית ל scroll event.

אנו יכולים ליצור observer הצופה מתי כל אחד מהסקשיינים שלנו יוצא ונכנס ל viewport, למשל:

const sections = [...document.querySelectorAll('section')]

const options = {
  rootMargin: '0px',
  threshold: 0.25
}

const callback = (entries) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio >= 0.25) {
      target.classList.add("is-visible");
    } else {
      target.classList.remove("is-visible");
    }
  })
}

const observer = new IntersectionObserver(callback, options)

sections.forEach((section, index) => {
  observer.observe(section)
})

בדוגמה זו מופעלת פונקציית חזרה בכל פעם שסקשיין כלשהו מצטלב או בעצם כש 25% ממנו מצטלב עם הקונטיינר (treshold). כלומר כש 25% מהסקשיין נכנס ל viewport אנו מוסיפים לאותו סקשיין קלאס בשם is-visible ומסירים קלאס זה כשהסקשיין אינו עומד בהגדרה זו.

כעת אנו פשוט מוסיפים את האנימציה שלנו, למשל את ה CSS הבא:

section .content {
  opacity: 0:
}

section.is-visible .content {
  opacity: 1;
  transition: opacity 1000ms:
}

והנה הדמו של התוצאה הסופית:

See the Pen
Full Screen scroll-snap Demo – Intersection Observer
by Roee Yossef (@roeey)
on CodePen.

מה לגבי תמיכת דפדפנים?

Data on support for the css-snappoints feature across the major browsers from caniuse.com

התכונות scroll-snap-type ו scroll-snap-align נתמכות בתורה די טובה, כנ״ל לגבי ה Intersection Observer. אם נעטוף את כל אלו ב feature query (כלומר supports@), אנו יכולים לספק עתודה של חווית גלישה סטנדרטית עבור דפדפנים ישנים שאינם תומכים בתכונות אלו.

@supports (scroll-snap-type: y mandatory) {
  .scroll-container {
    height: 100vh;
    overflow-y: scroll;
    scroll-snap-type: y mandatory;
  }

  section {
    height: 100vh;
    scroll-snap-align: center;
  }
}

לסיכום

אם חשבתם ש CSS Scroll Snap נגמר בשתי התכונות שהזכרנו אז אתם טועים. קיימות תכונות בשם scroll-padding ו scroll-snap-stop עליהן כלל לא דיברנו, המשיכו לחקור בעצמכם.

נסכם ונאמר שעולם ה web הולך ומתקרב לאפליקציות native ומהווה אלטרנטיבה פתוחה ונגישה לאלו. ולמרות שתכונות CSS אלו אינן מהוות תחליף מלא לספריות ה javascript המאפשרות שליטה רבה יותר, לתכונות ה CSS יש יתרון משמעותי: פשטות ואמינות.

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

 

רועי יוסף
רועי יוסף

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

3 תגובות...
  • אלרון 20 ינואר 2021, 6:50

    ממש מגניב! לא הכרתי את זה לפני כן. לא ייאמן מה מתאפשר לעשות עם CSS לאורך הזמן…
    אבל משום מה כשאני גולל (ממש ממש בעדינות) זה קופץ לי ב־2 פריטים ולא בפריט אחד בכל פעם. כלומר פריט 1 הופך ל2 ואז מיד ל3.

  • אילה 29 יוני 2022, 11:47

    וואו קוד מעניין ממש!!
    איך אפשר להטמיע את הקוד בעמוד שנבנה באלמנטור וורדפרס?

    • רועי יוסף 29 יוני 2022, 11:57

      היי איילה,

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

תגובה חדשה

ניווט מהיר

Up!
לבלוג