Voriges Jahr habe ich bereits einen Beitrag über die Programmierung eines animierten Sternenhimmels mit Javascript Canvas geschrieben. Die Programmierung war zwar mathematisch interessant, aber recht aufwändig, da Canvas von Haus aus keine dreidimensionalen Koordinaten unterstützt. Die x- und y-Positionen der Sterne habe ich daher mit Hilfe der Formeln der perspektivischen Projektion so berechnet, dass ein räumlicher, dreidimensionaler Eindruck entsteht.
CSS-Animationen sind eine Alternative zur Animation mit Javascript Canvas. Dabei werden sogar 3D-Transformationen unterstützt, wie z.B. "translateX, translateY, translateZ", "rotateX, rotateY, rotateZ" oder "scaleX, scaleY, scaleZ". Das verspricht eine viel einfachere Umsetzung des Sternenhimmels, ohne sich über perspektivische Transformationen Gedanken machen zu müssen.
Da die CSS-Transformationen per Javascript angesteuert werden, entsteht am Ende natürlich ein Javascript-Programm. Damit die Programmierung etwas bequemer wird, habe ich eine Hilfsbibliothek, nämlich Velocity.js, verwendet.
Im Folgenden werde ich erklären, wie ich die Sternenhimmel-Animation mit Velocity JS umgesetzt habe.
Grundgerüst HTML, CSS und Javascript
Das Grundgerüst besteht aus einer HTML-Datei mit folgendem Inhalt:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.js"></script>
<style>
body {
background-color: #1d1d1d;
overflow:hidden;
perspective: 800px;
}
</style>
<script>
jQuery(document).ready(function($) {
var container = document.getElementById("container");
container.style.perspectiveOrigin = window.innerWidth/2 + "px " + window.innerHeight/2 + "px";
...
});
</script>
</head>
<body id="container">
</body>
</html>
Zuerst müssen die Scripte "jQuery" und "Velocity" eingebunden werden (Velocity verwendet die jQuery-Bibliothek, wenn sie mit eingebunden wird).
Im Style-Bereich sind zwei wichtige Angaben enthalten. Mit overflow: hidden sorge ich dafür, dass keine horizontalen und vertikalen Scrollbalken entstehen, wenn die Sterne über den Bildschirmrand hinauswandern. Die Eigenschaft perspective sorgt für die räumliche Tiefe. Laut Mozilla Developer Network ist sie wie folgt definiert:
Die Eigenschaft perspective bestimmt die Entfernung zwischen der Ebene z=0 und dem Nutzer, um einem 3D-positionierten Element eine Perspektive zu geben. Jedes 3D-Element mit z>0 wird größer, jedes 3D-Element mit z<0 wird kleiner.
Im Script-Bereich ist des Weiteren schon die ready-Funktion enthalten: Hier wird der Animationscode eingefügt. Die ready-Funktion wird erst ausgeführt, wenn die HTML-Seite vollständig vom Browser geladen wurde. Die ersten beiden Anweisungen, die ich schon mal in die ready-Funktion eingefügt habe, sorgen dafür, dass die CSS-Eigenschaft perspective-origin, d.h. der Fluchtpunkt der Perspektive, auf die Mitte des Bildschirms gesetzt wird. Damit wird sichergestellt, dass die Sterne von der Mitte des Bildschirms ausgehen.
Schritt 1: Sterne als Div-Elemente generieren
Um 1000 Sterne zu erzeugen, verwende ich eine for-Schleife. Sie wird anstelle von "..." in die ready-Funktion eingefügt. In jeder Iteration der Schleife wird ein Div-Element generiert und dem container-Element als Kind zugewiesen (appendChild).
for (var i = 0; i < 1000; i++) {
var left = Math.random() * window.innerWidth;
var top = Math.random() * window.innerHeight;
var circle = document.createElement("DIV");
circle.className = "circle";
circle.style.position = "absolute"; //Voraussetzung, um left und top zu verwenden
circle.style.opacity = 1;
circle.style.zIndex = -1000000;
circle.style.borderRadius = "2.5px";
circle.style.backgroundColor = "#ffff99";
circle.style.left = left + "px";
circle.style.top = top + "px";
circle.style.width = "5px";
circle.style.height = "5px";
container.appendChild(circle);
circle.init_dist = Math.random() * (2000 + 5000) - 5000;
}
...
Die Sterne sollen gleichmäßig über den Bildschirm verteilt werden. Ich erreiche das, indem ich für jeden Stern eine zufällige Position für left und top wähle, die zwischen 0 und der Breite bzw. Höhe des Bildschirms liegt.
Die Eigenschaft zIndex ist nützlich, wenn die Animation später einmal als Hintergrund einer Webseite verwendet werden soll. Sie sorgt dafür, dass die Sterne keine anderen HTML-Elemente auf der Seite überlagern.
Die letzte Anweisung der for-Schleife weist jedem Stern eine benutzerdefinierte Variable zu, die die anfängliche Entfernung des Sterns vom Bildschirm als Zufallswert zwischen -5000 und 2000 festlegt. Ich nenne sie einfach "init_dist". Ich habe einen Zufallswert gewählt, da ich möchte, dass schon zu Beginn der Animation die Sterne in unterschiedlichen Entfernungen vom Bildschirm vorliegen. Es sollen sowohl nahe als auch ferne Sterne existieren. Wenn man stattdessen den Wert z.B. exakt auf -5000 festsetzt, dann liegen zu Beginn der Animation alle Sterne in großer Entfernung zum Bildschirm vor und bewegen sich dann erst auf den Bildschirm zu.
Schritt 2: Die Sterne animieren
Anstelle von "..." füge ich folgenden Code ein:
$(".circle").each(function(index, element) {
my_animate(element);
});
Für jeden Kreis wird also eine Funktion aufgerufen, die ich "my_animate" nenne. Die Funktion sieht wie folgt aus:
function my_animate(circle) {
$(circle).velocity(
{
translateZ: [
2000,
circle.init_dist
]
},
{
duration: Math.round(Math.random() * (50000 - 10000) + 10000),
complete: function() {
circle.init_dist = Math.random()*(-500+5000)-5000;
my_animate(circle);
}
}
);
}
Die Anweisung translateZ: [2000, circle.init_dist] bewirkt, dass sich der jeweilige Stern ausgehend von seiner ursprünglichen Entfernung (init_dist) auf den Bildschirm zubewegt. Der Zielwert (hier: 2000) muss so groß gewählt werden, dass der Eindruck entsteht, dass der Stern über den Bildschirmrand hinauswandert. Mehr Informationen zu dieser Notation finden Sie in der Velocity-Dokumentation unter dem Stichwort "forcefeeding".
Die Dauer der Animation wird über die Eigenschaft "duration" gesteuert. Mit einem Zufallswert sorge ich dafür, dass jeder Stern eine andere Geschwindigkeit bekommt und somit die Animation flüssiger und natürlicher wirkt.
Normalerweise würden die Sterne einmal in Richtung Bildschirm schweben und dann wäre Schluss. Damit die Animation endlos weiterläuft, müssen sich die Sterne im Anschluss wieder zurück an eine weiter entfernte Position begeben, damit sie von dort aus erneut in Richtung Bildschirm schweben können. Dafür sorgt die Funktion, die bei "complete" angegeben wird. Sie setzt den Stern zurück auf eine zufällige z-Position zwischen -5000 und -500. Wichtig ist, dass die Position weit genug vom Bildschirm entfernt ist, damit das "Zurückspringen" der Sterne dem Betrachter nicht auffällt. Die Animation wird wiederholt, indem erneut die Funktion "my_animate" aufgerufen wird.
Abschließende Bemerkungen
Die CSS-Animation mit Velocity ist sehr einfach und elegant zu programmieren. Ich bin wesentlich schneller zum Ziel gekommen als mit Javascript Canvas.
Bei einer zu großen Anzahl von Sternen bzw. Div-Elementen, die animiert werden sollen, gibt es erwartungsgemäß Performance-Probleme, d.h. die Animation ruckelt und beansprucht den Prozessor zu stark.
Trotzdem halte ich Velocity bzw. CSS-Animation im Allgemeinen für einen guten Weg, um Animationen umzusetzen, die sich relativ einfach als Abfolge von Translationen (translate), Rotationen (rotate) oder Skalierungen (scale) in drei Dimensionen ausdrücken lassen.
Hier nochmal das Ergebnis auf CodePen:
See the Pen Velocity JS Animated Starfield by Anna Prenzel (@blaustern_fotografie) on CodePen.