@starting-style

Baseline 2024
新推出

自 ⁨2024 年 8 月⁩起,此功能可在最新的裝置和瀏覽器版本上使用。此功能可能無法在舊裝置或瀏覽器上使用。

@starting-style CSS @ 規則用於為元素上設定的屬性定義起始值,當元素接收到首次樣式更新時(即當元素在先前載入的頁面上首次顯示時),你希望從這些起始值開始過渡。

語法

@starting-style @ 規則有兩種使用方式:

  1. 作為一個獨立的塊,此時它包含一個或多個規則集,定義了起始樣式宣告並選擇它們所應用的元素。

    css
    @starting-style {
      /* rulesets */
    }
    
  2. 巢狀在現有規則集中,此時它包含一個或多個宣告,為該規則集已選擇的元素定義起始屬性值。

    css
    selector {
      /* existing ruleset */
      /* ... */
    
      @starting-style {
        /* declarations */
      }
    }
    

描述

為避免意外行為,預設情況下,CSS 過渡在元素的初始樣式更新時,或當其 display 型別從 none 更改為其他值時,不會被觸發。要啟用首次樣式過渡,需要使用 @starting-style 規則。它們為沒有先前狀態的元素提供起始樣式,定義了過渡的起始屬性值。

@starting-style 在為顯示在頂層的元素(例如 popover 和模態 <dialog>)建立進入和退出過渡時特別有用,也適用於在 display: none 和其他值之間切換的元素,以及首次新增到 DOM 或從 DOM 中移除的元素。

備註: @starting-style 僅與 CSS 過渡相關。當使用 CSS 動畫來實現此類效果時,不需要 @starting-style。請參閱使用 CSS 動畫中的示例。

有兩種使用 @starting-style 的方式:作為獨立規則或巢狀在規則集內。

讓我們考慮一個場景:我們希望在 popover 顯示時(即新增到頂層時)為其新增動畫。指定開啟的 popover 樣式的“原始規則”可能如下所示(參見下面的popover 示例):

css
[popover]:popover-open {
  opacity: 1;
  transform: scaleX(1);
}

要使用第一種方法指定 popover 將要進行動畫的屬性的起始值,你可以在 CSS 中包含一個獨立的 @starting-style 塊:

css
@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: scaleX(0);
  }
}

備註: @starting-style @ 規則和“原始規則”具有相同的層疊優先順序。為確保應用起始樣式,請將 @starting-style @ 規則包含在“原始規則”之後。如果你在“原始規則”之前指定 @starting-style @ 規則,原始樣式將覆蓋起始樣式。

要使用巢狀方法為 popover 指定起始樣式,你可以將 @starting-style 塊巢狀在“原始規則”內:

css
[popover]:popover-open {
  opacity: 1;
  transform: scaleX(1);

  @starting-style {
    opacity: 0;
    transform: scaleX(0);
  }
}

起始樣式究竟在什麼時候使用?

重要的是要理解,當元素首次在 DOM 中渲染時,或者當它從 display: none 過渡到一個可見值時,元素將從其 @starting-style 樣式開始過渡。當它從初始可見狀態過渡回來時,它將不再使用 @starting-style 樣式,因為它現在在 DOM 中是可見的。相反,它將過渡回該元素預設狀態的任何現有樣式。

實際上,在這些情況下需要管理三種樣式狀態——起始樣式狀態、過渡後狀態和預設狀態。在這種情況下,“進入”和“退出”的過渡可能是不同的。你可以在我們下面的何時使用起始樣式的演示示例中看到這一點。

正式語法

@starting-style = 
@starting-style { <rule-list> }

示例

@starting-style 的基本用法

在元素初始渲染時,將其 background-color 從透明過渡到綠色。

css
#target {
  transition: background-color 1.5s;
  background-color: green;
}

@starting-style {
  #target {
    background-color: transparent;
  }
}

當元素的 display 值在 none 和其他值之間切換時,過渡其 opacity

css
#target {
  transition-property: opacity, display;
  transition-duration: 0.5s;
  display: block;
  opacity: 1;
  @starting-style {
    opacity: 0;
  }
}

#target.hidden {
  display: none;
  opacity: 0;
}

何時使用起始樣式的演示

在此示例中,按下一個按鈕會建立一個 <div> 元素,給它一個 showingclass,並將其新增到 DOM 中。

showing 被賦予了一個 background-color: red@starting-style 和一個 background-color: blue 的目標樣式以進行過渡。預設的 div 規則集包含 background-color: yellow,並且 transition 也在此處設定。

