calc-size()

Limited availability

This feature is not Baseline because it does not work in some of the most widely-used browsers.

Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.

Hàm calc-size() trong CSS cho phép bạn thực hiện tính toán trên các giá trị intrinsic size (kích thước nội tại) như auto, fit-content, và max-content; điều này không được hỗ trợ bởi hàm calc() thông thường.

Các giá trị trả về của calc-size() cũng có thể được nội suy, cho phép các giá trị từ khóa kích thước được sử dụng trong animationstransitions. Trên thực tế, việc đưa calc-size() vào giá trị thuộc tính sẽ tự động áp dụng interpolate-size: allow-keywords cho lựa chọn đó.

Tuy nhiên, lưu ý rằng interpolate-size được kế thừa, do đó áp dụng nó cho một phần tử sẽ kích hoạt nội suy các từ khóa kích thước nội tại cho mọi thuộc tính được áp dụng cho phần tử đó và các phần tử con của nó. Do đó, interpolate-size là giải pháp ưu tiên để kích hoạt các animation kích thước nội tại. Bạn chỉ nên sử dụng calc-size() để kích hoạt các animation kích thước nội tại nếu chúng cũng yêu cầu tính toán.

Cú pháp

css
/* Truyền một giá trị qua calc-size() */
calc-size(auto, size)
calc-size(fit-content, size)

/* Thực hiện tính toán */
calc-size(min-content, size + 100px)
calc-size(fit-content, size / 2)

/* Tính toán bao gồm một hàm */
calc-size(auto, round(up, size, 50px))

Tham số

Cú pháp của hàm calc-size() như sau:

calc-size(<calc-size-basis>, <calc-sum>)

Các tham số là:

<calc-size-basis>

Giá trị (thường là kích thước nội tại) mà bạn muốn thực hiện tính toán trên đó.

<calc-sum>

Một biểu thức xác định phép tính được thực hiện trên <calc-size-basis>.

Giá trị trả về

Trả về một giá trị bằng <calc-size-basis> được sửa đổi bởi biểu thức <calc-sum>. Vì giá trị <calc-size-basis> là một giá trị kích thước nội tại, giá trị trả về là một giá trị kích thước nội tại đã được sửa đổi, hoạt động giống như giá trị kích thước nội tại đưa vào hàm.

Mô tả

Một số thuật toán bố cục trình duyệt có các hành vi đặc biệt đối với các từ khóa kích thước nội tại. Hàm calc-size() được định nghĩa rõ ràng để đại diện cho một kích thước nội tại thay vì một <length-percentage>, từ đó đảm bảo tính chính xác. calc-size() cho phép thực hiện tính toán trên các giá trị kích thước nội tại một cách an toàn và có xác định rõ ràng.

Giá trị hợp lệ cho đối số đầu tiên (<calc-size-basis>)

Đối số calc-size() đầu tiên có thể là một trong các giá trị nội tại sau:

Ngoài ra còn có một số giá trị đặc biệt mà đối số này có thể nhận:

  • Giá trị calc-size() lồng nhau. Đây không phải điều bạn thường làm, nhưng nó có sẵn để đảm bảo việc sử dụng biến CSS làm <calc-size-basis> luôn hoạt động, miễn là biến là giá trị hợp lệ cho thuộc tính mà calc-size() đang được đặt. Ví dụ, điều này sẽ hoạt động:

    css
    section {
      height: calc-size(calc-size(max-content, size), size + 2rem);
    }
    

    Cũng như điều này:

    css
    :root {
      --intrinsic-size: calc-size(max-content, size);
    }
    
    section {
      height: calc-size(var(--intrinsic-size), size + 2rem);
    }
    
  • Một <calc-sum> khác, với các hạn chế tương tự như <calc-sum> được chỉ định cho đối số thứ hai, ngoại trừ từ khóa size không thể được đưa vào. Bạn có thể sẽ không làm điều này, vì bạn không còn thực hiện tính toán trên giá trị kích thước nội tại, nhưng nếu giá trị thuộc tính tùy chỉnh là <calc-sum>, hàm vẫn sẽ hoạt động. Ví dụ, điều này sẽ hoạt động trực tiếp hoặc nếu bạn sử dụng thuộc tính tùy chỉnh với giá trị là 300px + 2rem:

    css
    section {
      height: calc-size(300px + 2rem, size / 2);
    }
    
  • Từ khóa any, đại diện cho một kích thước xác định không chỉ định. Trong trường hợp này, từ khóa size không thể được đưa vào đối số thứ hai, và calc-size() trả về kết quả của phép tính đối số thứ hai. Ví dụ:

    css
    section {
      height: calc-size(any, 300px * 1.5); /* Returns 450px */
    }
    

