使用 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 属性可以进行过渡?

网页作者可以定义必须动画化的属性以及动画方式。这允许创建复杂的过渡。但是,某些属性不可动画化,因为动画化它们没有意义。

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

定义过渡

CSS 过渡使用简写transition属性进行控制。这是配置过渡的最佳方法,因为它可以更容易地避免参数不同步,而不同步的参数可能会让人非常沮丧,导致在 CSS 中花费大量时间调试。

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

transition-property

指定应将过渡应用到的 CSS 属性的名称或名称列表。只有此处列出的属性在过渡期间会进行动画;对所有其他属性的更改通常会像往常一样立即发生。

transition-duration

指定过渡持续时间。您可以指定一个适用于所有属性的持续时间,也可以指定多个值,以便每个属性以不同的时间段进行过渡。

transition-timing-function

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

transition-delay

定义在属性更改与过渡实际开始之间等待的时间。

transition 简写 CSS 语法如下所示

css
div {
  transition: <property> <duration> <timing-function> <delay>;
}

示例

基本示例

此示例执行一个持续 4 秒的字体大小过渡,在用户将鼠标悬停在元素上与动画效果开始之间存在 2 秒的延迟。

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: #0000ff;
  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: #333;
  color: #fff;
  border: 1px solid;
  padding: 0.5rem;
  text-align: center;
  text-decoration: none;
  transition: all 0.5s ease-out;
}

a:hover,
a:focus {
  background-color: #fff;
  color: #333;
}

此 CSS 确定了菜单的外观,当元素处于其:hover:focus 状态时,背景和文本颜色都会发生变化。

过渡显示和内容可见性

此示例演示了如何对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来为在元素上设置的属性提供一组起始值,当元素收到其第一个样式更新时,您希望从该值过渡。这是为了避免意外行为。默认情况下,CSS 过渡不会在元素的第一个样式更新时触发(当它们首次出现在 DOM 中时),包括当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>

使用 JavaScript,您可以使将球移动到特定位置的效果发生

js
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)`;
  },
  false,
);

使用 CSS,您可以使其在没有任何额外努力的情况下变得平滑。向元素添加过渡,任何更改都将平滑发生。

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

检测过渡的开始和完成

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

propertyName

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

elapsedTime

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

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

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

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

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

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

规范

规范
CSS 过渡

另请参阅