Création d'une horloge avec les nouvelles fonctions de trigonométrie CSS sin() et cos()

Création d'une horloge avec les nouvelles fonctions de trigonométrie CSS sin() et cos()

Nœud source: 1999799

Les fonctions de trigonométrie CSS sont là ! Eh bien, ils le sont si vous utilisez les dernières versions de Firefox et Safari, c'est-à-dire. Avoir ce genre de puissance mathématique en CSS ouvre tout un tas de possibilités. Dans ce didacticiel, j'ai pensé que nous plongerions nos orteils dans l'eau pour avoir une idée de quelques-unes des nouvelles fonctions : sin() ainsi que cos().

Il existe d'autres fonctions de trigonométrie dans le pipeline, y compris tan() - alors pourquoi se concentrer uniquement sur sin() ainsi que cos()? Ils se trouvent être parfaits pour l'idée que j'ai en tête, qui est de placer du texte le long du bord d'un cercle. Cela a été couvert ici sur CSS-Tricks quand Chris a partagé une approche qui utilise un mixin Sass. C'était il y a six ans, alors donnons-lui le traitement de pointe.

Voici ce que j'ai en tête. Encore une fois, il n'est pris en charge que dans Firefox et Safari pour le moment :

Donc, ce n'est pas exactement comme des mots formant une forme circulaire, mais nous plaçons des caractères de texte le long du cercle pour former un cadran d'horloge. Voici quelques balises que nous pouvons utiliser pour lancer les choses :

<div class="clock"> <div class="clock-face"> <time datetime="12:00">12</time> <time datetime="1:00">1</time> <time datetime="2:00">2</time> <time datetime="3:00">3</time> <time datetime="4:00">4</time> <time datetime="5:00">5</time> <time datetime="6:00">6</time> <time datetime="7:00">7</time> <time datetime="8:00">8</time> <time datetime="9:00">9</time> <time datetime="10:00">10</time> <time datetime="11:00">11</time> </div>
</div>

Ensuite, voici quelques styles super basiques pour le .clock-face récipient. J'ai décidé d'utiliser le <time> tag avec un datetime attribuer. 

.clock { --_ow: clamp(5rem, 60vw, 40rem); --_w: 88cqi; aspect-ratio: 1; background-color: tomato; border-radius: 50%; container-type: inline; display: grid; height: var(--_ow); place-content: center; position: relative; width var(--_ow);
}

J'ai un peu décoré les choses là-dedans, mais seulement pour obtenir la forme de base et la couleur de fond pour nous aider à voir ce que nous faisons. Remarquez comment nous sauvons le width valeur dans un Variable CSS. Nous l'utiliserons plus tard. Pas grand chose à voir pour l'instant :

Grand cercle de couleur tomate avec une liste verticale des nombres 1-12 sur la gauche.

Cela ressemble à une sorte d'expérience d'art moderne, non ? Introduisons une nouvelle variable, --_r, pour stocker les cercles radius, qui est égal à la moitié de la largeur du cercle. De cette façon, si la largeur (--_w) change, la valeur du rayon (--_r) sera également mis à jour — grâce à une autre fonction mathématique CSS, calc():

.clock { --_w: 300px; --_r: calc(var(--_w) / 2); /* rest of styles */
}

Maintenant, un peu de maths. Un cercle est de 360 ​​degrés. Nous avons 12 étiquettes sur notre horloge, nous voulons donc placer les chiffres tous les 30 degrés (360 / 12). Au pays des maths, un cercle commence à 3 heures, donc midi est en fait moins 90 degrés de cela, qui est de 270 degrés (360 - 90).

Ajoutons une autre variable, --_d, que nous pouvons utiliser pour définir un intensité valeur pour chaque nombre sur le cadran de l'horloge. Nous allons incrémenter les valeurs de 30 degrés pour boucler notre cercle :

.clock time:nth-child(1) { --_d: 270deg; }
.clock time:nth-child(2) { --_d: 300deg; }
.clock time:nth-child(3) { --_d: 330deg; }
.clock time:nth-child(4) { --_d: 0deg; }
.clock time:nth-child(5) { --_d: 30deg; }
.clock time:nth-child(6) { --_d: 60deg; }
.clock time:nth-child(7) { --_d: 90deg; }
.clock time:nth-child(8) { --_d: 120deg; }
.clock time:nth-child(9) { --_d: 150deg; }
.clock time:nth-child(10) { --_d: 180deg; }
.clock time:nth-child(11) { --_d: 210deg; }
.clock time:nth-child(12) { --_d: 240deg; }

