Animacja do wysokości: auto; (i inne kluczowe słowa dotyczące rozmiaru) w CSS

Użyj właściwości interpolate-size lub funkcji calc-size(), aby włączyć płynne przejścia i animacje z długości na słowa kluczowe z rozmiarem bezwzględnym i z powrotem.

Opublikowano: 17 września 2024 r.

Wprowadzenie

Często żądaną funkcją CSS jest możliwość animacji do height: auto. Nieznaczną odmianą tej prośby jest przejście na usługę width zamiast height lub na dowolną inną właściwą wielkość reprezentowaną przez słowa kluczowe takie jak min-content, max-contentfit-content.

Na przykład w tym demo dobrze byłoby, gdyby etykiety płynnie animowały się do naturalnej szerokości po najechaniu na ikony.

Używany kod 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 */
    }
}

Mimo że funkcja transition jest zadeklarowana do przejścia do właściwości width, a w elementach :hover jest zadeklarowana funkcja width: auto, nie następuje płynne przejście. Zamiast tego zmiana jest gwałtowna.

Umożliwia animowanie słów kluczowych o rozmiarze wewnętrznym za pomocą funkcji interpolate-size

Obsługa przeglądarek

  • Chrome: 129.
  • Edge: nieobsługiwane.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Właściwość CSS interpolate-size pozwala określić, czy animacje i przejścia z użyciem kluczowych słów dotyczących rozmiaru domyślnego w CSS mają być dozwolone.

Wartość domyślna to numeric-only, która nie umożliwia interpolacji. Gdy ustawisz tę właściwość na allow-keywords, akceptujesz interpolację z długości na słowa kluczowe z domyślnym rozmiarem CSS w przypadkach, gdy przeglądarka może animować te słowa kluczowe.

Zgodnie ze specyfikacją:

  • numeric-only: nie można interpolować <intrinsic-size-keyword>.
  • allow-keywords: można interpolować 2 wartości, jeśli jedna z nich jest wartością <intrinsic-size-keyword>, a druga – <length-percentage>. […]

Właściwość interpolate-size jest właściwością dziedziczną, więc możesz ją zadeklarować w elementach :root, aby umożliwić przejście z kluczowych słów z własną wielkością czcionki na kluczowe słowa z własną wielkością czcionki w całym dokumencie. To zalecane podejście.

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

W tym pokazie reguła jest dodawana do kodu. W rezultacie animacje do i z width: auto działają prawidłowo (w przeglądarkach z obsługą):

Ogranicz zasięg zgody na udział, zawężając selektor

Jeśli chcesz ograniczyć allow-keywords tylko do poddrzewa dokumentu, zmień selektor z :root na element, który chcesz uwzględnić w kierowaniu. Jeśli na przykład element <header> na Twojej stronie nie jest zgodny z tego typu przejściami, możesz ograniczyć zezwalanie tylko do elementu <main> i jego elementów podrzędnych w ten sposób:

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

Dlaczego nie zezwalać domyślnie na animację i wymiar słów kluczowych?

W przypadku tego mechanizmu często słyszymy, że przeglądarki powinny domyślnie zezwalać na przejścia i animacje z użyciem słów kluczowych z właściwymi rozmiarami do długości domyślnie.

Opcja włączenia tego zachowania została zbadana podczas opracowywania funkcji. Grupa robocza odkryła, że włączenie tej opcji domyślnie nie jest zgodne z wsteczną kompatybilnością, ponieważ wiele arkuszy stylów zakłada, że nie można animować słów kluczowych z właściwymi rozmiarami (takich jak auto lub min-content). Szczegółowe informacje znajdziesz w tym komentarzu do odpowiedniego problemu w grupie roboczej CSS.

Dlatego usługa jest opcjonalna. Dzięki właściwości dziedziczenia włączenie opcji w całym dokumencie jest tylko deklaracją interpolate-size: allow-sizes w :root, jak opisano wcześniej.

Umożliwia animowanie słów kluczowych o rozmiarze wewnętrznym za pomocą funkcji calc-size()

Obsługa przeglądarek

  • Chrome: 129.
  • Edge: 129.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Innym sposobem włączenia interpolacji do i z słów kluczowych z własną wielkością jest użycie funkcji calc-size(). Umożliwia ono bezpieczne i dobrze zdefiniowane wykonywanie obliczeń na podstawie rozmiarów bezwzględnych.

Funkcja przyjmuje 2 argumenty w tej kolejności:

  • Podstawa obliczeniowa, którą może być <intrinsic-size-keyword>, ale również zagnieżdżone calc-size().
  • Obliczenia wielkości obliczeń, które umożliwiają wykonywanie obliczeń na podstawie wielkości obliczeń. Aby odwołać się do podstawy calc-size, użyj słowa kluczowego size.

