Tạo ảnh động cho height: auto; (và các từ khoá định cỡ nội tại khác) trong CSS

Sử dụng thuộc tính interpolate-size hoặc hàm calc-size() để bật các hiệu ứng chuyển đổi và ảnh động mượt mà từ độ dài sang từ khoá định cỡ nội tại và ngược lại.

Ngày xuất bản: 17 tháng 9 năm 2024

Giới thiệu

Một tính năng CSS thường được yêu cầu là khả năng tạo ảnh động cho height: auto. Một biến thể nhỏ của yêu cầu đó là chuyển đổi thuộc tính width thay vì height hoặc chuyển đổi sang bất kỳ kích thước nội tại nào khác được biểu thị bằng các từ khoá như min-content, max-contentfit-content.

Ví dụ: trong bản minh hoạ sau, sẽ rất tuyệt nếu các nhãn có thể tạo hiệu ứng ảnh động mượt mà theo chiều rộng tự nhiên khi di chuột qua các biểu tượng.

CSS được sử dụng như sau:

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

Mặc dù transition được khai báo để chuyển đổi thuộc tính widthwidth: auto được khai báo trên :hover, nhưng không có quá trình chuyển đổi nào diễn ra suôn sẻ. Thay vào đó, sự thay đổi diễn ra đột ngột.

Tạo ảnh động đến và từ các từ khoá định cỡ nội tại bằng interpolate-size

Hỗ trợ trình duyệt

  • Chrome: 129.
  • Edge: không được hỗ trợ.
  • Firefox: không được hỗ trợ.
  • Safari: không được hỗ trợ.

Nguồn

Thuộc tính interpolate-size CSS cho phép bạn kiểm soát việc có nên cho phép các ảnh động và hiệu ứng chuyển đổi của từ khoá định kích thước nội tại CSS hay không.

Giá trị mặc định của lớp này là numeric-only, không cho phép nội suy. Khi đặt thuộc tính thành allow-keywords, bạn chọn sử dụng nội suy từ độ dài đến từ khoá định cỡ nội tại CSS trong trường hợp trình duyệt có thể tạo ảnh động cho các từ khoá đó.

Theo thông số kỹ thuật:

  • numeric-only: Không thể nội suy <intrinsic-size-keyword>.
  • allow-keywords: Bạn có thể nội suy hai giá trị nếu một trong hai giá trị là <intrinsic-size-keyword> và giá trị còn lại là <length-percentage>. […]

Vì thuộc tính interpolate-size là thuộc tính kế thừa, nên bạn có thể khai báo thuộc tính này trên :root để bật tính năng chuyển đổi đến và từ các từ khoá định cỡ nội tại cho toàn bộ tài liệu. Đây là phương pháp đề xuất.

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

Trong bản minh hoạ sau, quy tắc này được thêm vào mã. Do đó, các ảnh động đến và đi từ width: auto sẽ hoạt động tốt (trong các trình duyệt hỗ trợ):

Giới hạn phạm vi tiếp cận của người dùng chọn tham gia bằng cách thu hẹp bộ chọn

Nếu bạn chỉ muốn giới hạn việc chọn sử dụng allow-keywords cho một cây con của tài liệu, hãy điều chỉnh bộ chọn từ :root thành chỉ phần tử mà bạn muốn nhắm đến. Ví dụ: trong trường hợp <header> của trang không tương thích với các loại hiệu ứng chuyển đổi này, bạn có thể chỉ giới hạn việc chọn sử dụng cho phần tử <main> và các phần tử con của phần tử đó như sau:

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

Tại sao chúng tôi không cho phép tạo ảnh động vào và thay đổi kích thước từ khoá theo mặc định?

Một ý kiến phản hồi phổ biến về cơ chế chọn sử dụng này là trình duyệt chỉ nên cho phép chuyển đổi và ảnh động từ từ khoá định cỡ nội tại sang độ dài theo mặc định.

Tuỳ chọn bật hành vi này đã được nghiên cứu trong quá trình phát triển tính năng. Nhóm làm việc phát hiện rằng việc bật tính năng này theo mặc định không tương thích ngược vì nhiều trang kiểu giả định rằng không thể tạo ảnh động cho các từ khoá định cỡ nội tại (chẳng hạn như auto hoặc min-content). Bạn có thể xem thông tin chi tiết trong bình luận này về vấn đề liên quan của Nhóm làm việc về CSS.

Do đó, cơ sở lưu trú là khách hàng chọn sử dụng. Nhờ đặc điểm kế thừa, việc chọn tham gia toàn bộ tài liệu chỉ đơn thuần là một thao tác khai báo interpolate-size: allow-sizes trên :root như đã nêu chi tiết trước đó.

Tạo ảnh động và thay đổi kích thước từ khoá nội tại bằng calc-size()

Hỗ trợ trình duyệt

  • Chrome: 129.
  • Edge: 129.
  • Firefox: không được hỗ trợ.
  • Safari: không được hỗ trợ.

Nguồn

Một cách khác để bật nội suy đối với và từ các từ khoá định kích thước nội tại là sử dụng hàm calc-size(). Phương thức này cho phép thực hiện các phép toán trên kích thước nội tại theo cách an toàn và được xác định rõ ràng.