Việc kết hợp các kích thước nội tại khác nhau trong cùng một phép tính không hoạt động. Ví dụ, max-content - min-content không có ý nghĩa. calc-size() chỉ cho phép một giá trị kích thước nội tại duy nhất trong mỗi phép tính, tránh vấn đề này.

Giá trị hợp lệ cho đối số thứ hai (<calc-sum>)

Đối số calc-size() thứ hai là một biểu thức <calc-sum>.

Trong biểu thức này:

  • Từ khóa size đại diện cho <calc-size-basis> được chỉ định làm đối số đầu tiên.
  • Các toán hạng có thể bao gồm size, và bất kỳ kiểu giá trị nào có ý nghĩa trong ngữ cảnh.
  • Các toán tử +, -, *, và / có thể được bao gồm.
  • Các hàm toán học khác có thể được bao gồm như round(), max(), hoặc thậm chí một calc-size() lồng nhau.
  • Biểu thức tổng thể phải khớp với <length-percentage>, và giải thành <length>.

Kích hoạt animation của các giá trị kích thước nội tại

Các giá trị trả về của calc-size() có thể được nội suy, cho phép animation giữa giá trị <length-percentage> và giá trị trả về kích thước nội tại của calc-size().

Note: Bạn nên tránh animate các thuộc tính mô hình hộp nếu có thể, để giảm các sự kiện bố cục và giảm thiểu tác động đến hiệu suất (xem Critical rendering path > Layout).

Ví dụ, bạn có thể sử dụng transition để animate width của container giữa 0auto như sau:

css
section {
  width: 0;
  transition: width ease 1s;
}

section:hover,
section:focus {
  width: calc-size(auto, size);
}

Trong trường hợp trên, chúng ta không tính toán gì — chúng ta đưa auto vào calc-size() và trả về nó không thay đổi. Thuộc tính interpolate-size làm cho các animation như trên đơn giản hơn để thực hiện trong hầu hết các trường hợp, đặc biệt khi có nhiều animation cần xem xét. Nó được kế thừa và do đó chỉ cần được khai báo một lần trên thuộc tính tổ tiên, có nghĩa là chúng ta có thể đã chuyển đổi giữa 0auto mà không cần sử dụng calc-size().

Hàm calc-size() chỉ nên được sử dụng để kích hoạt animation kích thước nội tại nếu chúng cũng yêu cầu tính toán. Ví dụ, trong trường hợp sau đây, chúng ta đang animate width áp dụng tính toán cho trạng thái kết thúc kích thước nội tại:

css
section {
  width: 0;
  transition: width ease 1s;
}

section:hover,
section:focus {
  width: calc-size(auto, size + 2rem);
}

Một trường hợp trong đó calc-size() hữu ích là khi bạn muốn animate giữa một kích thước nội tại và phiên bản đã sửa đổi của cùng kích thước nội tại đó. Điều này không thể thực hiện với interpolate-sizecalc(). Ví dụ, định nghĩa @keyframes sau đây animate width của container giữa fit-content và 70% của fit-content.

css
@keyframes narrower {
  from {
    width: fit-content;
  }

  to {
    width: calc-size(fit-content, size * 0.7);
  }
}

Note: Lưu ý rằng calc-size() không cho phép animate giữa hai giá trị kích thước nội tại khác nhau.

Cú pháp hình thức

<calc-size()> = 
calc-size( <calc-size-basis> , <calc-sum> )

<calc-size-basis> =
<size-keyword> |
<calc-size()> |
any |
<calc-sum>

<calc-sum> =
<calc-product> [ [ '+' | '-' ] <calc-product> ]*

<calc-product> =
<calc-value> [ [ '*' | / ] <calc-value> ]*

<calc-value> =
<number> |
<dimension> |
<percentage> |
<calc-keyword> |
( <calc-sum> )

<calc-keyword> =
e |
pi |
infinity |
-infinity |
NaN

Ví dụ

Sử dụng cơ bản calc-size

Ví dụ này cho thấy cách định kích thước cơ bản của container bằng calc-size()

