Gibt es Zufallszahlen in CSS?

Avatar of Alvaro Montoro
Alvaro Montoro am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

CSS ermöglicht es Ihnen, dynamische Layouts und Oberflächen im Web zu erstellen, aber als Sprache ist es statisch: Sobald ein Wert festgelegt ist, kann er nicht mehr geändert werden. Die Idee von Zufälligkeit ist vom Tisch. Das Generieren von Zufallszahlen zur Laufzeit ist das Territorium von JavaScript, nicht wirklich von CSS. Oder doch? Wenn wir ein wenig Benutzerinteraktion einbeziehen, können wir tatsächlich einen gewissen Grad an Zufälligkeit in CSS erzeugen. Schauen wir es uns an!

Zufälligkeit aus anderen Sprachen

Es gibt Möglichkeiten, „dynamische Zufälligkeit“ mithilfe von CSS-Variablen zu erzielen, wie Robin Rendle in einem Artikel auf CSS-Tricks erklärt. Aber diese Lösungen sind nicht zu 100 % CSS, da sie JavaScript erfordern, um die CSS-Variable mit dem neuen Zufallswert zu aktualisieren.

Wir können Präprozessoren wie Sass oder Less verwenden, um Zufallswerte zu generieren, aber sobald der CSS-Code kompiliert und exportiert ist, sind die Werte fix und die Zufälligkeit geht verloren. Wie Jake Albaugh erklärt

Warum interessieren mich Zufallswerte in CSS?

Früher habe ich einfache CSS-only Apps entwickelt, wie z. B. ein Quizspiel, ein Simon-Spiel und einen Zaubertrick. Aber ich wollte etwas Komplizierteres machen. Ich werde eine Diskussion über die Gültigkeit, Nützlichkeit oder Praktikabilität der Erstellung solcher reinen CSS-Snippets für später aufheben.

Basierend auf der Prämisse, dass einige Brettspiele als endliche Automaten (FSM) dargestellt werden könnten, könnten sie mithilfe von HTML und CSS dargestellt werden. Also begann ich, ein Spiel „Leiterspiele“ (aka Chutes and Ladders) zu entwickeln. Es ist ein einfaches Spiel. Das Ziel ist es, einen Spielfiguren vom Anfang bis zum Ende des Spielfelds zu bewegen, wobei die Schlangen vermieden und die Leitern genutzt werden.

Das Projekt schien machbar, aber es gab etwas, das ich vermisste: **Würfel werfen**!

Das Würfeln (zusammen mit dem Münzwurf) ist universell als Zufälligkeit anerkannt. Man wirft die Würfel oder wirft die Münze und erhält jedes Mal einen unbekannten Wert.

Simulation eines zufälligen Würfelwurfs

Ich wollte Ebenen mit Beschriftungen übereinander legen und CSS-Animationen verwenden, um zu „rotieren“ und auszutauschen, welche Ebene oben lag. Etwas in dieser Art

Simulation, wie die Ebenen in einem Browser animiert werden

Der Code zur Nachahmung dieser Zufälligkeit ist nicht übermäßig kompliziert und kann mit einer Animation und unterschiedlichen Animationsverzögerungen erreicht werden

/* The highest z-index is the numbers of sides in the dice */ 
@keyframes changeOrder {
  from { z-index: 6; } 
  to { z-index: 1; } 
} 

/* All the labels overlap by using absolute positioning */ 
label { 
  animation: changeOrder 3s infinite linear;
  background: #ddd;
  cursor: pointer;
  display: block;
  left: 1rem;
  padding: 1rem;
  position: absolute;
  top: 1rem; 
  user-select: none;
} 
    
/* Negative delay so all parts of the animation are in motion */ 
label:nth-of-type(1) { animation-delay: -0.0s; } 
label:nth-of-type(2) { animation-delay: -0.5s; } 
label:nth-of-type(3) { animation-delay: -1.0s; } 
label:nth-of-type(4) { animation-delay: -1.5s; } 
label:nth-of-type(5) { animation-delay: -2.0s; } 
label:nth-of-type(6) { animation-delay: -2.5s; }