<div> 首次新增到 DOM 時,你會看到背景從紅色過渡到藍色。在超時之後,我們透過 JavaScript 從 <div> 中移除 showing 類。此時,它從藍色過渡回黃色,而不是紅色。這證明了起始樣式僅在元素首次在 DOM 中渲染時使用。一旦它出現,元素就會過渡回其上設定的預設樣式。

在另一個超時之後,我們從 DOM 中完全移除 <div>,重置示例的初始狀態,以便可以再次執行。

HTML

html
<button>Display <code>&lt;div&gt;</code></button>

CSS

css
div {
  background-color: yellow;
  transition: background-color 3s;
}

div.showing {
  background-color: skyblue;
}

@starting-style {
  div.showing {
    background-color: red;
  }
}

JavaScript

js
const btn = document.querySelector("button");

btn.addEventListener("click", () => {
  btn.disabled = true;
  const divElem = document.createElement("div");
  divElem.classList.add("showing");
  document.body.append(divElem);

  setTimeout(() => {
    divElem.classList.remove("showing");

    setTimeout(() => {
      divElem.remove();
      btn.disabled = false;
    }, 3000);
  }, 3000);
});

結果

程式碼渲染如下:

為 popover 新增動畫

在此示例中,使用 CSS 過渡為一個 popover 添加了動畫。使用 transition 屬性提供了基本的進入和退出動畫。

HTML

HTML 包含一個使用 popover 屬性宣告為 popover 的 <div> 元素,以及一個使用其 popovertarget 屬性指定為 popover 顯示控制元件的 <button> 元素。

html
<button popovertarget="mypopover">Show the popover</button>
<div popover="auto" id="mypopover">I'm a Popover! I should animate.</div>

CSS

在此示例中,我們希望為兩個屬性新增動畫:opacitytransform(具體來說是水平縮放變換),以使 popover 淡入淡出以及水平放大和縮小。

css
html {
  font-family: "Helvetica", "Arial", sans-serif;
}

[popover]:popover-open {
  opacity: 1;
  transform: scaleX(1);
}

[popover] {
  font-size: 1.2rem;
  padding: 10px;

  /* Final state of the exit animation */
  opacity: 0;
  transform: scaleX(0);

  transition:
    opacity 0.7s,
    transform 0.7s,
    overlay 0.7s allow-discrete,
    display 0.7s allow-discrete;
  /* Equivalent to
  transition: all 0.7s allow-discrete; */
}

/* Include after the [popover]:popover-open rule */
@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: scaleX(0);
  }
}

/* Transition for the popover's backdrop */
[popover]::backdrop {
  background-color: transparent;
  transition:
    display 0.7s allow-discrete,
    overlay 0.7s allow-discrete,
    background-color 0.7s;
  /* Equivalent to
  transition: all 0.7s allow-discrete; */
}

[popover]:popover-open::backdrop {
  background-color: rgb(0 0 0 / 25%);
}

/* Nesting (&) is not supported for pseudo-elements
so specify a standalone starting-style block. */
@starting-style {
  [popover]:popover-open::backdrop {
    background-color: transparent;
  }
}

為實現此目的,我們在 popover 元素的預設隱藏狀態(透過 [popover] 選擇)上為這些屬性設定了起始狀態,並在 popover 的開啟狀態(透過 :popover-open 偽類選擇)上設定了結束狀態。

然後,我們設定了一個 transition 屬性以在這兩種狀態之間進行動畫。動畫的起始狀態包含在 @starting-style @ 規則內,以啟用進入動畫。

因為被動畫的元素在顯示時被提升到頂層,而在隱藏時從頂層移除(使用 display: none),所以需要一些額外的步驟來確保動畫在兩個方向上都能正常工作:

  • display 被新增到過渡元素的列表中,以確保動畫元素在進入和退出動畫期間都可見(設定為 display: block 或其他可見的 display 值)。沒有這個,退出動畫將不可見;實際上,popover 會直接消失。請注意,transition-behavior: allow-discrete 值也在簡寫中設定,以啟用動畫。
  • overlay 被新增到過渡元素的列表中,以確保將元素從頂層移除的操作延遲到動畫結束。這對於像這樣的動畫來說差別不大,但在更復雜的情況下,不這樣做可能導致元素過早地從頂層移除,意味著動畫不平滑或無效。同樣,在這種情況下,需要 transition-behavior: allow-discrete 才能發生動畫。

備註: 我們還為 popover 開啟時出現在其後面的 ::backdrop 添加了過渡,以提供一個漂亮的變暗動畫。[popover]:popover-open::backdrop 用於選擇 popover 開啟時的背景板。

