@starting-style
@starting-style CSS @ 規則用於為元素上設定的屬性定義起始值,當元素接收到首次樣式更新時(即當元素在先前載入的頁面上首次顯示時),你希望從這些起始值開始過渡。
語法
@starting-style @ 規則有兩種使用方式:
-
作為一個獨立的塊,此時它包含一個或多個規則集,定義了起始樣式宣告並選擇它們所應用的元素。
css@starting-style { /* rulesets */ } -
巢狀在現有規則集中,此時它包含一個或多個宣告,為該規則集已選擇的元素定義起始屬性值。
cssselector { /* existing ruleset */ /* ... */ @starting-style { /* declarations */ } }
描述
為避免意外行為,預設情況下,CSS 過渡在元素的初始樣式更新時,或當其 display 型別從 none 更改為其他值時,不會被觸發。要啟用首次樣式過渡,需要使用 @starting-style 規則。它們為沒有先前狀態的元素提供起始樣式,定義了過渡的起始屬性值。
@starting-style 在為顯示在頂層的元素(例如 popover 和模態 <dialog>)建立進入和退出過渡時特別有用,也適用於在 display: none 和其他值之間切換的元素,以及首次新增到 DOM 或從 DOM 中移除的元素。
有兩種使用 @starting-style 的方式:作為獨立規則或巢狀在規則集內。
讓我們考慮一個場景:我們希望在 popover 顯示時(即新增到頂層時)為其新增動畫。指定開啟的 popover 樣式的“原始規則”可能如下所示(參見下面的popover 示例):
[popover]:popover-open {
opacity: 1;
transform: scaleX(1);
}
要使用第一種方法指定 popover 將要進行動畫的屬性的起始值,你可以在 CSS 中包含一個獨立的 @starting-style 塊:
@starting-style {
[popover]:popover-open {
opacity: 0;
transform: scaleX(0);
}
}
備註: @starting-style @ 規則和“原始規則”具有相同的層疊優先順序。為確保應用起始樣式,請將 @starting-style @ 規則包含在“原始規則”之後。如果你在“原始規則”之前指定 @starting-style @ 規則,原始樣式將覆蓋起始樣式。
要使用巢狀方法為 popover 指定起始樣式,你可以將 @starting-style 塊巢狀在“原始規則”內:
[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 從透明過渡到綠色。
#target {
transition: background-color 1.5s;
background-color: green;
}
@starting-style {
#target {
background-color: transparent;
}
}
當元素的 display 值在 none 和其他值之間切換時,過渡其 opacity。
#target {
transition-property: opacity, display;
transition-duration: 0.5s;
display: block;
opacity: 1;
@starting-style {
opacity: 0;
}
}
#target.hidden {
display: none;
opacity: 0;
}
何時使用起始樣式的演示
在此示例中,按下一個按鈕會建立一個 <div> 元素,給它一個 showing 的 class,並將其新增到 DOM 中。
showing 被賦予了一個 background-color: red 的 @starting-style 和一個 background-color: blue 的目標樣式以進行過渡。預設的 div 規則集包含 background-color: yellow,並且 transition 也在此處設定。
當 <div> 首次新增到 DOM 時,你會看到背景從紅色過渡到藍色。在超時之後,我們透過 JavaScript 從 <div> 中移除 showing 類。此時,它從藍色過渡回黃色,而不是紅色。這證明了起始樣式僅在元素首次在 DOM 中渲染時使用。一旦它出現,元素就會過渡回其上設定的預設樣式。
在另一個超時之後,我們從 DOM 中完全移除 <div>,重置示例的初始狀態,以便可以再次執行。
HTML
<button>Display <code><div></code></button>
CSS
div {
background-color: yellow;
transition: background-color 3s;
}
div.showing {
background-color: skyblue;
}
@starting-style {
div.showing {
background-color: red;
}
}
JavaScript
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> 元素。
<button popovertarget="mypopover">Show the popover</button>
<div popover="auto" id="mypopover">I'm a Popover! I should animate.</div>
CSS
在此示例中,我們希望為兩個屬性新增動畫:opacity 和 transform(具體來說是水平縮放變換),以使 popover 淡入淡出以及水平放大和縮小。
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
<button>Create new column</button>
<section></section>
JavaScript
JavaScript 實現了元素的新增和移除:
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,用於在每列新增和移除時為其 opacity 和 scale 新增動畫:
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 時為其 opacity 和 scale 新增動畫,並在其從 DOM 中移除時反轉動畫,我們:
- 在
div { ... }規則上指定我們想要過渡的屬性的結束狀態。 - 在
@starting-style塊內指定屬性過渡的起始狀態。 - 在
.fade-out規則內指定退出動畫——這是 JavaScript 在按下關閉按鈕時分配給<div>元素的類。除了設定opacity和scale的結束狀態外,我們還為<div>設定了display: none——我們希望它們在從 UI 中移除時立即變得不可用。 - 在
div { ... }規則內指定transition列表,為opacity、scale和display新增動畫。請注意,對於display,transition-behavior: allow-discrete值也在簡寫中設定,以便它能夠進行動畫。
結果
最終結果如下:
規範
| 規範 |
|---|
| CSS Transitions Level 2 # defining-before-change-style |
瀏覽器相容性
載入中…
另見
- CSS 過渡模組
overlaytransition-behaviorCSSStartingStyleRule- 用於平滑進入和退出動畫的四個新 CSS 功能,來自 developer.chrome.com (2023)