progress()

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

progress() CSS 函数返回一个 <number> 值,表示一个值(进度值)相对于另外两个值(进度起始值和结束值)的位置。

语法

css
/* With fixed progress value */
progress(300, 0, 1000)
progress(50px, 0px, 100px)
progress(50%, 30%, 80%)

/* With custom property */
progress(var(--container-width), 320, 1200)

/* Inside math function */
calc((progress(var(--container-width), 20%, 80%) / 2) + 0.5)

/* Inside non-math function */
rgb(
  calc(255 * progress(var(--container-width), 320px, 1200px))
  calc(255 * progress(var(--container-width), 320px, 1200px)) 255 / 0.5
);

/* Math function inside progress() */
progress(calc(20 + 30), 0, 100)

参数

progress() 函数接受三个以逗号分隔的 <calc-sum> 表达式作为参数。

progress(<calc-sum>, <calc-sum>, <calc-sum>)

它们分别是:

进度

需要计算其相对于另外两个值的位置的值。

进度起始值

进度的下限。

进度结束值

进度的上限。

返回值

一个 <number>,表示进度值相对于另外两个值的位置。计算方式如下:

(progress - progress start) / (progress end - progress start)

如果进度值介于进度起始值和进度结束值之间,返回值将在 01 之间,表示一个百分比。如果进度值小于进度起始值或大于进度结束值,函数仍然有效,但返回值将被分别限制在 01

描述

CSS progress() 函数提供了一种计算进度比率的方法,这对于创建进度条动画或随着宽度增加而淡入以显示其内容的盒子等用例非常有用。

最简单的用法可能如下:

css
opacity: progress(5, 0, 10);

在这种情况下,opacity 的计算值为 0.5,因为 5 是 010 的中间值。

允许的单位类型

progress() 函数的参数可以是数学表达式或简单值。这些值(或表达式结果)可以是任何 <number><dimension><percentage> 值。它们可以有不同的单位,但必须都是同一类型,否则函数无效。

我们之前看到的例子是有效的——它的所有参数都是无单位的 <number> 值:

css
progress(5, 0, 10)

下一个例子也是有效的——它的所有参数都有 <length> 单位。在后台,计算值被用于计算。如果在计算时 font-size16px,那么 3em 将解析为 48px,这是 0px100px 之间距离的 48%,所以返回值将是 0.48

css
progress(3em, 0px, 100px)

然而,本节中最后几个例子是无效的。类型不匹配,因此最终的计算没有意义。

css
progress(3s, 0px, 100px)
progress(3em, 0, 100)

创建无单位值

progress() 函数输出无单位值,因此可以像 tan(atan2()) 技巧一样,用于从值中移除单位。但请注意,由于 CSS 类型化算术 行为的更新,这也可以通过简单的除法来实现。

progress() 与其他函数和自定义属性结合使用

因为 progress() 总是返回一个介于 01 之间的无单位值,所以通常会将其与另一个数学函数(如 calc())结合使用,以输出你想要的值和单位。你还可以在 progress() 函数中使用 CSS 自定义属性——这是很合理的,因为你通常希望在多个地方设置相同的值,和/或将它们基于通过 JavaScript 设置的自定义属性。

以下示例计算视口宽度在 320px 的最小宽度和 1200px 的最大宽度之间的百分比。calc() 函数用于将 progress() 的返回值乘以 600px,将其转换为一个像素值,该值将是视口宽度在 320px1200px 之间进度值的一半。

css
width: calc(progress(100vw, 320px, 1200px) * 600px);

例如,如果视口宽度为 700px,进度值将计算为 ((700 - 320) / (1200 - 320)) = 0.431818。然后宽度将计算为 0.431818 * 600px,等于 259.1px

下一个示例是前一个的更新,其中我们为进度、进度起始和进度结束值使用了自定义属性。

css
width: calc(
  progress(
      var(--container-width),
      var(--custom-min-width),
      var(--custom-max-width)
    ) *
    var(--custom-max-width)
);

只要你的函数为这些值返回有效的类型,就可以使用 progress() 函数来计算其他函数内的单个值以及简写属性值中的组件值。

