@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)