Im letzten Artikel haben wir einen ziemlich coolen kleinen Slider (oder "Karussell", wenn Sie das bevorzugen) erstellt, der sich in einer Kreisbewegung dreht. Dieses Mal werden wir einen erstellen, der durch einen Stapel von Polaroidbildern blättert.
Cool, oder? Schauen Sie noch nicht auf den Code, denn es gibt viel zu entwirren. Begleiten Sie mich, ja?
CSS Sliders Serie
- Runddrehender Bilder-Slider
- Durch Polaroidbilder blättern (Sie sind hier!)
- Unendliche 3D-Slider
Die Grundeinrichtung
Ein Großteil des HTML und CSS für diesen Slider ist ähnlich wie beim runden Slider, den wir letztes Mal gemacht haben. Tatsächlich verwenden wir genau die gleiche Markup-Struktur.
<div class="gallery">
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
<img src="" alt="">
</div>
Und dies ist das grundlegende CSS, das unseren übergeordneten .gallery-Container als Gitter einrichtet, in dem alle Bilder übereinander gestapelt sind.
.gallery {
display: grid;
width: 220px; /* controls the size */
}
.gallery > img {
grid-area: 1 / 1;
width: 100%;
aspect-ratio: 1;
object-fit: cover;
border: 10px solid #f2f2f2;
box-shadow: 0 0 4px #0007;
}
Bis hierhin nichts Komplexes. Selbst für den Polaroid-ähnlichen Stil der Bilder verwende ich nur etwas border und box-shadow. Sie können es vielleicht besser machen, also spielen Sie ruhig mit diesen dekorativen Stilen herum! Wir werden uns hauptsächlich auf die Animation konzentrieren, die der kniffligste Teil ist.
Was ist der Trick?
Die Logik dieses Sliders beruht auf der Stapelreihenfolge der Bilder – ja, wir werden mit z-index spielen. Alle Bilder beginnen mit demselben z-index-Wert (2), was logischerweise das letzte Bild oben auf dem Stapel macht.
Wir nehmen dieses letzte Bild und schieben es nach rechts, bis es das nächste Bild im Stapel freilegt. Dann verringern wir den z-index-Wert des Bildes und schieben es zurück in den Stapel. Und da sein z-index-Wert niedriger ist als der Rest der Bilder, wird es zum letzten Bild im Stapel.
Hier ist eine abgespeckte Demo, die den Trick zeigt. Fahren Sie mit der Maus über das Bild, um die Animation zu aktivieren.
Stellen Sie sich nun denselben Trick für alle Bilder vor. Hier ist das Muster, wenn wir den :nth-child()-Pseudo-Selektor verwenden, um die Bilder zu unterscheiden:
- Wir schieben das letzte Bild (
N). Das nächste Bild ist sichtbar (N - 1). - Wir schieben das nächste Bild (
N - 1). Das nächste Bild ist sichtbar (N - 2). - Wir schieben das nächste Bild (
N - 2). Das nächste Bild ist sichtbar (N - 3). - (Wir setzen den gleichen Prozess fort, bis wir das erste Bild erreichen)
- Wir schieben das erste Bild (
1). Das letzte Bild (N) ist wieder sichtbar.
Das ist unser unendlicher Slider!
Zerlegung der Animation
Wenn Sie sich an den vorherigen Artikel erinnern, habe ich nur eine Animation definiert und mit Verzögerungen gearbeitet, um jedes Bild zu steuern. Das Gleiche werden wir hier tun. Versuchen wir zunächst, die Zeitachse unserer Animation zu visualisieren. Wir beginnen mit drei Bildern und verallgemeinern sie dann für eine beliebige Anzahl (N) von Bildern.

Unsere Animation ist in drei Teile unterteilt: "nach rechts schieben", "nach links schieben" und "nicht bewegen". Wir können die Verzögerung zwischen den einzelnen Bildern leicht erkennen. Wenn wir davon ausgehen, dass das erste Bild bei 0s beginnt und die Dauer gleich 6s ist, dann beginnt das zweite bei -2s und das dritte bei -4s.
.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */
Wir können auch sehen, dass der Teil "nicht bewegen" zwei Drittel der gesamten Animation (2*100%/3) dauert, während die Teile "nach rechts schieben" und "nach links schieben" zusammen ein Drittel davon ausmachen – also ist jeder Teil gleich 100%/6 der gesamten Animation.
Wir können unsere Animations-Keyframes so schreiben:
@keyframes slide {
0% { transform: translateX(0%); }
16.67% { transform: translateX(120%); }
33.34% { transform: translateX(0%); }
100% { transform: translateX(0%); }
}
Diese 120% ist ein willkürlicher Wert. Ich brauchte etwas Größeres als 100%. Die Bilder müssen nach rechts weg vom Rest der Bilder geschoben werden. Dazu muss es sich mindestens um 100% seiner Größe bewegen. Deshalb bin ich auf 120% gegangen – um etwas zusätzlichen Platz zu gewinnen.
Nun müssen wir den z-index berücksichtigen. Vergessen Sie nicht, dass wir den z-index-Wert des Bildes aktualisieren müssen, *nachdem* es nach rechts aus dem Stapel geschoben wurde, und *bevor* wir es zurück an den unteren Rand des Stapels schieben.
@keyframes slide {
0% { transform: translateX(0%); z-index: 2; }
16.66% { transform: translateX(120%); z-index: 2; }
16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
33.34% { transform: translateX(0%); z-index: 1; }
100% { transform: translateX(0% ); z-index: 1; }
}
Anstatt einen Zustand bei 16,67% (100%/6) im Zeitverlauf zu definieren, definieren wir zwei Zustände an nahezu identischen Punkten (16,66% und 16,67%), an denen der z-index-Wert sinkt, bevor wir das Bild zurück in den Stapel schieben.
Hier ist, was passiert, wenn wir all das zusammenbringen:
Hm, der Schiebe-Teil scheint gut zu funktionieren, aber die Stapelreihenfolge ist komplett durcheinander! Die Animation beginnt gut, da sich das oberste Bild nach hinten bewegt... aber die nachfolgenden Bilder folgen nicht. Wenn Sie bemerken, kehrt das zweite Bild in der Sequenz an den oberen Rand des Stapels zurück, bevor das nächste Bild darüber aufblitzt.
Wir müssen die z-index-Änderungen genau verfolgen. Anfangs haben alle Bilder z-index: 2. Das bedeutet, die Stapelreihenfolge sollte so sein...
Our eyes 👀 --> 3rd (2) | 2nd (2) | 1st (2)
Wir schieben das dritte Bild und aktualisieren seinen z-index, um diese Reihenfolge zu erhalten:
Our eyes 👀 --> 2nd (2) | 1st (2) | 3rd (1)
Dasselbe machen wir mit dem zweiten:
Our eyes 👀 --> 1st (2) | 3rd (1) | 2nd (1)
...und dem ersten:
Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)
Wenn wir das tun, scheint alles in Ordnung zu sein. Aber in Wirklichkeit ist es das nicht! Wenn das erste Bild nach hinten verschoben wird, beginnt das dritte Bild eine weitere Iteration, was bedeutet, dass es zu z-index: 2 zurückkehrt.
Our eyes 👀 --> 3rd (2) | 2nd (1) | 1st (1)
Also, in Wirklichkeit hatten wir nie alle Bilder auf z-index: 2! Wenn sich die Bilder nicht bewegen (d.h. der "nicht bewegen"-Teil der Animation), ist der z-index 1. Wenn wir das dritte Bild schieben und seinen z-index-Wert von 2 auf 1 ändern, bleibt es oben! Wenn alle Bilder denselben z-index haben, ist das letzte in der Quellreihenfolge – in diesem Fall unser drittes Bild – oben auf dem Stapel. Das Schieben des dritten Bildes führt zu Folgendem:
Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)
Das dritte Bild ist immer noch oben, und direkt danach rücken wir das zweite Bild nach oben, wenn seine Animation mit z-index: 2 neu startet.
Our eyes 👀 --> 2nd (2) | 3rd (1) | 1st (1)
Sobald wir es verschoben haben, erhalten wir:
Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)
Dann springt das erste Bild nach oben.
Our eyes 👀 --> 1st(2) | 3rd (1) | 2nd (1)
OK, ich bin verwirrt. Die ganze Logik ist dann falsch?
Ich weiß, es ist verwirrend. Aber unsere Logik ist nicht völlig falsch. Wir müssen die Animation nur ein wenig korrigieren, damit alles so funktioniert, wie wir es wollen. Der Trick besteht darin, den z-index korrekt zurückzusetzen.
Nehmen wir die Situation, in der das dritte Bild oben ist.
Our eyes 👀 --> 3rd (2) | 2nd (1) | 1st (1)
Wir haben gesehen, dass das Schieben des dritten Bildes und das Ändern seines z-index es oben hält. Was wir tun müssen, ist, den z-index des zweiten Bildes zu aktualisieren. Bevor wir das dritte Bild aus dem Stapel schieben, aktualisieren wir also den z-index des zweiten Bildes auf 2.
Mit anderen Worten, wir setzen den z-index des zweiten Bildes zurück, bevor die Animation endet.

