使用 CSS 过渡

CSS 过渡提供了一种控制 CSS 属性变化时动画速度的方法。您不再需要让属性变化立即生效,而是可以让属性变化在一段时间内发生。例如,如果您将元素的颜色从白色更改为黑色,通常变化是瞬时的。启用 CSS 过渡后,变化会按照加速度曲线在时间间隔内发生,所有这些都可以自定义。

涉及在两个状态之间过渡的动画通常被称为隐式过渡,因为起始状态和最终状态之间的中间状态是由浏览器隐式定义的。

A CSS transition tells the browser to draw the intermediate states between the initial and final states, showing the user a smooth transitions.

CSS 过渡让您可以决定要动画哪些属性(通过明确列出它们)、动画何时开始(通过设置延迟)、过渡将持续多长时间(通过设置持续时间),以及过渡如何运行(通过定义一个缓动函数,例如,线性或开始时快,结束时慢)。

哪些 CSS 属性可以进行过渡?

Web 作者可以定义哪些属性需要动画以及以何种方式动画。这允许创建复杂的过渡。然而,有些属性是不可动画的,因为动画它们没有意义。

注意:auto 值通常是一个非常复杂的情况。规范建议不要从 auto 动画到 auto。一些用户代理,例如基于 Gecko 的用户代理,实现了此要求,而其他用户代理,例如基于 WebKit 的用户代理,则不那么严格。将动画与 auto 一起使用可能会导致不可预测的结果,具体取决于浏览器及其版本,应避免使用。

定义过渡

CSS 过渡使用速记transition属性进行控制。这是配置过渡的最佳方式,因为它更容易避免参数不同步,这在 CSS 调试中可能非常令人沮丧。

您可以使用以下子属性控制过渡的各个组件

transition-property

指定应应用过渡的 CSS 属性的名称。只有此处列出的属性在过渡期间进行动画;所有其他属性的更改照常立即发生。

transition-duration

指定过渡应发生持续时间。您可以指定适用于过渡期间所有属性的单个持续时间,或指定多个值以允许每个属性在不同的时间段内过渡。

transition-timing-function

指定一个函数来定义属性的中间值如何计算。缓动函数确定如何计算过渡的中间值。大多数缓动函数可以通过提供相应函数的图形来指定,该图形由定义三次贝塞尔曲线的四个点定义。您还可以从缓动函数备忘单中选择缓动。

transition-delay

定义属性更改和过渡实际开始之间等待多长时间。

transition 速记 CSS 语法编写如下

transition: <property> <duration> <timing-function> <delay>;

示例

基本示例

此示例执行一个四秒的字体大小过渡,并在用户将鼠标悬停在元素上和动画效果开始之间有两秒的延迟

css
#delay {
  font-size: 14px;
  transition-property: font-size;
  transition-duration: 4s;
  transition-delay: 2s;
}

#delay:hover {
  font-size: 36px;
}

多个动画属性示例

CSS

css
.box {
  border-style: solid;
  border-width: 1px;
  display: block;
  width: 100px;
  height: 100px;
  background-color: blue;
  transition:
    width 2s,
    height 2s,
    background-color 2s,
    rotate 2s;
}

.box:hover {
  background-color: #ffcccc;
  width: 200px;
  height: 200px;
  rotate: 180deg;
}

当属性值列表长度不同时

如果任何属性的值列表比其他属性短,则其值会重复以使其匹配。例如

css
div {
  transition-property: opacity, left, top, height;
  transition-duration: 3s, 5s;
}

这被视为

css
div {
  transition-property: opacity, left, top, height;
  transition-duration: 3s, 5s, 3s, 5s;
}

同样,如果任何属性的值列表比transition-property的值列表长,它将被截断,因此如果您有以下 CSS

css
div {
  transition-property: opacity, left;
  transition-duration: 3s, 5s, 2s, 1s;
}

这被解释为

css
div {
  transition-property: opacity, left;
  transition-duration: 3s, 5s;
}

在突出显示菜单时使用过渡

CSS 的一个常见用途是当用户将鼠标光标悬停在菜单项上时突出显示它们。使用过渡可以轻松使效果更具吸引力。

首先,我们使用 HTML 设置菜单

html
<nav>
  <a href="#">Home</a>
  <a href="#">About</a>
  <a href="#">Contact Us</a>
  <a href="#">Links</a>
</nav>

然后我们构建 CSS 来实现菜单的外观和感觉

css
nav {
  display: flex;
  gap: 0.5rem;
}

a {
  flex: 1;
  background-color: #333333;
  color: white;
  border: 1px solid;
  padding: 0.5rem;
  text-align: center;
  text-decoration: none;
  transition: all 0.5s ease-out;
}

a:hover,
a:focus {
  background-color: white;
  color: #333333;
}

此 CSS 建立了菜单的外观,当元素处于其:hover:focus状态时,背景和文本颜色都会改变

过渡 displaycontent-visibility

此示例演示了如何过渡displaycontent-visibility。此行为对于创建进入/退出动画很有用,例如,您希望使用 display: none 从 DOM 中删除容器,但希望它使用opacity淡出而不是立即消失。