OK, c'est le moment de se salir les mains avec le sin() ainsi que cos() les fonctions! Ce que nous voulons faire, c'est les utiliser pour obtenir les coordonnées X et Y de chaque nombre afin de pouvoir les placer correctement autour du cadran de l'horloge.

La formule de la coordonnée X est radius + (radius * cos(degree)). Branchons cela dans notre nouveau --_x variable:

--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));

La formule de la coordonnée Y est radius + (radius * sin(degree)). Nous avons ce dont nous avons besoin pour calculer que:

--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));

Il y a quelques tâches ménagères que nous devons faire pour configurer les nombres, alors mettons-leur un style de base pour nous assurer qu'ils sont absolument positionnés et placés avec nos coordonnées :

.clock-face time { --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d)))); --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d)))); --_sz: 12cqi; display: grid; height: var(--_sz); left: var(--_x); place-content: center; position: absolute; top: var(--_y); width: var(--_sz);
}

Avertissement --_sz, que nous utiliserons pour le width ainsi que height des chiffres dans un instant. Voyons ce que nous avons jusqu'à présent.

Grand cercle de couleur tomate avec des étiquettes de numéro d'heure décentrées le long de son bord.

Cela ressemble définitivement plus à une horloge! Voyez comment le coin supérieur gauche de chaque nombre est positionné au bon endroit autour du cercle ? Nous devons "rétrécir" le rayon lors du calcul des positions pour chaque nombre. Nous pouvons déduire la taille d'un nombre (--_sz) de la taille du cercle (--_w), avant de calculer le rayon :

--_r: calc((var(--_w) - var(--_sz)) / 2);
Grand cercle de couleur tomate avec des étiquettes de numéro d'heure le long de son bord arrondi.

Bien mieux ! Modifions les couleurs pour qu'elles soient plus élégantes :

Un cadran blanc avec des chiffres sur un fond gris foncé. L'horloge n'a pas de bras.

On pourrait s'arrêter là ! Nous avons atteint l'objectif de placer du texte autour d'un cercle, n'est-ce pas ? Mais qu'est-ce qu'une horloge sans bras pour afficher les heures, les minutes et les secondes ?

Utilisons une seule animation CSS pour cela. Tout d'abord, ajoutons trois éléments supplémentaires à notre balisage,

<div class="clock"> <!-- after <time>-tags --> <span class="arm seconds"></span> <span class="arm minutes"></span> <span class="arm hours"></span> <span class="arm center"></span>
</div>

Ensuite, un balisage commun pour les trois bras. Encore une fois, la plupart de cela consiste simplement à s'assurer que les bras sont absolument positionnés et placés en conséquence :

.arm { background-color: var(--_abg); border-radius: calc(var(--_aw) * 2); display: block; height: var(--_ah); left: calc((var(--_w) - var(--_aw)) / 2); position: absolute; top: calc((var(--_w) / 2) - var(--_ah)); transform: rotate(0deg); transform-origin: bottom; width: var(--_aw);
}

Nous utiliserons le même animation pour les trois bras :

@keyframes turn { to { transform: rotate(1turn); }
}

La seule différence est le temps que mettent les bras individuels pour faire un tour complet. Pour le bras des heures, ça prend 12 heures faire un tour complet. Le animation-duration La propriété accepte uniquement les valeurs en millisecondes et secondes. Tenons-nous en aux secondes, soit 43,200 XNUMX secondes (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Il faut 1 heure pour le bras des minutes faire un tour complet. Mais nous voulons que ce soit un animation en plusieurs étapes ainsi le mouvement entre les bras est échelonné plutôt que linéaire. Nous aurons besoin de 60 étapes, une pour chaque minute :

animation: turn 3600s steps(60, end) infinite;

La bras des secondes is presque la même comme armement des minutes, mais la durée est de 60 secondes au lieu de 60 minutes :

animation: turn 60s steps(60, end) infinite;

Mettons à jour les propriétés que nous avons créées dans les styles communs :

.seconds { --_abg: hsl(0, 5%, 40%); --_ah: 145px; --_aw: 2px; animation: turn 60s steps(60, end) infinite;
}
.minutes { --_abg: #333; --_ah: 145px; --_aw: 6px; animation: turn 3600s steps(60, end) infinite;
}
.hours { --_abg: #333; --_ah: 110px; --_aw: 6px; animation: turn 43200s linear infinite;
}

Et si nous voulions commencer à l'heure actuelle ? Nous avons besoin d'un peu de JavaScript :

const time = new Date();
const hour = -3600 * (time.getHours() % 12);
const mins = -60 * time.getMinutes();
app.style.setProperty('--_dm', `${mins}s`);
app.style.setProperty('--_dh', `${(hour+mins)}s`);

J'ai ajouté id="app" au cadran et définissez deux nouvelles propriétés personnalisées dessus qui définissent un négatif animation-delay, comme l'a fait Mate Marschalko quand il a partagé une horloge CSS uniquementL’ getHours() méthode de JavaScipt Date l'objet utilise le format 24 heures, nous utilisons donc le remainder opérateur pour le convertir au format 12 heures.

Dans le CSS, nous devons ajouter le animation-delay ainsi que:

.minutes { animation-delay: var(--_dm, 0s); /* other styles */
} .hours { animation-delay: var(--_dh, 0s); /* other styles */
}

Encore une chose. Utilisation de CSS @supports et les propriétés que nous avons déjà créées, nous pouvons fournir une alternative aux navigateurs qui ne prennent pas en charge sin() ainsi que  cos(). (Merci, Temani Afif !) :

@supports not (left: calc(1px * cos(45deg))) {
  time {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d)))
  }
}

