Animierte Slideshows ohne JavaScript

Ich finde CSS-Transitions und Animations ja ziemlich lustig 🙂 und wollte schon immer so eine Web-Slideshow basteln ohne irgend ein JavaScript basteln. Hier mal ein Beispiel:

  • Slide01
  • Slide02
  • Slide03
  • Slide04
  • Slide05

Das war einfacher als gedacht. In diesem Artikel hab ich das nun etwas aufbereitet - als Inspiration, zum Nachbauen oder Experimentieren (und nicht zuletzt als Dokumentation für mich).

Bilder aussuchen

Man nehme ein paar Bilder, z.B. diese hier:

Oder andere:

Hier ein Auszug aus meinem Lesezeichen-Ordner "Free Stock Images": Die Bilder sind meist kostenfrei - auch kommerziell - nutzbar. Aber bitte die Lizenzbedingungen der Portale prüfen und ggf. Lizenzbedingungen (evtl. sind Angabe des Portals und/oder der Urheber o.ä.) erfüllen. Die Liste ist alphabetisch und nach keinen anderen Kriterien sortiert:

Viel Spaß beim Stöbern 🙂

Das HTML-Gerüst

Man benötigt nur wenig HTML. Ich lege einen div-Container ("Wrapper") an, und darin eine ul-Liste. Die Anzahl der Listenpunkte (li) entspricht der Anzahl der Slides:

HTML

<div id="#slideshow_wrapper">
  <ul class="slideshow">
    <li>Slide01</li>
    <li>Slide02</li>
    <li>Slide03</li>
      ...
    <li>SlideXX</li>     /* XX = Anzahl der Slides */
  </ul>
</div>

Alles weitere ist reines CSS 🙂

Das Basis-CSS

Der Wrapper wird - je nach Seitengerüst - positioniert und mit Höhe und Breite, optional mit Hintergrundfarbe (dazu unten mehr: Abschnitt "Die Überblendung", Stichwort "Timing überprüfen") ausgestattet:

CSS

/* Wrapper für "in-page-slideshows" */
#slideshow_wrapper {
  background:red;
  position:relative;
  width:100%;
  height:500px;       /* Beispielwert! */
  max-height:100vh;   /* für Smartphones */
}

/* Wrapper für "background-slideshows" */
#slideshow_wrapper {
  background:red;
  position:fixed;
  width:100%;
  height:100%;
}

Die erste Variante ist ein div, welches sich relative positioniert in die anderen DOM-Objekte einfügt, die Höhe (height) ist dabei variabel festsetzbar, die max. Höhe (max-height) verhindert zu große Darstellungen auf kleinen Bildschirmen.

Die zweite Variante ist eine für eine bildschirm- (bzw. browserfenster-) füllende Variante (position:fixed und height:100%;).

Die "unodered list" ul soll ohne Aufzählingszeichen (list-style:none;)  und absolut positioniert den ganzen Wrapper einnehmen (via position, width, height, top, right, bottom, left, margin, padding). Das overflow:hidden; ist sinnvoll, um bei skalierten und/oder gedrehten Bildern deren "überstehende" Ränder und Ecken abzuschneiden.

CSS

.slideshow {
  list-style:none;
  position:absolute;
  width: 100%;
  height: 100%;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: 0;
  padding: 0;
  overflow:hidden;
}

Die Slides werden durch die "list items" li repräsentiert. Diese werden ebenfalls absolut positioniert und auf maximale Größe gebracht (position, height, width, top, right, bottom, left). Die Bilder werden via Hintergrundbild in die Slides eingebastelt:

CSS

.slideshow li {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: transparent url(01.jpg) no-repeat center center / cover;
}

Die "shorthand property" für den Hintergrund kann ich hier gerne mal aufdröseln, um sie evtl. besser verständlich zu machen:

CSS

.slideshow li {
  ...
  background-color: transparent;
  background-image: url(01.jpg);
  background-repeat: no-repeat;
  background-position: center center;     /* horizontal, vertikal */
  background-size: cover;
}

Das Bild wird also horizontal und vertikal zentriert (background-position: center center;) und mit der Größenangabe background-size:cover; versehen, so dass es den zu Verfügung stehenden Platz immer ausfüllt.

Das heißt aber auch: Ist das Bild nicht hoch und/oder breit genug, um den Container auszufüllen, wird es so groß skaliert, bis es passt. Wenn das Bild ein anderes Seitenverhältnis hat als der Container, steht es beim Hochskalieren dann aber aus diesem heraus. Hier ist hier das oben erwähnte overflow:hidden; der ul.slideshow dafür verantwortlich, dass die überstehenden Bildbereiche ausgeblendet werden.

Um nicht zu viel CSS zu schreiben, gruppiere ich die für alle Slides geltenden Regeln in einer allgemeinen Definition für alle .slideshow li's, die speziellen weise ich dagegen den :nth-child-Pseudoklassen zu. Optional kann man auch den li's im HTML z.B. durchnummerierte Klassen mitgeben, z.B. .slide01, .slide02, usw., um diese ohne Pseudoklassen anzusprechen.

CSS