支持的浏览器通过离散动画类型的变体过渡 displaycontent-visibility。这通常意味着属性在两种值之间动画进行到 50% 时会翻转。

但是有一个例外,那就是动画到/从 display: nonecontent-visibility: hidden。在这种情况下,浏览器将在两个值之间翻转,以便过渡内容在整个动画持续时间内显示。

所以例如:

  • displaynone 动画到 block(或其他可见的 display 值)时,该值将在动画持续时间的 0% 处切换到 block,使其在整个过程中可见。
  • displayblock(或其他可见的 display 值)动画到 none 时,该值将在动画持续时间的 100% 处切换到 none,使其在整个过程中可见。

在过渡这些属性时,需要在过渡上设置transition-behavior: allow-discrete。这有效地启用了 display/content-visibility 过渡。

在过渡 display 时,需要@starting-style来为元素上设置的属性提供一组起始值,当元素接收到其第一个样式更新时,您希望从这些值开始过渡。这是为了避免意外行为。默认情况下,当元素首次出现在 DOM 中时,CSS 过渡不会在元素的第一个样式更新时触发,这包括当 displaynone 更改为另一个状态时。content-visibility 动画不需要在 @starting-style 块中指定起始值。这是因为 content-visibility 不会像 display 那样从 DOM 中隐藏元素:它只是跳过渲染元素的内容。

HTML

HTML 包含两个<p>元素,中间有一个<div>,我们将从 display none 动画到 block

html
<p>
  Click anywhere on the screen or press any key to toggle the
  <code>&lt;div&gt;</code> between hidden and showing.
</p>

<div>
  This is a <code>&lt;div&gt;</code> element that transitions between
  <code>display: none; opacity: 0</code> and
  <code>display: block; opacity: 1</code>. Neat, huh?
</div>

<p>
  This is another paragraph to show that <code>display: none;</code> is being
  applied and removed on the above <code>&lt;div&gt; </code>. If only its
  <code>opacity</code> was being changed, it would always take up the space in
  the DOM.
</p>

CSS

css
html {
  height: 100vh;
}

div {
  font-size: 1.6rem;
  padding: 20px;
  border: 3px solid red;
  border-radius: 20px;
  width: 480px;

  display: none;
  opacity: 0;
  transition:
    opacity 1s,
    display 1s allow-discrete;
  /* Equivalent to
  transition: all 1s allow-discrete; */
}

.showing {
  opacity: 1;
  display: block;
}

@starting-style {
  .showing {
    opacity: 0;
  }
}

请注意用于指定过渡起始样式的 @starting-style 块,以及在过渡列表中包含 display 属性,并设置了 allow-discrete

JavaScript

最后,我们包含一些 JavaScript 来设置事件监听器以触发过渡(通过 showing 类)。

js
const divElem = document.querySelector("div");
const htmlElem = document.querySelector(":root");

htmlElem.addEventListener("click", showHide);
document.addEventListener("keydown", showHide);

function showHide() {
  divElem.classList.toggle("showing");
}

结果

代码渲染如下:

JavaScript 示例

注意:在使用过渡后立即使用时应谨慎

  • 使用 .appendChild() 将元素添加到 DOM
  • 移除元素的 display: none; 属性。

这被视为初始状态从未发生过,并且元素始终处于其最终状态。克服此限制的简单方法是在更改您打算过渡到的 CSS 属性之前应用几毫秒的 setTimeout()

使用过渡使 JavaScript 功能流畅

过渡是一个很好的工具,可以在不改变 JavaScript 功能的情况下使事物看起来更流畅。请看以下示例。

html
<p>Click anywhere to move the ball</p>
<div id="foo" class="ball"></div>
js
// Make the ball move to a certain position:
const f = document.getElementById("foo");
document.addEventListener("click", (ev) => {
  f.style.transform = `translateY(${ev.clientY - 25}px)`;
  f.style.transform += `translateX(${ev.clientX - 25}px)`;
});

使用 CSS,您可以平滑通过 JavaScript 应用的样式。向元素添加过渡,任何更改都将平滑发生

css
.ball {
  border-radius: 25px;
  width: 50px;
  height: 50px;
  background: #cc0000;
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 1s;
}

检测过渡的开始和完成

您可以使用transitionend事件来检测动画是否已完成运行。这是一个TransitionEvent对象,除了典型的Event对象之外,它还有两个附加属性

propertyName

一个字符串,指示其过渡完成的 CSS 属性的名称。

elapsedTime

一个浮点数,指示事件触发时过渡已运行的秒数。此值不受transition-delay值的影响。

像往常一样,您可以使用addEventListener()方法来监听此事件

js
el.addEventListener("transitionend", updateTransition);

您可以使用transitionrun(在任何延迟之前触发)和transitionstart(在任何延迟之后触发)以相同的方式检测过渡的开始

js
el.addEventListener("transitionrun", signalStart);
el.addEventListener("transitionstart", signalStart);

注意:如果过渡在完成之前中止,因为元素被设置为display: none或动画属性的值被更改,则不会触发 transitionend 事件。

规范

规范
CSS 过渡

另见