HTML

HTML chứa một phần tử <section> duy nhất chứa một số nội dung con.

html
<section>
  <h2>Favorite quote</h2>

  <p>
    Fashion is something so ugly it has to be changed every fifteen minutes.
  </p>
</section>

CSS

Trong CSS, chúng ta sử dụng flexbox để căn giữa các phần tử con bên trong <section>, và đặt widthheight của <section> thành các hàm calc-size(). width được đặt bằng fit-content cộng 6rem. height được đặt thành auto nhân với hai.

css
section {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;

  width: calc-size(fit-content, size + 6rem);
  height: calc-size(auto, size * 2);
}

Phần còn lại của CSS đã được ẩn để ngắn gọn.

Kết quả

Chúng ta đã tạo ra một số không gian ngang và dọc để văn bản được căn giữa, mà không cần sử dụng padding.

Animation cơ bản calc-size

Ví dụ này minh họa cách sử dụng calc-size() để animate giữa một kích thước cụ thể và kích thước nội tại. Demo có một thẻ nhân vật/"tên thẻ", có thể được hover hoặc focus để hiển thị thông tin về nhân vật. Việc hiển thị được xử lý bằng một transition height giữa độ dài cố định và max-content.

HTML

HTML chứa một phần tử <section> duy nhất với tabindex="0" được đặt trên nó để nó có thể nhận focus bàn phím. <section> chứa các phần tử <header><main>, mỗi phần tử với nội dung con của riêng chúng.

html
<section tabindex="0">
  <header>
    <h2>Chris Mills</h2>
  </header>
  <main>
    <p>Chris is the silent phantom of MDN.</p>
    <ul>
      <li><strong>Height</strong>: 3.03m</li>
      <li><strong>Weight</strong>: 160kg</li>
      <li><strong>Tech Fu</strong>: 7</li>
      <li><strong>Bad Jokes</strong>: 9</li>
    </ul>
  </main>
</section>

CSS

Trong CSS, chúng ta đặt height của <section> thành 2.5removerflow thành hidden để chỉ <header> được hiển thị theo mặc định, sau đó chỉ định một transition animate height của <section> trong 1 giây trong quá trình thay đổi trạng thái. Cuối cùng, chúng ta đặt height của <section> thành một lần gọi hàm calc-size() khi :hover:focus. Giá trị trả về của hàm tương đương với max-content + 2rem.

css
section {
  height: 2.5rem;
  overflow: hidden;
  transition: height ease 1s;
}

section:hover,
section:focus {
  height: calc-size(max-content, size + 2rem);
}

Phần còn lại của CSS đã được ẩn để ngắn gọn.

Kết quả

Thử hover qua <section> hoặc focus nó qua bàn phím — nó sẽ animate đến chiều cao đầy đủ + 2rem, hiển thị tất cả nội dung với 2rem không gian thêm ở dưới cùng.

Điều chỉnh chiều rộng đọc dựa trên fit-content

Ví dụ này cho thấy một container với văn bản bên trong, và một nút có thể được nhấp để làm cho chiều rộng của container hẹp hơn hoặc rộng hơn tùy thuộc vào sở thích đọc.

HTML

HTML chứa một phần tử <section> duy nhất chứa nội dung văn bản con, cộng với một <button> để thay đổi chiều rộng <section>.

html
<section class="easy-reader">
  <h2>Easy reader</h2>

  <p>
    Eius velit aperiam ipsa. Deleniti eum excepturi ut magni maxime maxime
    beatae. Dicta aperiam est laudantium ut illum facere qui officiis. Sunt
    deleniti quam id. Quis sunt voluptatem praesentium minima dolorum autem
    consequatur velit.
  </p>

  <p>
    Vitae ab incidunt velit aspernatur deleniti distinctio rerum. Et natus sed
    et quos mollitia quia quod. Quae officia ex ea. Ducimus ut voluptatem et et
    debitis. Quidem provident laboriosam exercitationem similique deleniti.
    Temporibus vel veniam mollitia magni unde a nostrum.
  </p>

  <button class="width-adjust">Narrower</button>
</section>

CSS

Trong CSS, chúng ta đặt width của <section> mặc định thành fit-content. Sau đó chúng ta định nghĩa hai tập hợp @keyframes, narrower, animate từ fit-content đến 70% của fit-content (tính bằng calc-size()), và wider, animate các giá trị tương tự nhưng theo hướng ngược lại. Cuối cùng, chúng ta gắn các animation đó vào hai lớp — .narrower.wider. Mỗi animation được định nghĩa kéo dài một giây và giữ trạng thái cuối cùng sau khi kết thúc.