.slideshow li {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  color: transparent;
  background-size: cover;
  background-position: center center;
}

.slideshow li:nth-child(1)  {background-image: url(01.jpg);}
.slideshow li:nth-child(2)  {background-image: url(05.jpg);}
.slideshow li:nth-child(3)  {background-image: url(06.jpg);}
.slideshow li:nth-child(4)  {background-image: url(08.jpg);}
.slideshow li:nth-child(5)  {background-image: url(09.jpg);}
.slideshow li:nth-child(6)  {background-image: url(10.jpg);}
.slideshow li:nth-child(7)  {background-image: url(13.jpg);}
.slideshow li:nth-child(8)  {background-image: url(15.jpg);}
.slideshow li:nth-child(9)  {background-image: url(17.jpg);}
.slideshow li:nth-child(10) {background-image: url(20.jpg);}

So weit, so gut. Die allererste Slide wird angezeigt, ist "wrapper-füllend" skaliert und positioniert:

  • Slide01
  • Slide02
  • Slide03
  • Slide04
  • Slide05

Was fehlt? Ach ja, klar:

Die Animation

Das "Kind braucht einen Namen", z.B. animation-name: slideShow;, soll nicht ein- und/oder ausschwenken sondern linear ablaufen (animation-timing-function: linear;), unendlich oft (animation-iteration-count: infinite;) wiederholt werden und ohne verzögerung starten (animation-delay: 0s;). Bei 10 Bildern und einer Anzeigedauer von - sagen wir einmal - 5 Sek. je Bild dauert der Spaß insgesamt die animation-duration: 50s; (nämlich: 10 Bilder * 5s = 50s).

Daher füge ich der .slideshow li folgendes hinzu:

CSS

.slideshow1 li {
  [... bisheriges CSS ...]
  animation-name: slideShow
  animation-duration: 50s;
  animation-timing-function: linear;
  animation-interation-count: infinite;
  animation-delay: 0s;

  /* shorthand */
  animation: slideShow 50s linear infinite 0s;

  opacity: 0;
}

Da der Slide-Übergang mittels Deckkraft animiert wird, setze ich diese hier schon einmal auf 0 (opacity:0;).

Die Überblendung

Jetzt benötigt unsere Animation slideShow noch Angaben dazu, wer wann wie animiert werden soll. Das passiert mittels sog. Keyframes. Dafür muss man mehrere Schlüsselbilder (mind. 2) definieren, welche mithilfe einer Prozentangabe zeitlich in der animation-duration platziert werden. Beispiel: Drei Keyframes (Anfang, Mitte und Ende) innerhalb des CSS mit 0%, 50% und 100% würden bei einer Animationsdauer von 50 Sekunden in etwa (s.u.) den den Zeitpunkten "Sekunden 0", "Sekunde 25" und "Sekunde 50" entsprechen und folgendermaßen notiert:

CSS

@keyframes slideShow { 
  0% {
    [ Zustand ]
  }
  50% {
    [ Zustand ]
  }
  100% {
    [ Zustand ]
  }
}

Die Keyframes enthalten also eine Zustandsbeschreibung des DOM-Objektes, welchem die Animation zugeordnet ist. Die Animation selber (also die "Zwischenzustände") berechnet der Browser netterweise eigenständig und zeigt sie als Bewegung. 🙂

Bei den Prozentangaben in Relation zur Zeit kommt es mir manchmal so vor, als ob 100% von 50 Sekunden nicht bei der Zeitmarke 0:50:00 sondern bei der Zeitmarke 0:50:99999... liegt. Im Beispiel entspräche der Keyframe 25% also eher der Zeitmarke 0:25:50. Die Milisekunden sind nicht immer vernachlässigbar...
Weiß da jemand mehr?

Um die Keyframes festzulegen, muss man sich nun mal genau überlegen, was denn da eigentlich passieren soll: Die Slides sollen zu Beginn ca. 1 Sek. eingeblendet, 4 Sekunden angezeigt und dann wieder 1 Sek. ausgeblendet werden. Während Slide 1 ausblendet, soll Slide 2 bereits eingeblendet werden. Für die Slides 2 bis 10 nutzen wir später dafür eine Verzögerung via animation-delay. Nach der letzten Slide soll wieder mit Slide 1 begonnen werden (daher animation-iteration-count:infinite;).

Wenn ich das mal schematisch darstellen soll, würde in etwa so etwas dabei heraus kommen:

Bilder sollen eine Sekunde eingeblendet, vier Sekunden angezeigt und eine Sekunde ausgeblendet werden...

Für das erste Bild heißt das:

Keyframe bei Sekunde 0 mit Deckkraft 0%,
Keyframe bei Sekunde 1 mit Deckkraft 100%,
Keyframe bei Sekunde 5 mit Deckkraft 100%,
Keyframe bei Sekunde 6 mit Deckkraft 0%.

Um nun die Keyframes über die animation-duration (= 50 Sek.) in Prozent angeben zu können, muss ich also nur ein wenig rechnen:

Sek. 0 von 50s = 0%,
Sek. 1 von 50s ~ 2%,
Sek. 5 von 50s ~ 10%,
Sek. 6 von 50s ~ 12%.