Das grüne Pluszeichen stellt eine Erhöhung des z-index auf 2 dar, und das rote Minuszeichen entspricht z-index: 1. Das zweite Bild beginnt mit z-index: 2, dann aktualisieren wir es auf 1, wenn es aus dem Stapel geschoben wird. Aber bevor das erste Bild aus dem Stapel geschoben wird, ändern wir den z-index des zweiten Bildes zurück auf 2. Dies stellt sicher, dass beide Bilder denselben z-index haben, aber immer noch das dritte bleibt oben, da es später im DOM erscheint. Aber nachdem das dritte Bild geschoben wurde und sein z-index aktualisiert wurde, bewegt es sich nach unten.
Dies geschieht zwei Drittel der Animation, also aktualisieren wir unsere Keyframes entsprechend:
@keyframes slide {
0% { transform: translateX(0%); z-index: 2; }
16.66% { transform: translateX(120%); z-index: 2; }
16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
33.34% { transform: translateX(0%); z-index: 1; }
66.33% { transform: translateX(0%); z-index: 1; }
66.34% { transform: translateX(0%); z-index: 2; } /* and also here */
100% { transform: translateX(0%); z-index: 2; }
}
Ein bisschen besser, aber immer noch nicht ganz richtig. Es gibt noch ein Problem...
Oh nein, das hört nie auf!
Keine Sorge, wir werden die Keyframes nicht noch einmal ändern, denn dieses Problem tritt nur auf, wenn das letzte Bild beteiligt ist. Wir können eine "spezielle" Keyframe-Animation speziell für das letzte Bild erstellen, um die Dinge zu reparieren.
Wenn das erste Bild oben ist, haben wir folgende Situation:
Our eyes 👀 --> 1st (2) | 3rd (1) | 2nd (1)
Unter Berücksichtigung der vorherigen Anpassung wird das dritte Bild vor dem ersten Bild nach oben springen. Das passiert nur in dieser Situation, weil das nächste Bild, das nach dem ersten Bild bewegt wird, das *letzte* Bild ist, das eine höhere Ordnung im DOM hat. Der Rest der Bilder ist in Ordnung, weil wir N, dann N - 1 haben, dann gehen wir von 3 zu 2 und 2 zu 1... aber dann gehen wir von 1 zu N.
Um das zu vermeiden, verwenden wir für das letzte Bild die folgenden Keyframes:
@keyframes slide-last {
0% { transform: translateX(0%); z-index: 2;}
16.66% { transform: translateX(120%); z-index: 2; }
16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
33.34% { transform: translateX(0%); z-index: 1; }
83.33% { transform: translateX(0%); z-index: 1; }
83.34% { transform: translateX(0%); z-index: 2; } /* and also here */
100% { transform: translateX(0%); z-index: 2; }
}
Wir setzen den z-index-Wert 5/6 des Weges durch die Animation zurück (anstatt zwei Drittel), was bedeutet, dass das erste Bild aus dem Stapel ist. So sehen wir kein Springen mehr!
TADA! Unser unendlicher Slider ist jetzt perfekt! Hier ist unser endgültiger Code in seiner ganzen Pracht:
.gallery > img {
animation: slide 6s infinite;
}
.gallery > img:last-child {
animation-name: slide-last;
}
.gallery > img:nth-child(2) { animation-delay: -2s; }
.gallery > img:nth-child(3) { animation-delay: -4s; }
@keyframes slide {
0% { transform: translateX(0%); z-index: 2; }
16.66% { transform: translateX(120%); z-index: 2; }
16.67% { transform: translateX(120%); z-index: 1; }
33.34% { transform: translateX(0%); z-index: 1; }
66.33% { transform: translateX(0%); z-index: 1; }
66.34% { transform: translateX(0%); z-index: 2; }
100% { transform: translateX(0%); z-index: 2; }
}
@keyframes slide-last {
0% { transform: translateX(0%); z-index: 2; }
16.66% { transform: translateX(120%); z-index: 2; }
16.67% { transform: translateX(120%); z-index: 1; }
33.34% { transform: translateX(0%); z-index: 1; }
83.33% { transform: translateX(0%); z-index: 1; }
83.34% { transform: translateX(0%); z-index: 2; }
100% { transform: translateX(0%); z-index: 2; }
}
Unterstützung für eine beliebige Anzahl von Bildern
Nun, da unsere Animation für drei Bilder funktioniert, machen wir sie für jede beliebige Anzahl (N) von Bildern. Aber zuerst können wir unsere Arbeit etwas optimieren, indem wir die Animation aufteilen, um Redundanzen zu vermeiden.
.gallery > img {
z-index: 2;
animation:
slide 6s infinite,
z-order 6s infinite steps(1);
}
.gallery > img:last-child {
animation-name: slide, z-order-last;
}
.gallery > img:nth-child(2) { animation-delay: -2s; }
.gallery > img:nth-child(3) { animation-delay: -4s; }
@keyframes slide {
16.67% { transform: translateX(120%); }
33.33% { transform: translateX(0%); }
}
@keyframes z-order {
16.67%,
33.33% { z-index: 1; }
66.33% { z-index: 2; }
}
@keyframes z-order-last {
16.67%,
33.33% { z-index: 1; }
83.33% { z-index: 2; }
}
Viel weniger Code jetzt! Wir erstellen eine Animation für den Schiebe-Teil und eine weitere für die z-index-Updates. Beachten Sie, dass wir steps(1) für die z-index-Animation verwenden. Das liegt daran, dass ich den z-index-Wert abrupt ändern möchte, im Gegensatz zur Schiebe-Animation, bei der wir eine sanfte Bewegung wünschen.
Nun, da der Code leichter zu lesen und zu pflegen ist, haben wir einen besseren Überblick, um zu ermitteln, wie wir eine beliebige Anzahl von Bildern unterstützen können. Was wir tun müssen, ist, die Animationsverzögerungen und die Prozentsätze der Keyframes zu aktualisieren. Die Verzögerungen sind einfach, da wir die exakt gleiche Schleife verwenden können, die wir im letzten Artikel verwendet haben, um mehrere Bilder im runden Slider zu unterstützen.
@for $i from 2 to ($n + 1) {
.gallery > img:nth-child(#{$i}) {
animation-delay: calc(#{(1 - $i)/$n}*6s);
}
}
Das bedeutet, wir wechseln von Vanilla CSS zu Sass. Als Nächstes müssen wir uns vorstellen, wie sich die Zeitachse mit N Bildern skaliert. Vergessen wir nicht, dass die Animation in drei Phasen abläuft:

Nach "nach rechts schieben" und "nach links schieben" sollte das Bild an Ort und Stelle bleiben, bis die restlichen Bilder die Sequenz durchlaufen haben. Der Teil "nicht bewegen" muss also genauso viel Zeit beanspruchen wie (N - 1) für "nach rechts schieben" und "nach links schieben". Und innerhalb einer Iteration werden N Bilder geschoben. Daher nehmen "nach rechts schieben" und "nach links schieben" jeweils 100%/N der gesamten Animationszeitachse ein. Das Bild schiebt sich für (100%/N)/2 vom Stapel weg und schiebt sich für 100%/N zurück.
Wir können das hier ändern:
@keyframes slide {
16.67% { transform: translateX(120%); }
33.33% { transform: translateX(0%); }
}
...zu diesem:
@keyframes slide {
#{50/$n}% { transform: translateX(120%); }
#{100/$n}% { transform: translateX(0%); }
}
Wenn wir N durch 3 ersetzen, erhalten wir 16,67% und 33,33%, wenn 3 Bilder im Stapel sind. Es ist die gleiche Logik mit der Stapelreihenfolge, bei der wir Folgendes haben:
@keyframes z-order {
#{50/$n}%,
#{100/$n}% { z-index: 1; }
66.33% { z-index: 2; }
}
Wir müssen den Punkt bei 66,33% immer noch aktualisieren. Dort soll das Bild seinen z-index vor Ende der Animation zurücksetzen. Zu diesem Zeitpunkt beginnt das nächste Bild mit dem Schieben. Da der Schiebe-Teil 100%/N dauert, sollte der Reset bei 100% - 100%/N erfolgen.
@keyframes z-order {
#{50/$n}%,
#{100/$n}% { z-index: 1; }
#{100 - 100/$n}% { z-index: 2; }
}
Aber damit unsere z-order-last-Animation funktioniert, sollte sie etwas später in der Sequenz erfolgen. Erinnern Sie sich an die Korrektur, die wir für das letzte Bild vorgenommen haben? Das Zurücksetzen des z-index-Wertes muss erfolgen, wenn das erste Bild aus dem Stapel ist und nicht, wenn es zu schieben beginnt. Wir können denselben Grund hier in unseren Keyframes verwenden.
@keyframes z-order-last {
#{50/$n}%,
#{100/$n}% { z-index: 1; }
#{100 - 50/$n}% { z-index: 2; }
}
Wir sind fertig! Hier ist, was wir erhalten, wenn wir fünf Bilder verwenden:
Wir können einen Hauch von Rotation hinzufügen, um die Dinge etwas ausgefallener zu gestalten.
Alles, was ich getan habe, ist, rotate(var(--r)) zur transform-Eigenschaft hinzuzufügen. Innerhalb der Schleife wird --r mit einem zufälligen Winkel definiert.
@for $i from 1 to ($n + 1) {
.gallery > img:nth-child(#{$i}) {
--r: #{(-20 + random(40))*1deg}; /* a random angle between -20deg and 20deg */
}
}
Die Rotation erzeugt kleine Glitches, da wir manchmal sehen, wie einige Bilder nach hinten im Stapel springen, aber das ist keine große Sache.
Zusammenfassung
All diese z-index-Arbeit war ein großer Balanceakt, oder? Wenn Sie sich vor dieser Übung unsicher waren, wie die Stapelreihenfolge funktioniert, haben Sie jetzt wahrscheinlich eine viel bessere Vorstellung! Wenn Sie einige Erklärungen schwer verständlich fanden, empfehle ich Ihnen dringend, den Artikel noch einmal zu lesen und die Dinge mit Bleistift und Papier zu skizzieren. Versuchen Sie, jeden Schritt der Animation mit einer unterschiedlichen Anzahl von Bildern zu illustrieren, um den Trick besser zu verstehen.
Letztes Mal haben wir ein paar geometrische Tricks verwendet, um einen runden Slider zu erstellen, der nach einer vollen Sequenz zum ersten Bild zurückkehrt. Dieses Mal haben wir einen ähnlichen Trick mit z-index erreicht. In beiden Fällen haben wir keine Bilder dupliziert, um eine kontinuierliche Animation zu simulieren, noch haben wir JavaScript für Berechnungen herangezogen.
Nächstes Mal machen wir 3D-Slider. Bleiben Sie dran!
Fantastische Arbeit!!