Hàm này chấp nhận hai đối số, theo thứ tự:

  • Cơ sở kích thước tính toán, có thể là <intrinsic-size-keyword> nhưng cũng có thể là calc-size() lồng nhau.
  • Tính toán kích thước tính toán, cho phép bạn thực hiện các phép tính bằng cách sử dụng cơ sở kích thước tính toán. Để tham chiếu đến cơ sở kích thước calc, hãy sử dụng từ khoá size.

Dưới đây là một số ví dụ:

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

Thêm calc-size() vào bản minh hoạ ban đầu, mã sẽ có dạng như sau:

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

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

Về mặt hình ảnh, kết quả hoàn toàn giống như khi sử dụng interpolate-size. Vì vậy, trong trường hợp cụ thể này, bạn nên sử dụng interpolate-size.

calc-size() tỏa sáng ở khả năng tính toán, đây là điều không thể làm được với 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

Ví dụ: nếu muốn tất cả đoạn trên một trang được định kích thước theo bội số gần nhất của 50px, bạn có thể sử dụng cách sau:

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

Việc calc-size() cũng cho phép bạn làm là nội suy giữa hai calc-size() khi cả hai cơ sở có kích thước calc của chúng giống hệt nhau. Đây cũng là điều không thể thực hiện được với interpolate-size.

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

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

Tại sao không cho phép <intrinsic-size-keyword> trong calc()?

Một câu hỏi thường xuất hiện với calc-size() là tại sao Nhóm làm việc về CSS không điều chỉnh hàm calc() để hỗ trợ các từ khoá định cỡ nội tại.

Một trong những lý do này là bạn không được phép kết hợp và so khớp các từ khoá định kích thước nội tại khi thực hiện tính toán. Ví dụ: bạn có thể muốn viết calc(max-content - min-content) trông có vẻ hợp lệ, nhưng thực tế thì không phải vậy. calc-size() thực thi tính chính xác vì không giống như calc(), hàm này chỉ chấp nhận một <intrinsic-size-keyword> duy nhất làm đối số đầu tiên.

Một lý do khác là khả năng nhận biết theo bối cảnh. Một số thuật toán bố cục có hành vi đặc biệt đối với các từ khoá định kích thước nội tại cụ thể. calc-size() được xác định rõ ràng để biểu thị kích thước nội tại, chứ không phải <length>. Nhờ đó, các thuật toán đó có thể coi calc-size(<intrinsic-size-keyword>, …)<intrinsic-size-keyword>, duy trì hành vi đặc biệt của từ khoá đó.

Nên sử dụng phương pháp nào?

Trong hầu hết các trường hợp, hãy khai báo interpolate-size: allow-keywords trên :root. Đây là cách dễ nhất để bật ảnh động cho và từ khoá định kích thước nội tại vì đây là từ khoá một lần.

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

Đoạn mã này là một điểm cải tiến dần dần thú vị, vì những trình duyệt không hỗ trợ đoạn mã này sẽ quay lại sử dụng không có hiệu ứng chuyển đổi.

Khi cần kiểm soát chi tiết hơn đối với các hoạt động (chẳng hạn như tính toán) hoặc bạn muốn sử dụng một hành vi mà chỉ calc-size() mới có thể thực hiện, bạn có thể sử dụng calc-size().

#specific-element {
    width: 50px;

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

Tuy nhiên, khi sử dụng calc-size() trong mã, bạn sẽ phải thêm các phương án dự phòng cho những trình duyệt không hỗ trợ calc-size(). Ví dụ: thêm các nội dung khai báo kích thước bổ sung hoặc quay lại phát hiện tính năng bằng @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. */

Bản minh hoạ khác

Dưới đây là một số bản minh hoạ khác sử dụng interpolate-size: allow-keywords để phát huy lợi thế.

Thông báo

Bản minh hoạ sau đây là một nhánh của bản minh hoạ @starting-style này. Mã này được điều chỉnh để cho phép thêm các mục có chiều cao khác nhau.

Để đạt được điều này, toàn bộ trang chọn sử dụng nội suy từ khoá theo kích thước và height trên mỗi phần tử .item được đặt thành auto. Nếu không, mã sẽ giống hệt như trước khi phân nhánh.

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

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

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

Tạo ảnh động cho phần tử <details>

Một trường hợp sử dụng thông thường mà bạn muốn sử dụng loại nội suy này là tạo ảnh động cho tiện ích thông tin công bố hoặc bảng điều khiển độc quyền khi mở. Trong HTML, bạn sử dụng phần tử <details> cho việc này.

Với interpolate-size: allow-keywords, bạn có thể làm được nhiều việc:

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

Tuy nhiên, như bạn có thể thấy, ảnh động chỉ chạy khi tiện ích thông tin đang mở. Để giải quyết vấn đề này, Chrome đang phát triển ::details-content giả lập sẽ được cung cấp trong Chrome vào cuối năm nay (và sẽ được đề cập trong một bài đăng sau này). Khi kết hợp interpolate-size: allow-keywords::details-content, bạn có thể nhận được ảnh động theo cả hai hướng: