ארבע תכונות 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 במשך 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

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

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

כדי להפעיל את המצב allow-discrete בנכס ספציפי, צריך לכלול אותו בקיצור הדרך transition:

.card {
  transition: opacity 0.25s, display 0.25s allow-discrete; /* Enable allow-discrete for the display property */
}

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

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

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

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

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

הכלל @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 ייחודי לכל כרטיס שנוצר.

סיכום

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