CSS Pie Timer

Avatar of Kitty Giraudel
Kitty Giraudel am

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

Hinweis der Redaktion: Kitty Giraudel hat eine coole Demo für einen "Pie Timer" auf CodePen erstellt. Es ist definitiv keine intuitive oder einfache Sache, das in CSS zu machen. Dann schickte sie mir einen Artikel darüber, wie sie es gemacht hat, und ich muss sagen, es ist ein wirklich schöner CSS-Tricks-Artikel geworden. Danke Kitty!

Was machen wir?

Sie haben vielleicht einige dieser Loader gesehen, meist auf Flash-Websites. Es ist im Grunde ein Kuchenstück, das immer größer wird, bis es ein ganzer Kreis ist.

Zuerst dachte ich, es wäre ein Kinderspiel, einfach einen Kreis zu nehmen, ihn drehen zu lassen, einen Teil davon hinter einer Maske zu verstecken, und fertig. Nun, es stellte sich heraus, dass es viel schwieriger ist. Tatsächlich ist CSS für eine solche Aufgabe nicht vorbereitet, selbst mit Präprozessoren wie Sass & Compass. Wir kämpfen immer mit Formen, und noch mehr, wenn wir diese stylen oder animieren müssen. Meistens gelingt es uns, eine Lösung zu finden und etwas funktionierendes zu erhalten, auf Kosten der Wartbarkeit oder Semantik.

Warum das tun?

Ich denke, der häufigste Anwendungsfall wären Timer. Aber diese Konzepte könnten auch verwendet werden, um Tortendiagramme mit reinem CSS zu erstellen. Wenn Sie...

Obwohl es einige tolle Werkzeuge gibt, um Tortendiagramme zu erstellen (hauptsächlich mit JavaScript), könnten wir wahrscheinlich leicht herausfinden, wie man Tortendiagramme nur mit CSS erstellt und diese sogar mit solchen Tricks animiert.
Es gibt ein Tutorial über das Erstellen von CSS-Tortendiagrammen mit der clip-Eigenschaft auf der Website von Atomic Noggin Enterprise.

Nun, das ist eine Workaround mit schlechter Semantik! Aber die Wartbarkeit ist nicht so schlecht, also los geht's.

Das HTML

Wir brauchen 3 verschiedene Elemente

  • einen Spinner: das ist die Halbkreis, die während des gesamten Vorgangs rotiert
  • eine Maske: das ist das Element, das den Spinner während der ersten 50% der Animation versteckt
  • einen Füller: das ist das Element, das den Kreis während der letzten 50% der Animation vervollständigt

Und wir brauchen all diese Elemente im selben Elternteil, um absolute Positionierung zu ermöglichen

<div class="wrapper">
<div class="pie spinner"></div>
<div class="pie filler"></div>
<div class="mask"></div>
</div>
 

Da der Spinner und der Füller zwei Hälften desselben Kreises sind, verwenden wir eine gemeinsame Klasse (.pie), um sie zu stylen.

 
Das CSS

Um den Code in diesem Artikel sauber und verständlich zu halten, werden wir keine Vendor-Präfixe hinzufügen.

Das übergeordnete Element richtet die Größe und den absoluten Positionierungskontext für den Timer ein

.wrapper {
  width: 250px;
  height: 250px;
  position: relative;
  background: white;
}

Es ist wichtig, sicherzustellen, dass Breite und Höhe gleich sind, um einen Kreis zu bilden und sicherzustellen, dass alles funktioniert.

Der "Spinner" und der "Füller" teilen sich dieses CSS

.pie {
  width: 50%;
  height: 100%;
  position: absolute;
  background: #08C;
  border: 10px solid rgba(0,0,0,0.4);
}

Ihre Breite beträgt 50% der übergeordneten Breite, da sie beide Teil desselben Kreises sind, und ihre Höhe ist dieselbe wie die übergeordnete Höhe. Wir geben ihnen auch etwas Farbe und einen Rand, um sie richtig zu identifizieren.

Der "Spinner"

.spinner {
  border-radius: 125px 0 0 125px;
  z-index: 200;
  border-right: none;
  animation: rota 10s linear infinite;
}

Wir müssen die Sache wie einen Halbkreis aussehen lassen, mit `border-radius` in der oberen linken und unteren linken Ecke. Außerdem geben wir ihm einen positiven hohen `z-index`, um ihn über dem Füller, aber hinter der Maske zu platzieren.

Dann fügen wir die `animation` hinzu, die 10 Sekunden dauert. Wir werden später mehr über Animationen sprechen.

Der "Füller"

.filler {
  border-radius: 0 125px 125px 0;
  z-index: 100;
  border-left: none;
  animation: fill 10s steps(1, end) infinite;
  left: 50%;
  opacity: 0;
}

Für den Spinner setzen wir `border-radius` und `z-index`, entfernen `border-left` und stellen die Animationsdauer auf 10 Sekunden ein. Für dieses Element ist `animation-timing-function` nicht auf `linear` eingestellt, sondern auf `steps(1, end)`. Das bedeutet, dass die Animation nicht schrittweise von 0% auf 100% fortschreitet, sondern sofort erfolgt.

Da der Füller im ersten Teil der Animation nicht sichtbar sein wird, setzen wir seine `opacity` auf 0 und seine Position auf 50% der übergeordneten Breite.

Die "Maske"

.mask {
  width: 50%;
  height: 100%;
  position: absolute;
  z-index: 300;
  opacity: 1;
  background: inherit;
  animation: mask 10s steps(1, end) infinite;
}