这可能会导致一些复杂的表达式。例如,在这里我们计算一个 rgb() 颜色的前两个通道,使其与之前的宽度比例成正比:

css
background-color: rgb(
  calc(
      255 *
        progress(
          var(--container-width),
          var(--custom-min-width),
          var(--custom-max-width)
        )
    )
    calc(
      255 *
        progress(
          var(--container-width),
          var(--custom-min-width),
          var(--custom-max-width)
        )
    )
    255 / 0.5
);

正式语法

<progress()> = 
progress( <calc-sum> , <calc-sum> , <calc-sum> )

<calc-sum> =
<calc-product> [ [ '+' | '-' ] <calc-product> ]*

<calc-product> =
<calc-value> [ [ '*' | / ] <calc-value> ]*

<calc-value> =
<number> |
<dimension> |
<percentage> |
<calc-keyword> |
( <calc-sum> )

<calc-keyword> =
e |
pi |
infinity |
-infinity |
NaN

示例

progress() 的基本用法

在这个例子中,我们将展示 progress() 函数的基本用法,将进度条的 width 设置为一个百分比,该百分比等于其父元素的 width 在其 min-widthmax-width 之间的进度比率。

HTML

我们的 HTML 有一个代表我们内容的 <section> 元素,以及一个代表宽度进度条的 <div> 元素。

html
<section>
  <div class="progress"></div>
</section>

CSS

在我们的 CSS 中,我们首先在 <section> 元素上设置一些自定义属性来表示其 min-widthmax-widthwidth。然后我们将这些属性设置为相应的自定义属性值,再给我们的 <section> 一个纯色的 background-color,使其可见。

css
section {
  --custom-min-width: 300px;
  --custom-max-width: 700px;
  --custom-width: 600px;

  min-width: var(--custom-min-width);
  max-width: var(--custom-max-width);
  width: var(--custom-width);

  background-color: cyan;
}

现在来看我们的 <div>——我们首先给它一个 height 和一个深色的 background-color,使其在我们的 <section> 元素上突出显示。然后,我们通过使用 progress() 函数计算宽度在最小和最大宽度之间的进度比率来计算其 width,再使用 calc() 函数将 progress() 的返回值乘以 100%,以返回一个百分比。

css
.progress {
  height: 4px;
  background-color: red;

  width: calc(
    progress(
        var(--custom-width),
        var(--custom-min-width),
        var(--custom-max-width)
      ) *
      100%
  );
}

结果

此演示将呈现如下:

<div> 的宽度是 <section> 宽度的 75%,因为 min-width400pxmax-width700px,而 width600px,这是前两个值之间距离的 75%

容器上的调整大小效果

这个例子展示了一些更复杂的 progress() 函数用法,当浏览器窗口调整大小时会产生一些有趣的效果。

此示例在桌面浏览器选项卡中以全尺寸渲染时效果更好。因此,我们没有在此页面中嵌入实时示例。你可以在 CSS progress() 函数演示中找到它的实时运行(另请参阅源代码)。

在单独的选项卡中打开实时示例,并尝试增加和减少浏览器窗口的宽度以查看效果。请保持此页面打开,以便在阅读下面的解释时可以参考。

HTML

我们的 HTML 有一个包含其余内容的 <article> 元素,以及两个 <section> 元素——一个用于悬挂背景图片,另一个用于包含我们的内容。<section class="content"> 还包含一个 <div class="progress">,表示一个宽度进度条,与我们之前的演示中的那个相同。为简洁起见,我们省略了其余内容。

html
<article>
  <section class="background"></section>
  <section class="content">
    <div class="progress"></div>
    <!-- Content here -->
  </section>
</article>

JavaScript

在我们的脚本中,我们首先获取对 <article> 元素的引用。然后我们定义一个名为 setContainerWidth() 的函数,该函数通过 Element.getBoundingClientRect() 获取 <article> 的客户端宽度,并在其上设置一个名为 --container-width 的自定义属性,该属性等于客户端宽度向下取整后附加 px 的值。

然后我们在 Window 对象上设置一个 resize 事件监听器,当浏览器窗口调整大小时运行 setContainerWidth()。我们还在页面加载时运行它一次,以在 <article> 元素上设置 --container-width 自定义属性。