Oto przykłady:

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

Dodanie calc-size() do oryginalnego dema powoduje, że kod wygląda tak:

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

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

Wizualnie efekt jest dokładnie taki sam jak w przypadku użycia interpolate-size. W tym przypadku należy więc użyć interpolate-size.

calc-size() wyróżnia się możliwością wykonywania obliczeń, których nie można wykonać za pomocą 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

Jeśli na przykład chcesz, aby wszystkie akapity na stronie miały rozmiar zbliżony do wielokrotności wartości 50px, możesz użyć tego kodu:

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

Funkcja calc-size() umożliwia też interpolację między 2 wartościami calc-size(), gdy obie mają identyczne podstawy rozmiaru obliczeń. Tego też nie można osiągnąć za pomocą interpolate-size.

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

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

Dlaczego nie chcesz zezwolić na <intrinsic-size-keyword> w calc()?

Pytanie, które często pojawia się w odpowiedzi na pytanie calc-size(), brzmi: dlaczego grupa robocza ds. usług porównywania cen nie dostosowała funkcji calc() pod kątem obsługi słów kluczowych w zakresie rozmiaru wewnętrznego.

Jednym z powodów jest to, że podczas wykonywania obliczeń nie można mieszać ze sobą słów kluczowych z domyślnym rozmiarem. Możesz na przykład chcieć napisać calc(max-content - min-content), co wygląda na prawidłowe, ale w rzeczywistości tak nie jest. Funkcja calc-size() wymusza poprawność, ponieważ w przeciwieństwie do funkcji calc() jako pierwszy argument przyjmuje tylko jeden element typu <intrinsic-size-keyword>.

Innym powodem jest uwzględnianie kontekstu. Niektóre algorytmy układu działają w specyficzny sposób w przypadku określonych słów kluczowych z właściwym rozmiarem. Wartość calc-size() jest zdefiniowana w sposób jednoznaczny, aby reprezentować rozmiar bezwzględny, a nie <length>. Dzięki temu algorytmy mogą traktować calc-size(<intrinsic-size-keyword>, …) jako <intrinsic-size-keyword>, zachowując specjalne zachowanie tego słowa kluczowego.

Jakie podejście zastosować?

W większości przypadków należy zadeklarować interpolate-size: allow-keywords w elementach :root. Jest to najprostszy sposób włączenia animacji do i z wyszukiwania z użyciem słów kluczowych z właściwymi wymiarami, ponieważ wymaga tylko jednej linii kodu.

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

Ten fragment kodu stanowi dobre ulepszenie progresywne – przeglądarki, które go nie obsługują, wracają do braku przejść.

Jeśli potrzebujesz bardziej szczegółowej kontroli nad pewnymi działaniami, np. wykonywaniem obliczeń, lub chcesz użyć zachowania, które jest możliwe tylko dla calc-size(), możesz użyć calc-size().

#specific-element {
    width: 50px;

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

Jednak użycie w kodzie wartości calc-size() wymaga uwzględnienia wartości zastępczych dla przeglądarek, które nie obsługują wartości calc-size(). Możesz na przykład dodać deklaracje dodatkowych rozmiarów lub użyć funkcji wykrywania za pomocą atrybutu @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. */

Więcej wersji demonstracyjnych

Oto kilka demonstracji, które wykorzystują interpolate-size: allow-keywords.

Powiadomienia

Poniższe demo jest pochodną tego demo @starting-style. Kod został dostosowany, aby umożliwić dodawanie elementów o różnej wysokości.

Aby to osiągnąć, cała strona korzysta z interpolacji rozmiaru słowa kluczowego, a wartość height w każdym elemencie .item jest ustawiona na auto. W pozostałych miejscach kod jest dokładnie taki sam jak przed utworzeniem gałęzi.

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

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

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

Animowanie elementu <details>

Typowym przypadkiem użycia tego typu interpolacji jest animacja widżetu wyświetlającego informacje lub wyłącznie rozkładanej harmonijki podczas jej otwierania. W HTML używasz do tego elementu <details>.

Dzięki interpolate-size: allow-keywords możesz osiągnąć wiele:

@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 */
        }
    }
}

Jak widać, animacja jest odtwarzana tylko podczas otwierania widżetu. Aby to umożliwić, pracujemy nad pseudonimem ::details-content, który zostanie dodany do Chrome jeszcze w tym roku (omówimy go w jednym z kolejnych wpisów). Połączenie właściwości interpolate-size: allow-keywords i ::details-content umożliwia animację w obie strony: