ארבע תכונות 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 שניות, ואז הגדרת התצוגה ל'ללא'. בנוסף, מילת המפתח 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 ייחודי לכל כרטיס שנוצר.

סיכום

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