js
const articleElem = document.querySelector("article");

function setContainerWidth() {
  const clientWidth = articleElem.getBoundingClientRect().width;
  articleElem.style.setProperty(
    "--container-width",
    `${Math.floor(clientWidth)}px`,
  );
}

window.addEventListener("resize", setContainerWidth);

setContainerWidth();

设置好之后,我们现在可以根据 --container-width 设置一些属性值,这样我们的设计部分就会随着窗口调整大小而动态变化。

CSS

以下部分仅解释与我们在演示中使用 progress() 函数相关的 CSS。有关完整的 CSS,请参阅 CSS 源代码

我们首先使用 flexbox<article><body> 内居中,然后在其上设置一些自定义属性来表示我们将在其他地方使用的 min-widthmax-width 值。然后我们针对 <article> 元素,给它 min-widthmax-width 值,等于我们之前设置的自定义属性。我们将其 position 设置为 relative,以便我们可以相对于它定位其内容,然后给它一个百分比的 width、固定的 heightborder

css
body {
  height: inherit;
  display: flex;
  justify-content: center;
  align-items: center;
  --custom-min-width: 320px;
  --custom-max-width: 1200px;
}

article {
  min-width: var(--custom-min-width);
  max-width: var(--custom-max-width);
  position: relative;
  width: 70%;
  height: 600px;
  border: 3px solid black;
}

现在来看我们的 progress <div>。我们将其 width 设置为等于一个百分比,该百分比基于通过 JavaScript 在 <article> 元素上设置的 --container-width 自定义属性在其 min-widthmax-width 之间的进度比率(我们在这里为第二个和第三个 progress() 参数使用与 <article>min-widthmax-width 相同的自定义属性)。

我们还给它一个 heightbackground-color,然后将其绝对定位在 <article> 的左上角。

css
.progress {
  width: calc(
    progress(
        var(--container-width),
        var(--custom-min-width),
        var(--custom-max-width)
      ) *
      100%
  );
  height: 4px;
  background-color: red;
  position: absolute;
  top: 0;
  left: 0;
}

接下来,我们来看我们的 background <section>。我们将其相对于 <article> 绝对定位,在其上设置 inset: 0,使其采用相同的大小并覆盖在其上方。然后我们在其上设置一个相当宽的 background-image,并通过给 background-position-x 属性与进度条的 width 属性相同的值来定位背景图片。这样做的效果是,当你增加浏览器窗口的宽度时,背景图片会向左移动,从而产生一个很好的图片滚动效果。

css
.background {
  position: absolute;
  inset: 0;
  background-image: url("https://mdn.github.io/shared-assets/images/examples/wide-background.jpg");
  background-position-x: calc(
    progress(
        var(--container-width),
        var(--custom-min-width),
        var(--custom-max-width)
      ) *
      100%
  );
}

我们将 content <section> 绝对定位,使其覆盖在 background <section> 之上,然后给它一些 padding。然后,我们使用与之前相同的进度比率,在浏览器窗口调整大小时改变两个属性值:

  • 我们改变 background-color 的 R 和 G 分量,在每种情况下都将进度比率乘以 255,以获得成比例的通道值。随着窗口变宽,背景颜色变得不那么蓝而更白,使场景看起来像是从黑夜到白天(颜色值的不透明度为 0.5,因此它起到为底层图像着色的作用)。
  • 我们改变 opacity,以便当窗口变宽时,内容会稍微淡入。
css
.content {
  position: absolute;
  inset: 0;
  padding: 20px;
  background-color: rgb(
    calc(
        255 *
          progress(
            var(--container-width),
            var(--custom-min-width),
            var(--custom-max-width)
          )
      )
      calc(
        255 *
          progress(
            var(--container-width),
            var(--custom-min-width),
            var(--custom-max-width)
          )
      )
      255 / 0.5
  );
  opacity: calc(
    (
        progress(
            var(--container-width),
            var(--custom-min-width),
            var(--custom-max-width)
          ) /
          2
      ) +
      0.5
  );
}

规范

规范
CSS 值和单位模块 Level 5
# progress

浏览器兼容性

另见