CSS animation-delay – @keyframes-Animationen verzögern und synchronisieren

CSS keyframes Animationen animation delay

Ohne weitere Angabe starten CSS-Animationen, wenn die Seite geladen wurde und CSS hat nur eine Möglichkeit, Animationen miteinander zu synchronisieren oder in Abhängigkeit voneinander zu starten: animation-delay bewirkt eine Verzögerung vor dem Start einer Animation in Sekunden.

CSS-Animationen mit animation-delay synchronisieren

Das CSS Animations-Modul lässt sich nicht darüber aus, welche Ereignisse eine Animation starten, sondern legt nur fest, was in einer Animation passiert. Generell starten CSS-Animationen mit dem Laden der Seite und die einzige Möglichkeit, eine Animation abhängig vom Verlauf einer anderen Animation zu starten, ist animation-delay.

animation-delay verzögert den Start einer Animation um eine bestimmte Zeit. Das folgende Beispiel besteht aus drei CSS-Animationen, die durch animation-delay aufeinander abgestimmt sind.

<div id="herz">
	<div id="quader"></div>
	<div id="kreis1"></div>
	<div id="kreis2"></div>
</div>

Die drei Elemente Quadrat, Kreis1 und Kreis2 sind absolut positionierte Elemente unter einem relativ positionierten Element #herz.

#herz {
	position: relative;
}

#kreis1, #kreis2, #quader {
	position: absolute;
	background: red;
}

#kreis1 {
    right: calc(100% - 500px);
    animation: slideLeft 3s forwards;
                         ▲
}                        └── Dauer 


@keyframes slideLeft {
	100% { transform: translateX(calc(-125px - 100%))} 
}

#kreis2 {
    bottom: calc(100% + 200px);
    animation: slideDown 3s 2s forwards;
                         ▲  ▲
}                Dauer ──┘  └── Verzögerung 


@keyframes slideDown {
	100% { transform: translateY(calc(100% + 125px))} 
}

#herz {
    animation: turn 3s 5s forwards;
                    ▲  ▲
}           Dauer ──┘  └── Verzögerung

@keyframes turn {
	100% { transform: rotate(-45deg)}
}

kreis1 und kreis2 brauchen insgesamt 5s (3s für Herz 1 + 2s Verzögerung für kreis2) bis sie ihre Position erreichen, also beginnt die Animation des Elements herz mit einem animation-delay von 5s.

CSS-Animationen haben keine Möglichkeit für einen erneuten Start. Das übernimmt in diesem Beispiel der Button Nochmal mit ein paar Zeilen JavaScript. Um eine Animation erneut zu starten, muss ihre Verbindung zur animation-Regel kurzfristig getrennt werden, anschließend setzt requestAnimationFrame die CSS-animation-Regel mit wenigen Zeilen wieder in Kraft.

JavaScript: CSS-Animationen erneut starten und CSS-Animation starten, wenn sie in den ViewPort kommt

CSS Slideshow mit animation-delay

Alle drei Slides sind absolut positioniert und liegen innerhalb eines relativ positionierten Blocks. Anfänglich liegt jede Slide mit einer animation-Regel slider direkt unterhalb der linken unteren Ecke.

#slides {
	height: 200px; 
	position: relative;
	overflow: hidden;
	margin: auto;
	width: 300px;
}

#slides > * {
	position: absolute; 
	top: 100%; 
	left: 0;
	animation: 12s slider infinite ease-in-out
}

Die Animation slider bewegt eine Slide von der Position unten innerhalb einer halben Sekunde (4% von 12s) an die obere Grenze des umfassenden Blocks und bleibt dort stehen bis die dritte Slide einfliegt. Dann bewegt sich das Slide-Element innerhalb einer halben Sekunde nach oben, bis sie vollständig über dem umfassenden Block liegt. Dort bleibt sie bis die Animation erneut startet.

@keyframes slider {
	0%   { top: 100% }
	4%   { top: 0% }
	20%  { top: 0% }
	20%  { top: -100% }
	100% { top: -100% }
}
Grün
Rot
Blau

Die Slide-Elemente starten versetzt – realisiert durch die animation-delay-Eigenschaft.

#slides > *:nth-child(1) { animation-delay: 0s }
#slides > *:nth-child(2) { animation-delay: 4s }
#slides > *:nth-child(3) { animation-delay: 8s }

Das macht es einfach, die Slideshow mit 4, 5 oder mehr Elementen auszubauen: Mit 5 Slides müssen nur die zwei Regeln für den animation-delay eingesetzt werden. Das Timing ändert sich nicht.

#slides > *:nth-child(2) { animation-delay: 12s }
#slides > *:nth-child(3) { animation-delay: 16s }

Stufenweise Verzögerung

Das kurz verzögerte Einspielen der Informationen ist eine Geste des Erkennens. Die Animation ist mit CSS umgesetzt, der Javascript Intersection Observer erkennt, wenn die Blöcke im Browserfenster erscheinen.

Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Einfaches HTML:

<div class="flip-block-columns">
	<div class="flip-section">
		<figure>
		</figure>
		<h3>Lorem Ipsum</h3>
		<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, … </p>
		<button>Lorem</button> 
	</div>
…
</div>

Das CSS für die verzögerte Animation:

.flip-section {
	width: calc(100% - 2rem);
	max-width: 480px;
	margin: auto;
	background: white;
	padding: 1rem;
	margin-bottom: 3rem;
	overflow: hidden;
}
.flip-section * {
	opacity: 0;
	transform: translateY(-20rem);
	transition-property: opacity, transform;
}
.flip-section.flip-in *:nth-child(1)  {
  transition-duration: 0.4s;
  transition-delay: 0.1s;
}

.flip-section.flip-in *:nth-child(2)  {
  transition-duration: 0.4s;
  transition-delay: 0.2s;
}
.flip-section.flip-in * {
	opacity: 1;
	transform: translateY(0)
}

Das Script für den Intersection Observer:

<script>
const flip = document.querySelectorAll (".flip-section");
const options = {rootMargin: "-50px"};

const flipObserver = new IntersectionObserver (function (entries, observer) {
	entries.forEach(function(entry) {
		if (entry.isIntersecting) {
			entry.target.classList.add("flip-in");
		} else {
			entry.target.classList.remove("flip-in");
		}
	});
}, options);

flip.forEach ( function (flip) {
	flipObserver.observe (flip);
});
</script>