Die Maske ist von Anfang der Animation an vorhanden, daher ist ihre `opacity` auf 1 gesetzt und ihr Hintergrund wird vom Hintergrund des Elternteils übernommen (um sie unsichtbar zu machen). Um den Spinner abzudecken, hat sie die gleiche Größe wie dieser, und ihr `z-index` ist auf 300 gesetzt.

Die Keyframes

@keyframes rota {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

@keyframes fill {
  0%        { opacity: 0; }
  50%, 100% { opacity: 1; }
}

@keyframes mask {
  0%        { opacity: 1; }
  50%, 100% { opacity: 0; }
}

Die erste Animation (`rota`) ist für den Spinner. Sie dreht sich progressiv von 0 auf 360 Grad in 10 Sekunden.

Die zweite Animation (`fill`) ist für den Füller. Sie geht sofort von 0 auf 1 `opacity` nach 5 Sekunden.

Die letzte Animation (`mask`) ist für die Maske. Sie geht sofort von 1 auf 0 `opacity` nach 5 Sekunden.

Die Animation sieht also so aus

  • T0 – der Spinner ist links, versteckt durch die Maske. Der Füller ist versteckt.
  • T1 – der Spinner beginnt sich im Uhrzeigersinn zu drehen und erscheint langsam hinter der Maske.
  • T2 – der Spinner hat sich 360/10*2 = 72° gedreht und dreht sich weiter.
  • T3 – der Spinner hat sich 360/10*3 = 108° gedreht und dreht sich weiter.
  • T4 – der Spinner hat sich 360/10*4 = 144° gedreht und dreht sich weiter.
  • T5 – der Spinner hat sich 360/10*5 = 180° gedreht und dreht sich weiter. In diesem Moment erhält der Füller sofort 100% `opacity`, während die Maske verschwindet.
  • T6 – der Spinner hat sich 360/10*6 = 216° gedreht und dreht sich weiter.
  • T7 – der Spinner hat sich 360/10*7 = 252° gedreht und dreht sich weiter.
  • T8 – der Spinner hat sich 360/10*8 = 288° gedreht und dreht sich weiter.
  • T9 – der Spinner hat sich 360/10*9 = 324° gedreht und dreht sich weiter.
  • T10 – der Spinner hat sich 360° gedreht und ist zu seinem Ausgangspunkt zurückgekehrt. Dann starten wir die Animation neu. Die Maske erhält 100% `opacity`, während der Füller verschwindet.

Bonus

Hier sind einige zusätzliche Tricks, die ziemlich cool sein können, je nachdem, was Sie wollen.

Pause beim Überfahren mit der Maus

.wrapper:hover .filler,
.wrapper:hover .spinner,
.wrapper:hover .mask {
  animation-play-state: paused;
}

Mit diesem Codeausschnitt können Sie die Animation durch Überfahren des übergeordneten Elements anhalten.

Inhalt im Inneren

Dank `z-index` können wir problemlos Inhalt in den Spinner einfügen und ihn auf die gleiche Weise drehen lassen. Versuchen Sie, den folgenden Ausschnitt zu Ihrem Code hinzuzufügen

.spinner:after {
  content: "";
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  top: 10px;
  right: 10px;
  background: #fff;
  border: 1px solid rgba(0,0,0,0.4);
  box-shadow: inset 0 0 3px rgba(0,0,0,0.2);
}

Präprozessoren oder CSS-Variablen

Derzeit ist dies nicht sehr einfach zu warten. Aber wenn wir Variablen verwenden (die Präprozessoren oder die kommenden nativen CSS-Variablen), können wir es einfacher machen. Zum Beispiel könnten Sie eine Variable hinzufügen, um die Dauer zu verwalten, anstatt sie in den 3 Animationsdeklarationen ändern zu müssen.
Wenn Sie die Wartbarkeit erleichtern möchten, ohne sich mit Präprozessoren zu befassen, könnten Sie eine Klasse erstellen, die nur die Animationsdauer verwaltet, und diese Klasse zu den 3 Kinderelementen hinzufügen. Es würde ungefähr so aussehen

.animation-duration {
  animation-duration: 10s;
}

Nachteile

Leider gibt es einige Dinge, die diese Technik nicht kann

  • Keine Unterstützung für Farbverläufe (sieht furchtbar aus)
  • Keine Unterstützung für `box-shadows` (sieht schmutzig aus)
  • Ist nicht vollständig responsiv. Wenn Sie die Abmessungen des übergeordneten Elements ändern, funktioniert alles bis auf den `border-radius`. Sie müssen die `border-radius`-Werte immer noch manuell ändern, da wir keinen 50% `border-radius` einstellen können, es sei denn, wir haben es mit einem perfekten Quadrat zu tun.
  • Nicht semantisch (4 Elemente für eine einzige Animation). Zukünftige multiple Pseudoelemente oder Web Components könnten hier helfen.

Browser-Unterstützung

Da wir CSS-Animationen und Keyframes verwenden, ist die Browserunterstützung ziemlich gering, wird sich aber mit der Zeit verbessern. Vorerst unterstützen nur die folgenden Browser CSS-Animationen:

  • Internet Explorer 10
  • Firefox 12+
  • Chrome
  • Safari 5+
  • Opera 12+
  • iOS Safari 3+
  • Android 2+ (fehlerhaft bis v4)

Demo

Demo auf CodePen ansehen