Timer-Balken in CSS mit benutzerdefinierten Eigenschaften

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich habe neulich an einer Sache gearbeitet, bei der ein sichtbarer Timer benötigt wurde. Es gab auf dem Projekt bereits UI-Präzedenzfälle für diese Art von Timer. Die Leute wollten keine Zahlen nach unten ticken sehen; es war besser, einen "Balken" zu sehen, der von voll zu leer abnimmt. Ich erwähne das, weil es unzählige Möglichkeiten gibt, eine "Timer"-UI zu gestalten. Dies ist keine Erkundung all dieser Möglichkeiten (eine Suche auf CodePen wäre dort hilfreicher), sondern eine Erkundung der einen Methode, die für mich nützlich war.

Die Art von Timer, die ich brauchte, war das, was das Projekt als "Rundenzeit"-Balken bezeichnete. Eine Aktion wird ausgeführt. Sie kann eine Rundenzeit verursachen, und die meisten weiteren Aktionen sind blockiert, bis die Rundenzeit vorbei ist. Also war ein klarer roter Balken, der abläuft, die richtige UI. Er vermittelt ein Gefühl von Rhythmus und Fluss, bei dem man das Ende des Timers spüren und seine nächste Aktion timen kann.

eine linear-Animation, die den Balken auf Null schrumpfen lässt.

Das einzurichten ist ziemlich einfach…

Geben wir uns ein Eltern/Kind-Konstrukt, nur für den Fall, dass wir den leeren Teil des Containers irgendwann stylen wollen.

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

Vorerst stylen wir nur den Balken darin.

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

Das gibt uns einen schönen kleinen roten Balken, den wir für die Zeitanzeige verwenden können.

Als Nächstes müssen wir ihn zum Ticken bringen, aber hier müssen wir über die Funktionalität nachdenken. Ein Timer wie dieser muss wissen, wie lange er zählt! Wir können ihm diese Information direkt im HTML mitgeben. Das bedeutet nicht, dass wir JavaScript vermeiden – wir umarmen es. Wir sagen: "Hey JavaScript, gib uns bitte die Dauer als Variable, und wir kümmern uns von da an darum."

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

Tatsächlich ist dieser Weg sehr freundlich zu modernem DOM-Handling mit JavaScript. Solange diese --variable korrekt ist, kann dieses DOM-Element jederzeit neu gerendert werden, und wir können sicherstellen, dass das Design dies problemlos bewältigt. Wir werden eine Variante erstellen, die das tut.

Vorerst lassen wir die Animation ablaufen. Gute Nachrichten, es ist einfach. Hier ist ein Einzeiler-Keyframe

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

Wir können den Balken "zusammendrücken", da das Design des Balkens nichts hat, das beim horizontalen Skalieren gequetscht aussehen würde. Wenn wir etwas hätten, könnten wir die width animieren. Es ist nicht so schlimm, besonders da es nichts anderes umbrechen muss.

Jetzt wenden wir es auf den Balken an

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

Sehen Sie, wie wir die Variable --duration verwenden, um die Dauer der Animation festzulegen? Das macht die Hauptarbeit. Ich verwende sie auch, um die gleiche Anzahl von steps() festzulegen, damit sie "heruntertickt". Dieses "Ticken" könnte ein visuelles UI-Element sein, das Ihnen gefällt (mir gefällt es), aber es kommt auch der Idee entgegen, dass JavaScript diesen Balken jederzeit neu rendern könnte, und das Ticken macht es weniger wahrscheinlich, dass Sie es bemerken. Ich habe eine Ganzzahl für den Dauerwert verwendet, damit er diese Doppelfunktion erfüllen kann.

Wenn Sie jedoch eine flüssige Animation wünschen, könnten wir das als Variante machen, wie zum Beispiel:

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

Dann nicht die Schritte machen

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

Beachten Sie, dass wir auch eine linear-Animation verwenden, was für einen Timer sinnvoll zu sein scheint. Die Zeit verlangsamt sich, sozusagen, nicht. Oder tut sie das? Egal, das ist Ihre Entscheidung. Wenn Sie einen Timer möchten, der an bestimmten Punkten zu beschleunigen oder zu verlangsamen scheint, legen Sie los.

Wir können die gleiche API, die auf Datenattributen basiert, für Variationen wie Farbvariationen verwenden

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

Und eine letzte Variante ist, jede "Sekunde" eine feste Breite zu geben. So wird ein 10-Sekunden-Timer buchstäblich länger aussehen als ein 5-Sekunden-Timer

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

Hier ist die Demo

Beachten Sie den kleinen Trick dort, um CSS-Animationen neu zu starten.

Oh, und hey, ich weiß, es gibt ein <meter>-Element, das vielleicht etwas semantischer ist, aber es bringt seine eigene UI mit, die nicht so animierbar ist, wie ich es hier wollte – zumindest nicht, ohne dagegen anzukämpfen. Aber ich frage mich, ob es zugänglicher ist? Kündigt es seinen aktuellen Wert auf nützliche Weise an? Wäre es ein zugänglicheres Timer, wenn wir einen <meter> in Echtzeit mit JavaScript aktualisieren würden? Wenn jemand es weiß, kann ich hier eine Lösung verlinken.