Komplexe CSS Slider mit Timeline

CSS Slideshow ohne Javascript

Für einen responsiven Slider mit kleinen Extras reicht schon reines CSS, für die mobilen Geräte mit Touchscreen muss dann doch noch ein kleines Script einspringen. Bei komplexen CSS-Keyframe-Animationen wie einer Slideshow oder einem Slider verliert man schnell den Überblick, wie die einzelnen Schritte ineinandergreifen.

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

Keine Timeline – keine Zeitleiste

Was bei CSS fehlt, ist eine Timeline wie einst in Adobe Flash, um den Ablauf der Keyframe-Animation gut zu erkennen.

Eine einfache Slideshow mit fünf Slides ist noch überschaubar, aber man braucht schon sechs Finger an jeder Hand, um die Bewegung, das Auftauchen und Verschwinden der einzelnen Slides durch CSS Keyframes miteinander zu synchronisieren.

simply an A
Gib mir ein A
kaktus
Mein kleiner Kaktus
Rote Uhr
Roter Wecker
Umschlag und Stempel
Brief und Siegel
Schwarze Box
Black Box

Animation für ein Bild: 5 sek

5 Bilder à 5 Sekunden = 25 sek Dauer der Animation

Jedes Bild läuft innerhalb einer Sekunde von unten in das Slideshow-Fenster hinein (Slide In), bleibt für drei Sekunden sichtbar und braucht dann wieder eine Sekunde, um nach oben außerhalb des sichtbaren Ausschnitts zu verschwinden (Slide Out).

Diese 5 Sekunden bestimmen den Takt der Animation: Die Gesamtdauer der Animation (100%) läuft im 4%-Takt (100% / 25).

Zeigt die Folge der Keyframes und ihre Dauer
Folge der Keyframes und ihre Dauer

Die Keyframes liegen zwischen 0 bis 100% – die Animation läuft in einem 4%-Takt.

Slideshow HTML-Markup: figure und figcation

Einfaches HTML: figure und figcaption innerhalb des Rahmens
<div id="slider">
   <div id="onframe">
      <figure class="animate1">
         <img src="a.jpg" width="680" height="360" alt="simply an A">
         <figcaption class="label">Gib mir ein A</figcaption>
      </figure>
      …
      <figure class="animate5">
         <img src="pack.jpg" width="680" height="360" alt="Schwarze Box">
         <figcaption class="label">Black Box</figcaption>
      </figure>
   </div>  <!-- /#onframe -->
   <div class="progress-bar"></div>  <!-- Fortschrittsbalken -->
</div>  <!-- /#Slider -->

Das CSS für den Slider und den Slideshow-Rahmen

#onframe {
   overflow: hidden;
   height: 360px;
}
#slider {
   background: #fff;
   border: 5px solid #eaeaea;
   box-sizing:border-box;
   height: 370px;
   width: 90%;
   margin: 40px auto 0;
   overflow: hidden;
   position: relative;
}

#slider figure {
   width: 680px;  /* Breite des Bildes */
   height: 360px; /* Höhe des Bildes */
   position: absolute;
   margin: 0;
   bottom: 360px; /* Originalposition - Nicht im sichtbaren Ausschnitt */
   left: calc(50% - 360px); /* Setzt Bild in die Mitte des sichtbaren Ausschnitts */
}

Die Bilder sitzen horizontal zentriert innerhalb des Sliders. CSS calc berechnet die Position in der Mitte.

left: calc(50% - 360px); 

Auf kleinen Monitoren passt sich die Slideshow nur in der Horizontalen an und wird schmaler. Das Bild wird dabei rechts und links abgeschnitten, die Mitte bleibt stehen.

Bevor die Slideshow anläuft, sitzen die Bilder unterhalb des sichtbaren Rahmens.

bottom: 360px;

Die Keyframe-Animation

Jedes Bild läuft innerhalb einer Sekunde in den sichtbaren Rahmen hinein, bleibt für drei Sekunden stehen, läuft in der fünften Sekunde wieder aus dem Rahmen. Danach muss das Bild wieder auf seine Startposition unterhalb der Slideshow.

Keyframe-Animation
#slider .animate1 {
   animation: cycle1 25s linear infinite;
}

#slider .animate2 {
   animation: cycle2 25s linear infinite;
}

#slider .animate3 {
   animation: cycle3 25s linear infinite;
}

#slider .animate4 {
   animation: cycle4 25s linear infinite;
}

#slider .animate5 {
   animation: cycle5 25s linear infinite;
}

Die Strecke von der Startposition bis zur Endposition (top: 325px) kann willkürlich gewählt werden, sie hat keinen Bezug zum Ablauf der Animation.

@keyframes cycle1 {
   0%  { top: 0px; } /* Am Anfang der Slideshow ist das erste Bild sichtbar */
   4%  { top: 0px; } /* Bild steht noch auf der Startposition */
   16% { top: 0px; opacity:1; z-index:0; } /* Von 4% bis 16 % ist das Bild für 3 Sekunden sichtbar */
   20% { top: -325px; opacity: 0; z-index: 0; } /* Von 16% bis 20% = 1 Sekunde für Slide Out */
   21% { top: 325px; opacity: 0; z-index: -1; } /* Zurück auf die Originalposition */
   92% { top: 325px; opacity: 0; z-index: 0; }
   96% { top: 325px; opacity: 0; } /* Von 96% bis 100% = 1 Sekunde für Slide In */
   100%{ top: 0px; opacity: 1; }
}

@keyframes cycle2 {
   0%  { top: 325px; opacity: 0; } /* Originalposition außer Sicht */
   16% { top: 325px; opacity: 0; }/* Beginnt mit der Bewegung */
   20% { top: 0px; opacity: 1; }
   24% { top: 0px; opacity: 1; }  /* Von 20% bis 24% = 1 Sekunde für Slide In */
   36% { top: 0px; opacity: 1; z-index: 0; }   /* Von 24% bis 36 % = Bild für 3 Sekunden sichtbar */
   40% { top: -325px; opacity: 0; z-index: 0; } /* Von 36% bis 40% = 1 Sekunde für Slide Out */
   41% { top: 325px; opacity: 0; z-index: -1; }   /* Zurück auf die Originalposition */
   100%{ top: 325px; opacity: 0; z-index: -1; }
}

@keyframes cycle3 {
   0%  { top: 325px; opacity: 0; }
   36% { top: 325px; opacity: 0; }
   40% { top: 0px; opacity: 1; }
   44% { top: 0px; opacity: 1; }
   56% { top: 0px; opacity: 1; }
   60% { top: -325px; opacity: 0; z-index: 0; }
   61% { top: 325px; opacity: 0; z-index: -1; }
   100%{ top: 325px; opacity: 0; z-index: -1; }
}

@keyframes cycle4 {
   0%  { top: 325px; opacity: 0; }
   56% { top: 325px; opacity: 0; }
   60% { top: 0px; opacity: 1; }
   64% { top: 0px; opacity: 1; }
   76% { top: 0px; opacity: 1; z-index: 0; }
   80% { top: -325px; opacity: 0; z-index: 0; }
   81% { top: 325px; opacity: 0; z-index: -1; }
   100%{ top: 325px; opacity: 0; z-index: -1; }
}

@keyframes cycle5 {
   0%  { top: 325px; opacity: 0; }
   76% { top: 325px; opacity: 0; }
   80% { top: 0px; opacity: 1; }
   84% { top: 0px; opacity: 1; }
   96% { top: 0px; opacity: 1; z-index: 0; }
   100%{ top: -325px; opacity: 0; z-index: 0; }
}

Der Fortschrittsbalken läuft innerhalb der 25 Sekunden 5 mal von links nach rechts – für jedes Bild der Slideshow einmal. In der Sekunde des Wechsels zwischen den Slides ist der Fortschrittsbalken nicht sichtbar.

Progress Bar – Fortschrittsbalken

Der Fortschrittsbalken läuft fünf mal während der 25 Sekunden der Animation – für jedes Bild einmal.

.progress-bar {
   position: relative;
   top: -5px;
   width: 680px;
   height: 5px;
   background: #00f;
   animation: fullexpand 25s ease-out infinite;
}

@keyframes fullexpand {
   /* Anfang: Fortschrittsbalken steht ist nicht sichtbar */
   0%, 20%, 40%, 60%, 80%, 100% { width: 0%; opacity: 0; }

   /* Fortschrittsbalken bereit zum Start */
   4%, 24%, 44%, 64%, 84% { width: 0%; opacity: 0.3; }

   /* Fortschrittsbalken läuft 3 Sekunden an */
   16%, 36%, 56%, 76%, 96% { width: 100%; opacity: 0.7; }

   /* Fortschrittsbalken hat Ende erreicht */
   17%, 37%, 57%, 77%, 97% { width: 100%; opacity: 0.3; }

   /* Fortschrittsbalken wird unsichtbar und beginnt einen neuen Durchlauf */
   18%, 38%, 58%, 78%, 98% { width: 100%; opacity: 0; }
}

Pausieren bei hover

Damit die Slideshow beim Hovern mit der Maus pausiert, sind eigentlich nur wenige Zeilen CSS erforderlich.

/**
#slider li:hover .label {
   left: 0px;
}

#slider:hover li,
#slider:hover .progress-bar {
   animation-play-state: paused;
}
**/

Aber auf den mobilen Geräten gibt es kein Hovern, also muss an dieser Stelle Javascript einspringen. Auch der Text in figcaption class="label" wird während der Pause eingeschoben und sichtbar.

#slider figure.toggle .label {
   left: 0px;
}

#slider figure.toggle,
#slider.toggle .progress-bar {
   animation-play-state: paused;
}
<script>
var slides = document.querySelectorAll("#onframe figure");
for (var i=0; i<slides.length; i++) {
	slides[i].ontouchstart =
	slides[i].onmouseover = function () {
		this.classList.add("toggle");
		document.querySelector("#slider").classList.add("toggle");
		for (var j=0; j<slides.length; j++) {
			slides[j].style.animationPlayState = "paused";
		}
	}
	slides[i].ontouchend =
	slides[i].onmouseout = function () {
		this.classList.remove("toggle");
		document.querySelector("#slider").classList.remove("toggle");
		for (var j=0; j<slides.length; j++) {
			slides[j].style.animationPlayState = "running";
		}
	}
}
</script>
Download ZIP

CSS-Animationen vs Animationen mit Javascript

CSS Keyframe-Animationen haben ein großes Potential, aber sie sind nicht so universell wie Animationen mit Javascript. Um mehr oder weniger Bilder in die Slideshow zu setzen, müssen alle Keyframes geändert werden.

Für komplexe Animationen ist Javascript animate() die bessere Alternative.