css
section {
  width: fit-content;
}

@keyframes narrower {
  from {
    width: fit-content;
  }

  to {
    width: calc-size(fit-content, size * 0.7);
  }
}

@keyframes wider {
  from {
    width: calc-size(fit-content, size * 0.7);
  }

  to {
    width: fit-content;
  }
}

.narrower {
  animation: narrower 1s ease forwards;
}

.wider {
  animation: wider 1s ease forwards;
}

Phần còn lại của CSS đã được ẩn để ngắn gọn.

JavaScript

JavaScript cung cấp một nút toggle hẹp hơn/rộng hơn áp dụng lớp liên quan cho <section> khi nút được nhấp:

js
const widthAdjustBtn = document.querySelector(".width-adjust");
const easyReader = document.querySelector(".easy-reader");

widthAdjustBtn.addEventListener("click", () => {
  if (easyReader.classList.length === 1) {
    easyReader.classList.add("narrower");
    widthAdjustBtn.textContent = "Wider";
  } else if (easyReader.classList.contains("wider")) {
    easyReader.classList.replace("wider", "narrower");
    widthAdjustBtn.textContent = "Wider";
  } else if (easyReader.classList.contains("narrower")) {
    easyReader.classList.replace("narrower", "wider");
    widthAdjustBtn.textContent = "Narrower";
  }
});

Kết quả

Thử nhấp nút <button> vài lần để điều chỉnh <section> giữa chiều rộng đọc rộng và hẹp, đạt được bằng cách thao tác width dựa trên giá trị fit-content.

Sử dụng hàm bên trong hàm calc-size()

Như đã đề cập trước đó, có thể sử dụng một hàm khác bên trong calc-size(). Ví dụ này đặt field-sizing: content trên các phần tử <input> để làm cho chúng rộng bằng nội dung đã nhập, và sau đó sử dụng hàm max() bên trong calc-size() để đảm bảo rằng các <input> ít nhất có kích thước tối thiểu, và chỉ bắt đầu phát triển khi văn bản đã nhập trở nên rộng hơn kích thước đó — bằng cách đặt thành fit-content cộng 20px.

HTML

HTML chứa một phần tử <form> chứa ba kiểu <input> văn bản. Mỗi <input> có một <label> liên kết với nó để làm cho form dễ tiếp cận, và một maxlength được áp dụng để ngăn các giá trị nhập quá dài làm vỡ bố cục form.

html
<form>
  <div>
    <label for="name">Name:</label>
    <input type="text" id="name" name="name" maxlength="48" />
  </div>
  <div>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" maxlength="48" />
  </div>
  <div>
    <label for="address">Address:</label>
    <input type="text" id="address" name="address" maxlength="60" />
  </div>
</form>

CSS

Trong CSS, chúng ta đặt width của các phần tử <label> thành 100px. Chúng ta đặt field-sizing: content trên các phần tử <input> để làm cho chúng rộng bằng nội dung đã nhập — theo mặc định chúng sẽ không có chiều rộng vì không có gì được nhập vào. Để bù lại điều này, chúng ta đặt giá trị width của chúng thành calc-size(fit-content, max(100px, size + 20px)). Điều này có nghĩa là chúng rộng tối thiểu 100px, ngay cả khi không có giá trị nào được nhập. Khi giá trị đã nhập trở nên rộng hơn 100px, width của chúng thay đổi thành fit-content cộng 20px, có nghĩa là chúng bắt đầu phát triển theo kích thước nội dung nhưng giữ khoảng cách 20px ở phía bên phải.

css
label {
  width: 100px;
}

input {
  field-sizing: content;
  width: calc-size(fit-content, max(100px, size + 20px));
}

Phần còn lại của CSS đã được ẩn để ngắn gọn.

Kết quả

Thử nhập một số văn bản vào các trường form, và xem chúng phát triển như thế nào khi các giá trị bắt đầu trở nên rộng bằng chiều rộng tối thiểu được áp dụng bởi hàm max().

Thông số kỹ thuật

Specification
CSS Values and Units Module Level 5
# calc-size

Khả năng tương thích trình duyệt

Xem thêm