Et voilà ! Notre horloge est terminée ! Voici la démo finale une fois de plus. Encore une fois, il n'est pris en charge que dans Firefox et Safari pour le moment.

Que pouvons-nous faire d'autre?

Juste déconner ici, mais nous pouvons rapidement transformer notre horloge en une galerie d'images circulaire en remplaçant le <time> balises avec <img> puis mise à jour de la largeur (--_w) et le rayon (--_r) valeurs:

Essayons-en un de plus. J'ai mentionné plus tôt à quel point l'horloge ressemblait à une expérience d'art moderne. Nous pouvons nous pencher là-dessus et recréer un motif que j'ai vu sur une affiche (que je n'ai malheureusement pas achetée) dans une galerie d'art l'autre jour. Si je me souviens bien, il s'appelait "Lune" et consistait en un groupe de points formant un cercle.

Un grand cercle formé d'un tas de petits cercles remplis de différentes couleurs de terre.

Nous utiliserons une liste non ordonnée cette fois puisque les cercles ne suivent pas un ordre particulier. Nous n'allons même pas mettre tous les éléments de la liste dans le balisage. Au lieu de cela, injectons-leur du JavaScript et ajoutons quelques contrôles que nous pouvons utiliser pour manipuler le résultat final.

Les commandes sont des entrées de plage (<input type="range">) que nous envelopperons dans un <form> et écoutez le input un événement.

<form id="controls"> <fieldset> <label>Number of rings <input type="range" min="2" max="12" value="10" id="rings" /> </label> <label>Dots per ring <input type="range" min="5" max="12" value="7" id="dots" /> </label> <label>Spread <input type="range" min="10" max="40" value="40" id="spread" /> </label> </fieldset>
</form>

Nous allons exécuter cette méthode sur "input", ce qui créera un tas de <li> éléments avec le degré (--_d) variable que nous avons utilisée précédemment appliquée à chacun. Nous pouvons également réutiliser notre variable de rayon (--_r).

Je veux aussi que les points soient de couleurs différentes. Alors, randomisons (enfin, pas complètement aléatoire) la valeur de couleur HSL pour chaque élément de la liste et stockez-la comme une nouvelle variable CSS, --_bgc:

const update = () => { let s = ""; for (let i = 1; i <= rings.valueAsNumber; i++) { const r = spread.valueAsNumber * i; const theta = coords(dots.valueAsNumber * i); for (let j = 0; j < theta.length; j++) { s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random( 50, 25 )},${random(90, 50)}%,${random(90, 60)}%)"></li>`; } } app.innerHTML = s;
}

La random() méthode sélectionne une valeur dans une plage définie de nombres :

const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;

Et c'est tout. Nous utilisons JavaScript pour rendre le balisage, mais dès qu'il est rendu, nous n'en avons pas vraiment besoin. Le sin() ainsi que cos() les fonctions nous aident à positionner tous les points aux bons endroits.

Réflexions finales

Placer des objets autour d'un cercle est un exemple assez basique pour démontrer les pouvoirs des fonctions de trigonométrie comme sin() ainsi que cos(). Mais c'est vraiment cool que nous obtenions des fonctionnalités CSS modernes qui fournissent de nouvelles solutions pour les anciennes solutions de contournement, je suis sûr que nous verrons des cas d'utilisation beaucoup plus intéressants, complexes et créatifs, en particulier avec la prise en charge des navigateurs par Chrome et Edge.

Horodatage:

Plus de Astuces CSS