使用自定义属性在 CSS 中创建计时器条

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您的旅程各个阶段提供云产品。 立即开始使用 价值 200 美元的免费积分!

前几天我在做一件事,需要一个可见的计时器。 该项目中此类计时器有 UI 先例。 人们不想看到数字向下计时;理想情况下是看到一个“条”从满到空逐渐消失。 我之所以提到这一点,是因为您可以通过多种方式实现“计时器”UI。 这不是对所有这些方法的探讨(在 CodePen 上搜索 会更有帮助),而只是对对我来说有用的一种方法的探讨。

我需要的计时器类型是项目中称为“回合时间”的条形。 执行一个操作。 它可能会导致回合时间,并且大多数后续操作将在回合时间结束之前被阻止。 因此,一个非常清晰的红色条形向下计时是正确的 UI。 它提供了一种节奏和流动感,您可以从中感觉到计时器的结束时间并为下一步操作计时。

一个linear动画,将条形缩小到零。

设置起来相当容易……

为了以防万一我们想在某个时候对容器的空白部分进行样式设置,让我们给自己一个父/子元素。

<div class="round-time-bar">
  <div></div>
</div>

现在,让我们只对内部的条形进行样式设置。

.round-time-bar div {
  height: 5px;
  background: linear-gradient(to bottom, red, #900);
}

这给了我们一个漂亮的小红色条形,我们可以用它来作为时间指示器。

接下来我们需要让它向下计时,但在这里我们需要考虑功能。 这样的计时器需要知道它正在计时多长时间! 我们可以直接在HTML中提供这些信息。 这并不意味着我们正在避免JavaScript - 我们正在拥抱它。 我们在说,“嘿,JavaScript,请给我们一个变量作为持续时间,我们会接着处理。”

<div class="round-time-bar" style="--duration: 5;">
  <div></div>
</div>

事实上,这种方式对现代 DOM 处理 JavaScript 非常友好。 只要--variable是正确的,它就可以随时重新渲染该 DOM 元素,我们可以确保设计可以很好地处理这一点。 我们将做一个会这样做的变体。

现在,让我们让动画发生。 好消息是,这很简单。 这是一个单行关键帧

@keyframes roundtime {
  to {
    /* More performant than animating `width` */
    transform: scaleX(0);
  }
}

我们可以“压缩”条形,因为条形的设计没有任何东西在水平缩放时会看起来被压缩。 如果我们有,我们可以对width进行动画处理。 这不是那么大不了,尤其是因为它没有重新流式任何其他内容。

现在我们将它应用到条形上

.round-time-bar div {
  /* ... */
  animation: roundtime calc(var(--duration) * 1s) steps(var(--duration)) forwards;
  transform-origin: left center;
}

看到我们是如何拉取--duration变量来设置动画的持续时间的吗? 这完成了繁重的工作。 我也用它来设置相同数量的steps(),以便它“向下计时”。 这种“计时”可能是您喜欢的视觉 UI 东西(我也喜欢),但它也适应了 JavaScript 可能会随时重新渲染此条形的概念,而计时使您不太可能注意到。 我使用了整数作为持续时间值,以便它可以像这样发挥双重作用。

但是,如果您想要平滑的动画,我们可以像这样进行变体处理,例如

<div class="round-time-bar" data-style="smooth" ... />

然后不要进行步骤处理

.round-time-bar[data-style="smooth"] div {
  animation: roundtime calc(var(--duration) * 1s) linear forwards;
}

请注意,我们还使用了linear动画,这似乎对计时器很有意义。 时间,正如它所是的那样,不会减速。 或者会吗? 无论如何,这是你的决定。 如果你想要一个看起来在某些点加速或减速的计时器,那就去做吧。

我们可以使用相同的变体数据属性驱动 API 来处理诸如颜色变体之类的事情

.round-time-bar[data-color="blue"] div {
  background: linear-gradient(to bottom, #64b5f6, #1565c0);
}

最后一个变体是使每个“秒”具有固定的宽度。 这样,一个 10 秒的计时器看起来会比一个 5 秒的计时器长

.round-time-bar[data-style="fixed"] div {
  width: calc(var(--duration) * 5%);
}

这是演示

注意其中用于重新启动 CSS 动画的小技巧。

哦,对了,我知道有一个<meter>元素,它可能更具语义性,但它带来了自己的 UI,而我在这里并不希望它具有动画性——至少在没有与它作斗争的情况下。 但我想知道它是否更易于访问? 它是否以一种有用的方式宣布其当前值? 如果我们使用 JavaScript 实时更新<meter>,它会是一个更易于访问的计时器吗? 如果有人知道,我可以在这里链接一个解决方案。