אנימציה לגובה: auto; (ומילות מפתח אחרות של שינוי גודל מהותי) ב-CSS

אפשר להשתמש במאפיין interpolate-size או בפונקציה calc-size() כדי להפעיל מעברים ואנימציות חלקות מאורכים למילות מפתח עם הגדרת גודל מובנית ובחזרה.

תאריך פרסום: 17 בספטמבר 2024

מבוא

תכונה של CSS שמופיעה לעיתים קרובות בבקשות היא היכולת ליצור אנימציה ל-height: auto. וריאציה קלה על הבקשה הזו היא מעבר של המאפיין width במקום height, או מעבר לכל אחד מהגדלים המובנים האחרים שמיוצגים על ידי מילות מפתח כמו min-content,‏ max-content ו-fit-content.

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

קוד ה-CSS שבו נעשה שימוש הוא:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease; /* 👈 Transition the width */

    &:hover,
    &:focus-visible {
        width: max-content; /* 👈 Doesn't work with transitions */
    }
}

למרות שהוצהר על transition להעברת הנכס width, ו-width: auto מוצהר ב-:hover, לא מתרחש מעבר חלק. במקום זאת, השינוי הוא פתאומי.

אנימציה של מעבר ממילות מפתח עם שינוי גודל פנימי אל מילות מפתח עם שינוי גודל פנימי וחזרה באמצעות interpolate-size

תמיכה בדפדפן

  • Chrome:‏ 129.
  • Edge: לא נתמך.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

הנכס interpolate-size ב-CSS מאפשר לכם לקבוע אם תהיה אפשרות להשתמש באנימציות ובמעבריים של מילות מפתח פנימיות של CSS לבחירת גודל.

ערך ברירת המחדל שלו הוא numeric-only, שלא מפעיל את האינטרפולציה. כשמגדירים את המאפיין כ-allow-keywords, מביעים הסכמה לשימוש באינטרפולציות מאורך למילות מפתח של CSS עם הגדרות גודל מובנות, במקרים שבהם הדפדפן יכול ליצור אנימציה של מילות המפתח האלה.

לפי המפרט:

  • numeric-only: לא ניתן לבצע אינטרפולציה של <intrinsic-size-keyword>.
  • allow-keywords: אפשר לבצע אינטרפולציה של שני ערכים אם אחד מהם הוא <intrinsic-size-keyword> והשני הוא <length-percentage>. […]

מאחר שהנכס interpolate-size הוא נכס יורש, ניתן להצהיר עליו ב-:root כדי לאפשר את המעבר למילות מפתח מהותיות בעלות מידה במסמך כולו. זו הגישה המומלצת.

/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

בדמו הבא, הכלל הזה מתווסף לקוד. כתוצאה מכך, האנימציות אל width: auto וממנו פועלות כמו שצריך (בדפדפנים עם תמיכה):

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

אם רוצים להגביל את ההסכמה ל-allow-keywords רק להסתעפות עץ משנית של המסמך, משנים את הבורר מ-:root רק לרכיב שרוצים לטרגט. לדוגמה, אם הרכיב <header> בדף לא תואם לסוגים האלה של מעברים, אפשר להגביל את ההסכמה רק לרכיב <main> ולצאצאיו באופן הבא:

main { /* 👈 Scope the opt-in to only <main> and its descendants */
    interpolate-size: allow-keywords;
}

למה לא מאפשרים אנימציה ממילת מפתח לבחירת גודל וממנה כברירת מחדל?

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

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

לכן, הנכס הוא נכס שבו צריך להביע הסכמה. בזכות מאפיין הירושה, הסכמה למסמך שלם היא רק הצהרת interpolate-size: allow-sizes ב-:root, כפי שמפורט למעלה.

אנימציה מעבר למילות מפתח עם שינוי גודל מובנה ומהן באמצעות calc-size()

תמיכה בדפדפנים

  • Chrome:‏ 129.
  • Edge:‏ 129.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

דרך נוספת להפעיל אינטרפולציה אל מילת מפתח עם שינוי גודל מובנה וממנה היא להשתמש בפונקציה calc-size(). הוא מאפשר לבצע פעולות מתמטיות על גדלים מהותיים בצורה בטוחה ומוגדרת היטב.

הפונקציה מקבלת שני ארגומנטים, לפי הסדר:

  • בסיס calc-size, שיכול להיות <intrinsic-size-keyword> אבל גם calc-size() בתצוגת עץ.
  • חישוב גודל, שמאפשר לבצע חישובים על בסיס שיטת החישוב. כדי להתייחס לבסיס החישוב של הגודל, צריך להשתמש במילת המפתח size.

הנה כמה דוגמאות:

width: calc-size(auto, size);        // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered

בהוספת calc-size() להדגמה המקורית, הקוד נראה כך:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease;

    &:hover,
    &:focus-visible {
        width: calc-size(max-content, size); /* 👈 */
    }
}

מבחינה חזותית, התוצאה זהה לזו שמתקבלת כשמשתמשים ב-interpolate-size. לכן במקרה הספציפי הזה צריך להשתמש ב-interpolate-size.