結果

程式碼渲染如下:

備註: 因為 popover 每次顯示時都會從 display: none 變為 display: block,所以每次進入過渡發生時,popover 都會從其 @starting-style 樣式過渡到其 [popover]:popover-open 樣式。當 popover 關閉時,它會從其 [popover]:popover-open 狀態過渡到預設的 [popover] 狀態。

備註: 你可以在 <dialog> 參考頁面上找到一個演示如何為 <dialog> 元素及其背景板在顯示和隱藏時新增過渡的示例——請參閱為對話方塊元素新增過渡

在 DOM 新增和移除時為元素新增過渡

此示例包含一個按鈕,按下時會將新元素附加到一個 <section> 容器中。每個元素內部又包含一個巢狀的按鈕,按下時會移除該元素。此示例演示瞭如何在元素新增到 DOM 或從 DOM 中移除時使用過渡為其新增動畫。

HTML

html
<button>Create new column</button>
<section></section>

JavaScript

JavaScript 實現了元素的新增和移除:

js
const btn = document.querySelector("button");
const sectionElem = document.querySelector("section");

btn.addEventListener("click", createColumn);

function randomBackground() {
  function randomNum() {
    return Math.floor(Math.random() * 255);
  }
  const baseColor = `${randomNum()} ${randomNum()} ${randomNum()}`;

  return `linear-gradient(to right, rgb(${baseColor} / 0), rgb(${baseColor} / 0.5))`;
}

function createColumn() {
  const divElem = document.createElement("div");
  divElem.style.background = randomBackground();

  const closeBtn = document.createElement("button");
  closeBtn.textContent = "✖";
  closeBtn.setAttribute("aria-label", "close");
  divElem.append(closeBtn);
  sectionElem.append(divElem);

  closeBtn.addEventListener("click", () => {
    divElem.classList.add("fade-out");

    setTimeout(() => {
      divElem.remove();
    }, 1000);
  });
}

當點選“建立新列”按鈕時,會呼叫 createColumn() 函式。該函式會建立一個帶有隨機生成背景色的 <div> 元素和一個用於關閉該 <div><button> 元素。然後它將 <button> 附加到 <div>,再將 <div> 附加到 <section> 容器。

然後,我們透過 addEventListener() 為關閉按鈕新增一個事件監聽器。點選關閉按鈕會做兩件事:

  • <div> 新增 fade-out 類。新增該類會觸發設定在該類上的退出動畫。
  • 在 1000 毫秒延遲後移除 <div>setTimeout() 會延遲從 DOM 中移除 <div>(透過 Element.remove()),直到動畫結束。

CSS

我們包含了一個 transition,用於在每列新增和移除時為其 opacityscale 新增動畫:

css
div {
  flex: 1;
  border: 1px solid gray;
  position: relative;
  opacity: 1;
  scale: 1 1;

  transition:
    opacity 0.7s,
    scale 0.7s,
    display 0.7s allow-discrete,
    all 0.7s allow-discrete;
  /* Equivalent to
  transition: all 0.7s allow-discrete; */
}

/* Include after the `div` rule */
@starting-style {
  div {
    opacity: 0;
    scale: 1 0;
  }
}

.fade-out {
  opacity: 0;
  display: none;
  scale: 1 0;
}

div > button {
  font-size: 1.6rem;
  background: none;
  border: 0;
  text-shadow: 2px 1px 1px white;
  border-radius: 15px;
  position: absolute;
  top: 1px;
  right: 1px;
  cursor: pointer;
}

為了在每個 <div> 新增到 DOM 時為其 opacityscale 新增動畫,並在其從 DOM 中移除時反轉動畫,我們:

  • div { ... } 規則上指定我們想要過渡的屬性的結束狀態。
  • @starting-style 塊內指定屬性過渡的起始狀態。
  • .fade-out 規則內指定退出動畫——這是 JavaScript 在按下關閉按鈕時分配給 <div> 元素的類。除了設定 opacityscale 的結束狀態外,我們還為 <div> 設定了 display: none——我們希望它們在從 UI 中移除時立即變得不可用。
  • div { ... } 規則內指定 transition 列表,為 opacityscaledisplay 新增動畫。請注意,對於 displaytransition-behavior: allow-discrete 值也在簡寫中設定,以便它能夠進行動畫。

結果

最終結果如下:

規範

規範
CSS Transitions Level 2
# defining-before-change-style

瀏覽器相容性

另見