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

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

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

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

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

הצגת אנימציות בתמונות מפתח

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

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

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

בדוגמה הקודמת, האטימות משתנה לאפס במהלך 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.

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

.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. 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;
}

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

/*  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. IS-OPEN STATE   */
dialog[open] {
  translate: 0 0;
}

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

/*   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. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;

    /*  1. 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;
    }
  }
  
  /*  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 גורמים לבריחה (escape) של קליפ אב וטרנספורמציות של אב, וגם מעבירים את התוכן לשכבה העליונה. אם לא תבצעו את המעבר 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 ייחודי לכל כרטיס שנוצר.

סיכום

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