Die Animation wurde verlangsamt, um die Interaktion zu erleichtern (aber immer noch schnell genug, um das unten erklärte Hindernis zu sehen). Die Pseudozufälligkeit ist ebenfalls klarer.

Siehe den Pen
Demo von pseudo-zufällig generierter Zahl mit CSS
von Alvaro Montoro (@alvaromontoro)
auf CodePen.

Aber dann stieß ich auf ein Hindernis: Ich erhielt zwar Zufallszahlen, aber manchmal, selbst wenn ich auf meine „Würfel“ klickte, gab es *keinen* Wert zurück.

Ich habe versucht, die Zeiten in der Animation zu erhöhen, und das schien ein wenig zu helfen, aber ich hatte immer noch einige unerwartete Werte.

Da bat ich, wie die meisten Entwickler, wenn sie auf ein Hindernis stoßen, das sie nicht allein durch Online-Suche lösen können, andere Entwickler um Hilfe in Form einer StackOverflow-Frage.

Zu meinem Glück kam der stets findige Temani Afif mit einer Erklärung und einer Lösung.

Um es ein wenig zu vereinfachen: Das Problem war, dass der Browser das Klick-/Drückereignis nur dann auslöst, wenn das Element, das beim Drücken der Maustaste aktiv ist, dasselbe Element ist, das beim Loslassen der Maustaste aktiv ist.

Aufgrund der Rotationsanimation war das oberste Label beim Drücken der Maustaste nicht das oberste Label beim Loslassen der Maustaste, es sei denn, ich tat es schnell oder langsam genug, damit die Animation herumzirkulierte. Deshalb verbargen die erhöhten Animationszeiten diese Probleme.

Die Lösung bestand darin, eine Position „static“ anzuwenden, um den Stapelkontext zu durchbrechen, und ein Pseudoelement wie ::before oder ::after mit einem höheren z-index zu verwenden, um seinen Platz einzunehmen. So wäre das aktive Label beim Loslassen der Maustaste immer oben.

/* The active tag will be static and moved out of the window */ 
label:active {
  margin-left: 200%;
  position: static;
}

/* A pseudo-element of the label occupies all the space with a higher z-index */
label:active::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  z-index: 10;
}

Hier ist der Code mit der Lösung bei schnellerer Animationszeit

Siehe den Pen
Demo von pseudo-zufällig generierter Zahl mit CSS
von Alvaro Montoro (@alvaromontoro)
auf CodePen.

Nachdem diese Änderung vorgenommen worden war, blieb nur noch die Erstellung einer kleinen Benutzeroberfläche, um einen gefälschten Würfel zum Anklicken zu zeichnen, und das CSS-Spiel „Leiterspiele“ war fertig.

Diese Technik hat einige offensichtliche Nachteile

  • Sie erfordert Benutzereingaben: ein Label muss angeklickt werden, um die „Zufallszahlengenerierung“ auszulösen.
  • Sie skaliert nicht gut: Sie funktioniert gut mit kleinen Wertemengen, ist aber bei großen Bereichen mühsam.
  • Sie ist nicht wirklich zufällig, sondern pseudo-zufällig: Ein Computer könnte leicht erkennen, welcher Wert zu jedem Zeitpunkt generiert werden würde.

Andererseits ist sie zu 100 % CSS (keine Präprozessoren oder andere externe Hilfsmittel erforderlich) und kann für einen menschlichen Benutzer zu 100 % zufällig aussehen.

Und wo wir gerade von Händen sprechen… Diese Methode kann nicht nur für Zufallszahlen, sondern für alles Zufällige verwendet werden. In diesem Fall haben wir sie verwendet, um die Computerwahl bei einem Stein-Schere-Papier-Spiel „zufällig“ auszuwählen.

Siehe den Pen
CSS Stein-Schere-Papier
von Alvaro Montoro (@alvaromontoro)
auf CodePen.