Y a-t-il des nombres aléatoires dans CSS?



CSS vous permet de créer des mises en page et des interfaces dynamiques sur Internet, mais en tant que langage de balisage, il est statique - vous ne pouvez pas le modifier après l'avoir défini. L'idée du hasard n'est pas discutée. La génération de nombres aléatoires à l'exécution est un territoire de JavaScript, pas CSS.

Ou pas? Si nous permettons une petite interaction avec l'utilisateur, nous pouvons en fait générer un certain degré d'aléatoire en CSS. Jetons un coup d'oeil!

Aléatoire dans d'autres langues


Il existe des moyens d'obtenir une «randomisation dynamique» à l'aide de variables CSS, comme l'explique Robin Rendle dans un article sur les astuces CSS . Mais ces solutions ne sont pas 100% CSS, car elles nécessitent JavaScript pour mettre à jour la variable CSS avec une nouvelle valeur aléatoire.

Nous pouvons utiliser des préprocesseurs comme Sass ou Less pour générer des valeurs aléatoires, mais dès que le code CSS est compilé et exporté, les valeurs sont fixes et l'aléatoire est perdu.

Pourquoi suis-je préoccupé par les valeurs aléatoires en CSS?


Dans le passé, j'ai développé de simples applications CSS uniquement telles que Quiz , le jeu CS Simon et Magic Trick , mais je voulais faire quelque chose de plus complexe.

* Je ne laisserai une discussion sur la validité, l'utilité ou la praticité de la création de ces extraits en CSS que plus tard.

Basé sur le fait que certains jeux de société peuvent être représentés comme des machines à états (FSM), ils peuvent être représentés à l'aide de HTML et CSS.

J'ai donc commencé à développer le jeu "Leela" (c'est aussi "Snakes and Ladders").

Ceci est un jeu simple. Bref, le but du jeu est de faire avancer la puce du début à la fin du plateau, en évitant les serpents et en essayant de monter les escaliers.

Le projet semblait réalisable, mais j'ai raté quelque chose, oui bien sûr - dés!
Lancer des dés (dans d'autres jeux, lancer des pièces) sont des méthodes généralement acceptées pour obtenir des valeurs aléatoires. Vous lancez des dés ou lancez une pièce, et chaque fois vous obtenez une valeur inconnue. Tout semble simple.

Simulation d'un jet d'os aléatoire


J'allais superposer les calques avec des étiquettes et utiliser l'animation CSS pour «faire pivoter» et partager le calque qui était au-dessus. Quelque chose comme ça:



Le code pour simuler cette randomisation n'est pas trop compliqué et peut être réalisé en utilisant l'animation et divers retards d'animation:

/*   z-index -     */ @keyframes changeOrder { from { z-index: 6; } to { z-index: 1; } } /*        */ label { animation: changeOrder 3s infinite linear; background: #ddd; cursor: pointer; display: block; left: 1rem; padding: 1rem; position: absolute; top: 1rem; user-select: none; } /*  ,        */ 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; } 

L'animation a été ralentie pour simplifier l'interaction (mais toujours assez rapide pour voir l'obstacle expliqué ci-dessous). Le pseudo-aléatoire est également plus clair.


 <label for="d1">Click here to roll the dice</label> <label for="d2">Click here to roll the dice</label> <label for="d3">Click here to roll the dice</label> <label for="d4">Click here to roll the dice</label> <label for="d5">Click here to roll the dice</label> <label for="d6">Click here to roll the dice</label> <div> <input type="radio" id="d1" name="dice"> <input type="radio" id="d2" name="dice"> <input type="radio" id="d3" name="dice"> <input type="radio" id="d4" name="dice"> <input type="radio" id="d5" name="dice"> <input type="radio" id="d6" name="dice"> <p>You got a: <span id="random-value"></span></p> </div> 

 @keyframes changeOrder { from { z-index: 6;} to { z-index: 1; } } label { animation: changeOrder 3s infinite linear; background: #ddd; cursor: pointer; display: block; left: 1rem; padding: 1rem; position: absolute; top: 1rem; user-select: none; } label:nth-of-type(1) { animation-delay: 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; } div { left: 1rem; position: absolute; top: 5rem; width: 100%; } #d1:checked ~ p span::before { content: "1"; } #d2:checked ~ p span::before { content: "2"; } #d3:checked ~ p span::before { content: "3"; } #d4:checked ~ p span::before { content: "4"; } #d5:checked ~ p span::before { content: "5"; } #d6:checked ~ p span::before { content: "6"; } 

Mais alors je suis tombé sur une certaine limitation. J'ai obtenu des nombres aléatoires, mais parfois, même lorsque je cliquais sur «dés», le champ ne renvoyait aucune valeur.

J'ai essayé d'augmenter le temps d'animation, et cela a semblé aider un peu, mais j'avais encore des valeurs inattendues.

C'est alors que j'ai fait ce que la plupart des développeurs font lorsqu'ils trouvent des obstacles qu'ils ne peuvent pas résoudre - j'ai fait appel à StackOverflow pour obtenir de l'aide. Je le recommande.

Heureusement pour moi, il y aura toujours des gens prêts à aider, dans mon cas c'était Temani Afif avec son explication .

Si vous essayez de simplifier, le problème était que le navigateur déclenche l'événement click uniquement lorsque l'élément est à l'état actif (lorsque la souris est cliquée), en d'autres termes, c'est le même élément qui est actif lorsque le bouton de la souris est enfoncé.

En raison de la modification de l'animation, le calque supérieur (étiquette), lorsque vous avez cliqué sur la souris, n'était en fait pas ce calque supérieur (étiquette) lorsque la souris a été enfoncée, sauf si je l'ai fait assez rapidement, enfin ou lentement, de sorte que l'animation est déjà réussi à parcourir toutes les valeurs du cycle. C'est pourquoi l'augmentation du temps d'animation a masqué ce problème.

La solution consistait à appliquer la position «statique» pour rompre le contexte de la pile et à utiliser un pseudo-élément tel que :: avant ou :: après avec un z-index plus élevé pour prendre sa place. Ainsi, l'étiquette active sera toujours au-dessus lorsque vous passez la souris.

 /*   label      */ label:active { margin-left: 200%; position: static; } /*         z-index */ label:active::before { content: ""; position: absolute; top: 0; right: 0; left: 0; bottom: 0; z-index: 10; } 

Voici le code avec une solution avec un temps d'animation plus rapide:


Après avoir fait ce changement, il reste à créer une petite interface pour dessiner des pseudo cubes qui peuvent être cliqués avec la souris, et le jeu CSS Snakes and Ladders est terminé.

Cette technique présente certains inconvénients évidents.


  1. Une interaction avec l'utilisateur est nécessaire - il est nécessaire de cliquer sur une étiquette pour provoquer la "génération de nombres aléatoires".
  2. La méthode n'est pas bien mise à l'échelle - elle fonctionne très bien avec de petits ensembles de valeurs, mais c'est pénible pour les grandes plages.
  3. Ce n'est pas un véritable aléatoire, mais toujours un pseudo-aléatoire, et l'ordinateur peut facilement déterminer quelle valeur sera générée à chaque instant.

Mais d'un autre côté, c'est 100% CSS (il n'y a pas besoin de préprocesseurs ou d'autres aides externes), et pour une personne, cela peut sembler 100% aléatoire.

Et, oui, cette méthode peut être utilisée non seulement pour des nombres aléatoires, mais aussi pour tout aléatoire. Dans ce cas, nous l'avons utilisé pour sélectionner «accidentellement» un ordinateur dans le jeu «Rock-paper-scissors».

Source: https://habr.com/ru/post/fr474818/


All Articles