Die Keyframes incl. der Deckkraft notiere ich also folgendermaßen:

CSS

@keyframes slideShow {
  0% {
    opacity:0;
  }
  2% {
    opacity:1;
  }
  10% {
    opacity:1;
  }
  12% {
    opacity:0;
  }
}

... und die Verzögerung für die folgenden Bilder schreibe ich wieder in die :nth-child()-Pseudoklassen der "list items":

CSS

.slideshow li:nth-child(1)  { ... animation-delay:  0s;}
.slideshow li:nth-child(2)  { ... animation-delay:  5s;}
.slideshow li:nth-child(3)  { ... animation-delay: 10s;}
.slideshow li:nth-child(4)  { ... animation-delay: 15s;}
.slideshow li:nth-child(5)  { ... animation-delay: 20s;}
.slideshow li:nth-child(6)  { ... animation-delay: 25s;}
.slideshow li:nth-child(7)  { ... animation-delay: 30s;}
.slideshow li:nth-child(8)  { ... animation-delay: 35s;}
.slideshow li:nth-child(9)  { ... animation-delay: 40s;}
.slideshow li:nth-child(10) { ... animation-delay: 45s;}

So wird die jeweils folgende Slide mit der gleichen Animation (slideShow) animiert, diese wird allerdings jeweils 5 Sekunden später gestartet. Wenn ich mich bei den Prozent-Angaben zu den Keyframes für das Ausblenden nicht völlig verrechnet habe, sollte das Einblenden der jeweils folgenden Slide mit dem Ausblenden der jeweils vorherigen zusammenfallen. Die letzte Slide wird dann also ab Sek. 45 animiert und ab Sek. 50 ausgeblendet, so dass es beim erneuten Einblenden der Slide 1 keinen harten Übergang gibt.

So siehts nun aus:

  • Slide01
  • Slide02
  • Slide03
  • Slide04
  • Slide05
  • Slide06
  • Slide07
  • Slide08
  • Slide09
  • Slide10

Ob das Timing der Überblendungen passt, kann man - wie oben angedeutet - mit einer auffälligen Hintergrundfarbe des Containers (#slideshow_wrapper) überprüfen. Wenn nämlich die eine Slide bereits aus-, die folgende aber noch nicht eingeblendet ist, sieht man den (hier: roten) Hintergrund durchscheinen. Dann kann man

  • die letzten Keyframes evtl. zeitlich etwas nach hinten korrigieren oder
  • eine neutrale Hintergrundfarbe (z.B. schwarz, grau oder weiß) nutzen. 🙂

Die Bewegung

Neben der Überblendung gibt eine kleine (wirklich: Hier ist weniger mehr!) Bewegung der Slideshow den letzten Schliff. Dafür nutze ich eine Skalierung (transform: scale()) und eine Drehung (transform:rotate()).

Diese lege ich an den bereits bestehenden Keyframes ganz am Anfang bzw. ganz am Ende der Animation fest; so verläuft die Bewegung auch über die Ein- und Ausblendung hinaus konstant. Bei der Kombination mehrerer CSS-Transformationen schreibt man alle in eine Deklaration. Hier wird also eine Skaierung (von scale(1.1); zu scale(1.3);) und eine Drehung um 3 Grad (von rotate(-1.5deg); zu rotate:(1.5deg);) kombiniert.

CSS

@keyframes slideShow1 { 
  0% {
    opacity: 0;
    transform: scale(1.1) rotate(-1.5deg);
  }
  2% {
    opacity: 1;
  }
  10% {
    opacity: 1;
  }
  12% {
    opacity: 0;
    transform: scale(1.3) rotate(1.5deg);
  }
}

Hier. Version 1 ist fertig:

  • Slide01
  • Slide02
  • Slide03
  • Slide04
  • Slide05
  • Slide06
  • Slide07
  • Slide08
  • Slide09
  • Slide10

Variationen

Die Version 1 und ein paar weitere Spielereien gibt es hier zum Download:

Beispiel 1: Der "Standard" von oben. 10 Bilder, je 5 Sek., mit Blende, Zoom und Drehung - Download hier

Beispiel 2: Slideshow als Hintergrund im "Vollbild" - Download hier

Beispiel 3: Kombination von zwei verschiedenen Animationen für gerade und ungerade Slides - Download hier

Beispiel 4: "Laufband" statt Überblendung - Download hier

Beispiel 5: Zusätzliche Animation von Texten - Download hier

Beispiel 6: Alles bewegt sich! - Download hier

Viel Spaß beim Basteln 🙂


Nachtrag: In einem anderen Projekt bin ich beinahe daran verzweifelt, warum der Internet Explorer 11 die Animation ums sprichwörtliche Verrecken nicht abspielen wollte. An dieser Stelle habe ich einen wertvollen Hinweis zur Platzierung der @keyframe-Section innerhalb des Stylesheets gefunden: Je weiter vorne, desto eher IE-Unterstützung. 🤦

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Folgende HTML-Tags sind möglich:
<a href=""> <code> <em> <strong> <pre>

Zustimmung zur Datenverarbeitung