הנקודה שבה calc-size() זורחת נובעת מהיכולת שלו לבצע חישובים, וזה משהו שלא ניתן לבצע באמצעות interpolate-size:

width: calc-size(auto, size - 10px); // = The auto width minus 10 pixels
width: calc-size(min-content, size + 1rem); // = The min-content width plus 1rem
width: calc-size(max-content, size * .5);   // = Half the max-content width

לדוגמה, אם רוצים שכל הקטעים בדף יהיו בגודל של מכפיל הקרוב ביותר של 50px, אפשר להשתמש בקוד הבא:

p {
    width: calc-size(fit-content, round(up, size, 50px));
    height: calc-size(auto, round(up, size, 50px));
}

נוסחת calc-size() מאפשרת גם לבצע אינטרפולציה בין שתי נוסחאות calc-size() כששתי הבסיסות שלהן לחישוב הגודל זהות. גם זה לא ניתן להשיג באמצעות interpolate-size.

#element {
    width: min-content; /* 👈 */
    transition: width 0.35s ease;

    &:hover {
        width: calc-size(min-content, size + 10px); /* 👈 */
    }
}

למה לא לאשר <intrinsic-size-keyword> בcalc()?

שאלה נפוצה לגבי calc-size() היא למה קבוצת העבודה של CSS לא שינתה את הפונקציה calc() כדי לתמוך במילות מפתח של תצוגה בגודל מובנה.

אחת הסיבות לכך היא שאסור לשלב בין מילות מפתח לטירגוט לפי גודל פנימי כשמבצעים חישובים. לדוגמה, יכול להיות שתתפתו לכתוב calc(max-content - min-content) שנראה תקין, אבל בפועל הוא לא תקין. calc-size() אוכפת תיקון כי היא, שלא כמו calc(), מקבלת רק <intrinsic-size-keyword> יחיד כארגומנט הראשון.

סיבה נוספת היא מוּדעוּת להקשר. לאלגוריתמים מסוימים של פריסה יש התנהגות מיוחדת למילות מפתח ספציפיות לבחירת גודל מודעה מובנית. calc-size() מוגדר במפורש לייצוג גודל מהותי, ולא <length>. כך, האלגוריתמים האלה יכולים להתייחס ל-calc-size(<intrinsic-size-keyword>, …) כאל <intrinsic-size-keyword>, תוך שמירה על ההתנהגות המיוחדת שלו למילת המפתח הזו.

באיזו גישה להשתמש?

ברוב המקרים, מגדירים את interpolate-size: allow-keywords ב-:root. זו הדרך הפשוטה ביותר להפעיל אנימציה אל מילות מפתח עם שינוי גודל פנימי ומהן, כי היא בעצם שורה אחת.

/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

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

כשצריך שליטה ברמת פירוט גבוהה יותר – למשל, ביצוע חישובים – או כשרוצים להשתמש בהתנהגות שרק calc-size() יכול לבצע, אפשר להשתמש ב-calc-size().

#specific-element {
    width: 50px;

    &:hover {
        width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
    }
}

עם זאת, שימוש ב-calc-size() בקוד ידרוש ממך לכלול חלופות לדפדפנים שלא תומכים ב-calc-size(). לדוגמה, הוספת הצהרות נוספות לגבי גודל או חזרה לזיהוי תכונות באמצעות @supports.

width: fit-content;
width: calc-size(fit-content, size + 1em);
       /* 👆 Browsers with no calc-size() support will ignore this second declaration,
             and therefore fall back to the one on the line before it. */

הדגמות נוספות

הנה כמה הדגמות נוספות שבהן נעשה שימוש ב-interpolate-size: allow-keywords.

התראות

ההדגמה הבאה היא שילוב של ההדגמה הזו של @starting-style. הקוד הותאם כדי לאפשר הוספה של פריטים בגבהים שונים.

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

:root {
    interpolate-size: allow-keywords; /* 👈 */
}

.item {
    height: auto; /* 👈 */

    @starting-style {
        height: 0px;
    }
}

הנפשת הרכיב <details>

תרחיש לדוגמה שבו כדאי להשתמש בסוג הזה של אינטרפולציה הוא כדי ליצור אנימציה לווידג'ט של גילוי נאות או לאקורדיון בלעדי בזמן שהוא נפתח. ב-HTML, משתמשים לצורך כך ברכיב <details>.

בעזרת interpolate-size: allow-keywords אפשר להגיע רחוק:

@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }
    
    details {
        transition: height 0.5s ease;
        height: 2.5rem;
        
        &[open] {
            height: auto;
            overflow: clip; /* Clip off contents while animating */
        }
    }
}

עם זאת, כפי שאפשר לראות, האנימציה פועלת רק כשחלונית הווידג'ט של גילוי הנאות נפתחת. כדי לטפל בבעיה הזו, אנחנו עובדים ב-Chrome על ::details-content pseudo שיגיע ל-Chrome בהמשך השנה (ונתאר אותו בפרסום עתידי). שילוב של interpolate-size: allow-keywords ו-::details-content יוצר אנימציה בשני הכיוונים: