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.
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
Obwohl das eine ziemlich coole Demonstration der Stärke von CSS ist, frage ich mich wieder einmal: Warum nicht SVG?
Machen Sie eine Demo!
Codepen: SVG Pie Timer
Basierend auf dem HSL-Spinner, der von Lars Gunther gepostet wurde.
Tatsächlich funktionieren Farbverläufe unter bestimmten Bedingungen.
Die Ränder dürfen nicht transparent sein (wie in der Demo) und die Farbverläufe müssen radial sein (natürlich).
Außerdem muss der Farbverlauf bei `.filler` linksbündig (`left center`) und der bei `.spinner` rechtsbündig (`right center`) ausgerichtet sein.
Außerdem sollten durch leichte Anpassung der Animationen auch andere Dinge (vielleicht sogar `box-shadows`) funktionieren (die `opacity` muss sich sofort ändern, was bedeutet
oder so etwas...)
Ich werde bald eine Demo machen :)
Jetzt hatte ich Zeit, die Demo zu erstellen.
Sie demonstriert Farbverläufe und einige weitere Dinge, sehen Sie selbst!
Hey, wirklich gute Arbeit, Felix, das gefällt mir!
Wie ich im Artikel sagte, wusste ich bereits, dass man Inhalte darauf platzieren kann (bezogen auf das "Click me"-Pseudoelement), und mit der `border-radius`-Korrektur wusste ich auch, dass das Ding vollständig responsiv sein kann. übrigens, wir hätten die `transform: scale()`-Eigenschaft verwenden können, um es größer oder kleiner zu machen. ;)
Aber deine Arbeit mit Farbverläufen sieht großartig aus! Ich muss mich definitiv mit `radial-gradients` beschäftigen. Wie auch immer, danke, dass du so eine Demo gemacht hast. :)
Hallo! Danke Chris, es ist eine Ehre, meine Arbeit auf CSS Tricks veröffentlicht zu sehen. :)
Dank Joshua Hibbert habe ich endlich herausgefunden, wie man das reaktionsfähige Problem mit `border-radius` löst.
Das stimmt, wir können so etwas nicht tun
Aber wir können die "komplexe" `border-radius`-Eigenschaft verwenden, um es zu erreichen, indem wir einfach
Auf diese Weise müssen wir die `border-radius`-Werte nicht ändern, wenn wir den gesamten Lader größer oder kleiner machen wollen. :)
@Felix Kiunke: Ich würde gerne sehen, wie du mit `radial-gradients` umgehst. Bitte lass es mich wissen, wenn du eine Demo machst. :)
Um die `opacity`-Änderungen sofort durchzuführen, möchten Sie vielleicht `steps(1,start/end)` verwenden, ich denke, das funktioniert ziemlich gut!
Tolle Zusammenfassung, Hugo!
Ein reiner CSS-Spinner: http://codepen.io/tmyg/pen/bwLom
Derzeit nur Webkit, könnte wahrscheinlich auch in Firefox funktionieren.
Spoiler-Alarm... :)
Nur für den Fall, dass Sie eine visuelle Unterstützung für Hugos (ausgezeichnete!) Schritt-für-Schritt-Erklärung benötigen. Das Ändern einiger Hintergrundfarben reicht aus.
http://codepen.io/anon/pen/tBryp
Gute Arbeit, Hugo!
Und vielleicht wurde das oben erwähnt, aber aus irgendeinem Grund gibt mir ein Teil des Posts die Fehlermeldung "Es tut uns leid, aber etwas ist schiefgelaufen". Könnte daran liegen, dass ich an meinem Arbeitslaptop bin.
Danke dafür, Keith!
Das macht es viel einfacher zu verstehen, wie alles funktioniert. Sehr coole verwendete Techniken.
Chris, nicht genau dasselbe und nicht so schön, da mir Designfähigkeiten fehlen, aber schau dir den inneren Kreis in dieser SVG-basierten Demo an.
Ich bin noch nicht von CSS3-Animationen überzeugt. Ich liebe die Tatsache, dass wir das können, aber es gibt meiner Meinung nach einfach zu viele Nachteile.
Der Mangel an Browserunterstützung ist offensichtlich der Hauptgrund, aber als Entwickler und nicht als Designer finde ich jQuery-basierte Animationspakete "einfacher" zu verwenden und zu interagieren.
Ich denke, das ist eine großartige Art, Dinge zu tun, aber ich kann nicht so viel CSS dafür aufwenden, um etwas zu tun, das ich in wenigen Zeilen in JQ machen kann.
Brillanter Blog übrigens :)
Ich stimme dir vollkommen zu. Es ist nur zum Spaß (und zur Herausforderung). :)
Rich, ich stimme vollkommen zu, dass etwas wie das, wenn man es tatsächlich in einer Produktionswebsite verwenden möchte, besser JavaScript und jQuery überlassen sollte. Aber als Demo, um herauszufinden, was möglich ist, ist das sehr beeindruckend.
Für kleinere Dinge sind CSS-Animationen jedoch perfekt. Sie degradieren gut, sie sind Teil des Stylings, das man sowieso machen muss, und man bekommt die Barrierefreiheits-Befürworter von hinten, weil das, was man tut, nicht auf JavaScript basiert und trotzdem von Screenreadern und ähnlichem gesehen werden kann.
Für einfache Animationen dauert es auch definitiv nicht länger! Es ist eher kürzer!
Zum Beispiel, um den Hintergrund eines Navigationsbuttons beim Überfahren mit der Maus dunkler zu machen, unter Verwendung des folgenden HTML
Mit CSS-Animationen
Mit jQuery
Wenn Sie einen nicht unterstützten Browser haben, springt der Button bei CSS-Animationen von einem zum anderen. Wenn Sie mit jQuery einen nicht unterstützten Browser haben, passiert nichts!
Es hat definitiv seine Vorteile, und kleine Details wie diese sind genau das, wofür sie gedacht sind!
Sie vergessen, dass CSS-Animationen schneller sind http://jsperf.com/jquery-animate-vs-css – der Hauptgrund für mich war der Wechsel zu CSS, als jQuery auf mobilen Geräten schwere Lags verursachte.
Sieht gut aus. Aber meine CPU wird vielleicht heißer. Sie verbraucht etwa 10 %.
Das war ein tolles kleines Tutorial, danke fürs Teilen.
Hugo ist mir immer zwei Schritte voraus. Ich habe einen Transparent Dwindler (Dekrement-Spinner) erstellt, an dem ich noch feile.
Wenn Sie sich CodePen noch nicht angesehen haben, tun Sie es bitte. Es ist in der Beta-Phase, also vergessen Sie nicht, Ihre Unterstützung anzubieten.
Leicht anders, aber genauso effektiv,
http://codepen.io/anon/pen/ItEni
Ludvig Lindblom hat eine etwas andere, aber sehr clevere Version erstellt, die lineare Farbverläufe auf dem Kreis zulässt: http://codepen.io/ludviglindblom/pen/dfjAt.
Tatsächlich lässt er die Maske anstelle des Spinners rotieren und spielt mit `z-index` und `opacity` in der zweiten Hälfte der Animation.
Schauen Sie sich das unbedingt an, es ist auch großartig. ;)
Nicht verwechseln mit dem CSS3 PIE-Projekt. Google hat mich versehentlich hierher geführt!
Ich habe kürzlich eine Möglichkeit gefunden, eine der 3 Animationen zu entfernen, um den Pie Timer zu erstellen.
Die `animation-direction`-Eigenschaft ermöglicht es uns, die Art und Weise zu steuern, wie die Animation abgespielt wird: vorwärts oder rückwärts. Dank dessen benötigen wir keine 2 Animationen mehr für die `opacity`-Steuerung: nur eine und den Rückwärtswert.
Laut MDN
Anstatt also eine Animation von 0 auf 1 `opacity` zu verwenden, verwende ich die von 1 auf 0 und spiele sie rückwärts ab. Also habe ich die "fill"-Animation entfernt und dem `.fill`-Element folgendes gegeben
Schauen Sie sich die Demo an, um zu sehen, wie es funktioniert: http://codepen.io/HugoGiraudel/pen/BHEwo
Immer der Handwerker.
Wenn diese Demo auf dieser Seite eingebettet wird, funktioniert sie in Chrome nicht.
Mit ein wenig jQuery habe ich hier einen flexiblen Pie Countdown-Timer erstellt (Sie können die Gesamtzeit ändern). Ich hoffe, es ist nützlich für Sie :D
http://codepen.io/elaelation/pen/CxgmH?editors=001