ארבע תכונות CSS חדשות לאנימציות של כניסה ויציאה חלקות

אונה קראבץ
אונה קראבטס
ג'ואי ארהר
ג'ואי ארה

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

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

ארבע התכונות החדשות כוללות:

  • האפשרות להוסיף אנימציה ל-display ול-content-visibility בציר זמן בתמונת מפתח (מ-Chrome 116).
  • הנכס transition-behavior עם מילת המפתח allow-discrete כדי לאפשר מעברים של נכסים נפרדים כמו display (מ-Chrome 117).
  • הכלל @starting-style לאנימציה של אפקטים של כניסה מ-display: none ולשכבה העליונה (מ-Chrome 117).
  • המאפיין overlay לשליטה בהתנהגות של השכבה העליונה במהלך אנימציה (מ-Chrome 117). ## הצגת אנימציות בתמונות מפתח

החל מגרסה 116 של Chrome אפשר להשתמש ב-display וב-content-visibility בכללים של תמונות מפתח. האובייקטים האלה יוחלפו בזמן יצירת תמונת המפתח. לא נדרשים ערכים חדשים כדי לתמוך בכך:

.card {
  animation: fade-out 0.5s forwards;
}

@keyframes fade-out {
  100% {
    opacity: 0;
    display: none;
  }
}

בדוגמה הקודמת מוצגת אנימציה של מידת האטימות ל-0 למשך 0.5 שניות, ולאחר מכן הגדרת התצוגה כ-None. בנוסף, מילת המפתח forwards מבטיחה שהאנימציה תישאר במצב הסיום שלה, כך שהאלמנט שעליו היא חלה יישאר display: none ו-opacity: 0.

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

.card {
  animation: spin-and-delete 1s ease-in forwards;
}

@keyframes spin-and-delete {
  0% {
    transform: rotateY(0);
    filter: hue-rotate(0);
  }
  80% {
    transform: rotateY(360deg);
    filter: hue-rotate(180deg);
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
  }
}

האנימציה spin-and-delete היא אנימציית יציאה. קודם כל, הכרטיס יסתובב על ציר ה-y, יעבור סיבוב גוון, ולאחר מכן ב-80% לאורך ציר הזמן, השקיפות שלו תשתנה מ-1 ל-0. לבסוף, הכרטיס מתחלף מ-display: block ל-display: none.

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

.spin-out {
   animation: spin-and-delete 1s ease-in forwards;
}
document.querySelector('.delete-btn').addEventListener('click', () => {
 document.querySelector('.card').classList.add('spin-out');
})

בדוגמה שלמעלה יש עכשיו מצב סיום display:none. יש מקרים רבים שבהם כדאי להמשיך הלאה ולהסיר את צומת ה-DOM עם זמן קצוב לתפוגה, כדי לאפשר לאנימציה להסתיים ראשונה.

העברה של אנימציות נפרדות

בניגוד ליצירת אנימציה של מאפיינים נפרדים באמצעות תמונות מפתח, כדי להעביר מאפיינים נפרדים צריך להשתמש במצב התנהגות המעבר של allow-discrete.

הנכס transition-behavior

מצב allow-discrete מאפשר מעברים נפרדים, והוא ערך של המאפיין transition-behavior. אפשר להזין שני ערכים בשדה transition-behavior: normal ו-allow-discrete.

.card {
  transition: opacity 0.25s, display 0.25s;
  transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}

.card.fade-out {
  opacity: 0;
  display: none;
}
הערה: הדגמת המעבר הזו מציגה שיטה שונה מזו של הדגמת האנימציה הראשונה, אבל המראה שלה דומה.

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

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

אם יוצרים אנימציה של מספר נכסים נפרדים, צריך לכלול allow-discrete אחרי כל נכס שרוצים להוסיף לו אנימציה. לדוגמה:

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

הכלל @starting-style לאנימציות כניסה

עד עכשיו, המאמר הזה עוסק באנימציות של יציאה. כדי ליצור אנימציות כניסה, צריך להשתמש בכלל @starting-style.

יש להשתמש ב-@starting-style כדי להחיל סגנון שהדפדפן יכול לחפש לפני שהרכיב פתוח בדף. זהו המצב 'לפני הפתיחה' (כלומר, ממנו מתבצעת האנימציה).

/*  0. BEFORE-OPEN STATE   */
/*  Starting point for the transition */
@starting-style {
  .item {
    opacity: 0;
    height: 0;
  }
}

/*  1. IS-OPEN STATE   */
/*  The state at which the element is open + transition logic */
.item {
  height: 3rem;
  display: grid;
  overflow: hidden;
  transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete;
}

/*  2. EXITING STATE   */
/*  While it is deleting, before DOM removal in JS, apply this
    transformation for height, opacity, and a transform which
    skews the element and moves it to the left before setting
    it to display: none */
.is-deleting {
  opacity: 0;
  height: 0;
  display: none;
  transform: skewX(50deg) translateX(-25vw);
}

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

הנפשת רכיבים מהשכבה העליונה וממנה

כדי להוסיף אנימציה לרכיבים מהשכבה העליונה וממנה, יש לציין את @starting-style במצב 'פתוחה' כדי להורות לדפדפן מאיפה להתחיל את האנימציה. בתיבת דו-שיח, המצב הפתוח מוגדר באמצעות המאפיין [open]. כדי ליצור חלון קופץ, משתמשים בפסאודו מחלקה :popover-open.

דוגמה פשוטה לתיבת דו-שיח יכולה להיראות כך:

/*   0. BEFORE-OPEN STATE   */
@starting-style {
  dialog[open] {
    translate: 0 100vh;
  }
}

/*   1. IS-OPEN STATE   */
dialog[open] {
  translate: 0 0;
}

/*   2. EXIT STATE   */
dialog {
  transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete;
  translate: 0 100vh;
}

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

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

.settings-popover {
  &:popover-open {
    /*  0. BEFORE-OPEN STATE  */
    /*  Initial state for what we're animating *in* from, 
        in this case: goes from lower (y + 20px) to center  */
    @starting-style {
      transform: translateY(20px);
      opacity: 0;
    }
    
    /*  1. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;
  }
  
  /*  2. EXIT STATE  */
  /*  Initial state for what we're animating *out* to , 
      in this case: goes from center to (y - 50px) higher */
  transform: translateY(-50px);
  opacity: 0;
  
  /*  Enumerate transitioning properties, 
      including display and allow-discrete mode */
  transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete;
}

מלון אחד (overlay)

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

[open] {
  transition: opacity 1s, display 1s allow-discrete;
}

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

[open] {
  transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete;
}

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

הערה לגבי מעברים בין תצוגות

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

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

קודם כול, ב-CSS, מזינים view-transition-name נפרד לכל כרטיס.

.card-1 {
  view-transition-name: card-1;
}

.card-2 {
  view-transition-name: card-2;
}

/* etc. */

לאחר מכן, ב-JavaScript, עוטפים את המוטציה של DOM (במקרה הזה, מסירים את הכרטיס) במעבר בין תצוגה.

deleteBtn.addEventListener('click', () => {
  // Check for browser support
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      // DOM mutation
      card.remove();
    });
  } 
  // Alternative if no browser support
  else {
    card.remove();
  }
})

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

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

סיכום

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