Bienvenue mes cadettes et cadets ! Cette semaine, pour un projet, j'ai voulu créer cette horloge.

La difficulté était de faire en sorte que les chiffres soient bien répartis sur le pourtour du cercle. Dans un premier temps, je voulais partir sur du Javascript, car j'avais déjà fait un peu de trigonométrie pour un ancien projet avec ce langage, mais lors de ma veille documentaire, j'ai découvert une petite nouveauté qui est arrivée cette année dans CSS 3 : tout le matériel pour faire de la trigonométrie. La fonctionnalité a été proposée par Safari en mars 2022, puis a été validée par le W3C : 10.4. Mathematical expressions - Trigonometric functions, avant d'être implémentée de décembre 2022 à mars 2023 par les autres navigateurs.
Rappelez-vous ces douces années de lycée, avec les cours de trigonométrie qu'on a toutes et tous adorés !
Rappels de trigonométrie
Le cercle trigonométrique
Un cercle trigonométrique est une figure géométrique, ayant un rayon d'une unité (m, cm, dm, mm...). Dans ce cercle passent les axes d'un graphique : axe des abscisses (axe des x) et axe des ordonnées (axe des y).

Le cercle permet alors de calculer n'importe quel angle grâce aux fonctions : cosinus (cos) et sinus (sin). Je vois bien que celles et ceux du fond de la classe se demandent : "Mais pourquoi il nous parle de ce cercle trigonométrique pour placer les chiffres de l'horloge ?" Vous allez voir c'est simple !
Cosinus et sinus
L'avantage que nous apporte ce cercle, c'est qu'il nous permet de calculer des cosinus et des sinus simplement, et ce, sans calculatrice ! En effet, le cosinus est la distance du point rapportée sur l'axe des x et le sinus celle rapportée sur l'axe des y. Petit exemple :

Pour un angle α, la distance entre 0 et px (en orange ci-dessus) correspond à la valeur de cos(α), alors que la distance entre 0 et py (en bleu ci-dessus) correspond à sin(α). C'est cette propriété qui va nous intéresser pour placer nos chiffres.
Angle et trigonométrie
Dans la vie quotidienne, quand on parle d'angle, on a tendance à utiliser les degrés. En trigonométrie, on utilisera plus souvent les radians. L'angle en radians correspond à la longueur de l'arc de cercle formalisé par l'angle (partie verte sur l'image ci-dessus). Pour rappel, le périmètre d'un cercle se calcule avec la formule suivante : 2 * π * R où R est le rayon du cercle.
Pour faire la conversion "degrés → radians", on va utiliser un cercle complet. Si l'on prend un disque entier, l'angle en degrés sera de 360°. En radian, on prend la longueur de l'arc de cercle, qui correspond au cercle entier, donc à son périmètre. Il sera donc de 2 * π * R, sachant qu'on a dit plus haut que notre cercle avait comme particularité d'avoir un rayon d'une unité, le résultat final en radians sera : 360° = 2π rad. On peut ensuite calculer les autres angles 180° = π rad, 90° = π/2 rad, ...
Deuxième chose à savoir, les angles en radians peuvent être "simplifiés" :
- Un cercle trigonométrique se lit dans le sens antihoraire. Un angle de 90° vers le haut du cercle sera de
π/2 rad, là où le même angle vers le bas sera de 270° et donc de3π/2 radou de-π/2 rad, ce qui équivaudrait à -90°, notation qu'on utilise peu en degrés. - Tout angle supérieur à
2π radpourra être simplifié en lui retranchant "2π". Un angle de 450° équivaut à5π/2 radet sera donc simplifié enπ/2 rad.
Construction de notre horloge
Le code de départ
Maintenant qu'on s'est bien rafraîchi la mémoire en ce qui concerne la trigonométrie, on a tout ce qu'il nous faut pour créer notre horloge. Pour commencer, je construis le HTML. Pour rappel, l'horloge doit ressembler à ça :

<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Horloge</title>
</head>
<body>
<section>
<div id="clock">
<div></div>
<div></div>
<button>12</button>
<button>24</button>
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
<span>7</span>
<span>8</span>
<span>9</span>
<span>10</span>
<span>11</span>
<span>12</span>
</div>
</section>
</body>
</html>Le code HTML est assez simple :
- Une
sectionpour englober notre horloge et la centrer facilement sur notre page. Elle n'est pas indispensable, elle ne me sert que pour avoir un environnement de travail propre pour le projet actuel. - La
divavec l'id "clock", qui sera le conteneur des autres éléments HTML,. Elle portera le fond de notre horloge (le cercle un peu plus foncé). - Les deux premières
divenfants dediv#clockreprésentant, dans l'ordre, le petit rond au centre de l'horloge et l'aiguille. - Les deux
buttonpour changer entre le matin et l'après-midi. - Et les douze
spanpour représenter les chiffres du cadran qu'on va devoir placer avec nos fonctions trigonométriques.
On met en place le CSS de départ :
:root {
--page-bgc: rgb(25, 25, 25);
--clock-bgc: rgb(18, 18, 18);
--clock-size: 320px;
}
body {
margin: 0;
}
section {
display: flex;
justify-content: center;
align-items: center;
background-color: var(--page-bgc);
width: 100vw;
height: 100vh;
}
#clock {
width: var(--clock-size);
height: var(--clock-size);
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
background-color: var(--clock-bgc);
color: white;
}Dans l'ordre :
- Je définis les variables CSS dans
:root. Pour plus de détails, vous référer à la partie "Variables CSS" de ce chapitre. - Dans
body, je supprime la marge par défaut pour avoir une page complète. - Pour la section :
- J'ajoute l'option
flexpour centrer notre horloge sur l'axe principal et secondaire de notre page. Pour rappel, vous référer au cours sur le Flex. - Je mets une couleur de fond via la variable
--page-bgc. - Je fais en sorte que la section prenne toute la largeur de la page (vw) et toute sa hauteur (vh).
- J'ajoute l'option
- Pour la
divavec l'id "clock" :- J'utilise la variable
--clock-sizepourwidthetheightpour qu'elle ait la même taille en horizontal et en vertical. - J'ajoute l'option
flexpour centrer tous les éléments au centre de l'horloge. J'expliquerai plus bas pourquoi. - Je mets un
border-radiusà 50% pour avoir un cercle parfait. - J'ajoute une couleur de fond via la variable
--clock-bgc. - Je mets la couleur du texte en blanc avec
color.
- J'utilise la variable
J'obtiens alors un cercle plus foncé (fond de notre horloge) avec les deux boutons qui ont leur design par défaut et les douze chiffres écrits en blanc, le tout aligné au centre du cercle par flex.

Placer les boutons
À partir d'ici, pour vous entraîner, je vais donner des consignes de design afin de vous permettre de tester de votre côté, avant de vous fournir le code de correction.
On va commencer par la partie "simple" : les boutons "12" et "24" qui nous permettront de sélectionner le matin ou l'après-midi. Pour commencer, on va s'occuper de leur design. Ils ont trois états :
- Par défaut = non sélectionné et sans survol : la police est blanche à
1rem, pas de bordure, pas de couleur de fond, les boutons font 36px de haut et de large et ils sont ronds. - Au survol : le fond est noir transparent (
rgba(0, 0, 0, .6)), le curseur de la souris doit avoir une forme de main spéciale pour le clic. - Sélectionné : le fond est bleu (
rgb(50, 243, 255)), la police est noire. Pour cet état, une classselectedsera ajoutée en Javascript. Pour tester, ajoutez manuellement la classe au bouton "12" dans le HTML, en production la classe sera ajoutée au clic par Javascript.
Pour le placement, ils sont en position absolute (attention au placement en absolute, il a une petite spécificité qu'il ne faut pas oublier, si besoin vous pouvez revoir la partie dédiée sur ce cours) et les positions respectives sont :
- Bouton "12" = 0 par rapport à la gauche et au haut du parent.
- Bouton "24" = 0 par rapport à la droite et au haut du parent.
Code de correction :
:root {
--page-bgc: rgb(25, 25, 25);
--clock-bgc: rgb(18, 18, 18);
--clock-size: 320px;
--button-selected-bgc: rgba(50, 243, 255);
}
#clock button {
--button-size: 36px;
color: white;
font-size: 1rem;
border: none;
background-color: transparent;
height: var(--button-size);
width: var(--button-size);
border-radius: 50%;
position: absolute;
top: 0;
}
#clock button:first-of-type {
left: 0;
}
#clock button:last-of-type {
right: 0;
}
#clock button:hover {
background-color: rgba(0, 0, 0, 0.6);
cursor: pointer;
}
#clock button.selected {
background-color: var(--button-selected-bgc);
color: black;
}- Le sélecteur
#clock buttonme permet de sélectionner les deux boutons d'un coup. Pour rappel, il veut dire "je sélectionne tous lesbuttoncontenus dans un élément qui a l'id 'clock'". À l'intérieur, j'ai ajouté les élements demandés dans les consignes. On remarque plusieurs choses :- J'ai réglé la bordure sur
noneet lebackground-colorsurtransparentpour supprimer les valeurs par défaut liées au design des boutons. - Pour le
widthet leheight, comme je veux un rond parfait, ils doivent être identiques. J'aurais pu mettre directement les valeurs en dur, mais je suis passé par une variable directement définie dans mon bouton. Cela permet d'avoir une seule valeur utilisée deux fois, ce qui fait que si demain je souhaite que mon bouton ait un diamètre de 40px, je ne change que la valeur de la variable et ça changera directement lewidthet leheighten une seule modification. - Le position est en
absolutecomme indiqué dans les consignes. Si vous souhaitez que les boutons se positionnent correctement en fonction de #clock et non de la page générale, il faut ajouterposition: relativeà #clock. En effet, si vous avez relu le cours que je vous ai mis en lien plus haut, la position en absolu se fera en fonction du premier parent qui déclare unepositionautre questatic. - Les deux sont alignés en haut de leur parent donc on met dans le sélecteur commun le
top: 0.
- J'ai réglé la bordure sur
- Ensuite, je sélectionne le premier bouton à part pour l'aligner à gauche. Pour ça, j'utilise le sélecteur spécifique
:first-of-typequi permet de sélectionner le premier élément de ce type (icibutton) dans le parent. Je lui mets la valeurleft: 0pour aligner le bouton à gauche du parent. - Puis je fais de même pour le second bouton avec le sélecteur
:last-of-typeet la valeurright: 0pour l'aligner à droite du parent. - Pour l'état "au survol" qui correspond au sélecteur
:hover, j'ajoute la couleur de fond et le curseur demandé. - Pour l'état "selected", j'ai utilisé la sélection
button.selectedavec les deux sélecteurs collés pour dire "je veux sélectionner tous lesbuttonqui ont la classeselected". Puis j'ajoute la couleur de fond via la variable--button-selected-bgcet la couleur de police.
Vous devriez arriver au résultat demandé : le fond de l'horloge en noir, les boutons pour sélectionner le matin ou le soir de part et d'autre de l'horloge en haut.

Les chiffres de l'horloge
Maintenant que nos boutons sont parfaits, on va pouvoir passer au but de ce chapitre : placer les chiffres de notre horloge. Avant de commencer à utiliser la trigonométrie, on va commencer par faire en sorte :
- que nos chiffres soient tous vraiment placés au centre de l'horloge = au niveau de l'origine sur notre graphique, les uns au dessus des autres ;
- que les
spansoient rondes et fassent 30px de diamètre ; - qu'il y ait le même effet au survol que nos boutons "12" et "24".
#clock button:hover, #clock span:hover {
background-color: rgba(0, 0, 0, 0.6);
cursor: pointer;
}
#clock span {
--number-size: 30px;
position: absolute;
width: var(--number-size);
height: var(--number-size);
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}Vous pouvez voir que :
- j'ai ajouté
#clock span:hoverau sélecteur#clock button:hovercar j'ai bien dit que je voulais le même effet. Autant mutualiser le code ! - pour les
span:- j'ai utilisé la même technique que ci-dessus pour le
widthet leheightavec la variable et j'ai mis un border-radius pour qu'elles soient rondes. - je les ai mises en
absolutepour qu'elles se placent toutes en fonction de l'origine = centre du cercle. Le fait qu'elles soient au centre est lié au fait que#clockest enflexavec les optionsalign-itemsetjustify-contentsàcenter. - j'ai également mis ces éléments en
flexavec les mêmes options pour centrer le texte à l'intérieur.
- j'ai utilisé la même technique que ci-dessus pour le
Les chiffres et la trigonométrie
On y est ! On va utiliser nos fonctions cos et sin pour placer nos chiffres ! D'abord, petite analyse d'une horloge. Les chiffres sont tous répartis équitablement sur le périmètre de celle-ci. Il y a 12 chiffres, je rappelle qu'un cercle complet a un angle de 2π qui sera divisé en 12 parties, donc chacun des chiffres sera séparé des autres par un angle de 2π / 12 = π/6 rad. C'est le chiffre "3" qui est sur l'axe des abscisses et donc qui est à 0°, les autres se placeront en fonction de ça.
J'ajoute une variable (--number-position) dans #clock span pour avoir la distance où placer les chiffres :
#clock span {
--number-size: 30px;
--number-position: calc(var(--clock-size) / 2 - var(--number-size));
position: absolute;
width: var(--number-size);
height: var(--number-size);
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}Cette distance, c'est le résultat de calc qui est une fonction CSS pour calculer une valeur en fonction d'autres valeurs. Pour la calculer, nous aurons besoin du rayon du cercle. Sachant que --clock-size correspond au width du cercle, cela correspond donc à son diamètre. Ainsi, pour avoir le rayon, on fait var(--clock-size) / 2, auquel j'enlève la taille de nos span pour qu'elles soient bien à l'intérieur.
Et voici la correction pour les fonctions trigonométriques :
#clock span {
--number-size: 30px;
--number-position: calc(var(--clock-size) / 2 - var(--number-size));
--number-angle: 0;
position: absolute;
width: var(--number-size);
height: var(--number-size);
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
transform: translate(calc(cos(var(--number-angle)) * var(--number-position)), calc(sin(var(--number-angle)) * var(--number-position)));
}
#clock span:nth-of-type(1) {
--number-angle: calc(pi / -3);
}
#clock span:nth-of-type(2) {
--number-angle: calc(pi / -6);
}
#clock span:nth-of-type(3) {
--number-angle: 0;
}
#clock span:nth-of-type(4) {
--number-angle: calc(pi / 6);
}
#clock span:nth-of-type(5) {
--number-angle: calc(pi / 3);
}
#clock span:nth-of-type(6) {
--number-angle: calc(pi / 2);
}
#clock span:nth-of-type(7) {
--number-angle: calc(2 * pi / 3);
}
#clock span:nth-of-type(8) {
--number-angle: calc(5 * pi / 6);
}
#clock span:nth-of-type(9) {
--number-angle: pi;
}
#clock span:nth-of-type(10) {
--number-angle: calc(-5 * pi / 6);
}
#clock span:nth-of-type(11) {
--number-angle: calc(-2 * pi / 3);
}
#clock span:nth-of-type(12) {
--number-angle: calc(pi / -2);
}La formule la plus importante est celle-ci :
transform: translate(calc(cos(var(--number-angle)) * var(--number-position)), calc(sin(var(--number-angle)) * var(--number-position)));Pour rappel :
translateprend la distance en px sur l'axe des x en premier paramètre, puis la valeur sur l'axe des y ; il peut aussi prendre en troisième paramètre la valeur sur l'axe des z quand on fait de la 3D.- cos calcule la distance de l'angle sur l'axe des x et le sin sur l'axe des y.
Si l'on décompose ce qu'on a :
- J'ai ajouté une variable
--number-angleavec une valeur de 0 par défaut pour l'intégrer ensuite dans nos fonctions trigonométriques. - Je place la variable ci-dessus dans cos en premier paramètre de
translatecar ça va nous donner la valeur de déplacement en x, puis dans sin en second paramètre pour avoir le déplacement en y. Comme on l'a dit plus haut, cette valeur sera toujours comprise entre 0 et 1 puisque le cercle trigonométrique a un rayon de 1 unité. - Je multiplie les valeurs retournées par les fonctions trigonométriques par la distance que je souhaite et qui correspond à la variable que je vous avais donnée avant le corrigé =
--number-positionpour avoir des valeurs comprises entre 0 et 130px. - Je place cette multiplication dans un
calcpour avoir le produit. Le produit avec cos en premier paramètre et le produit avec sin en second.
transform qu'une seule fois car la valeur des angles, je ne l'ai pas mise en dur mais en variable. En utilisant cette technique, la formule complexe n'est écrite qu'une seule fois, ensuite ce n'est que la valeur de l'angle qui est modifiée pour chaque chiffre. On aurait pu mettre cette longue formule sous le sélecteur de chaque span spécifique, mais si un jour, la formule était légèrement modifiée, il faudrait le faire 12 fois ! Ici, elle ne serait modifiée qu'une seule fois.Pour sélectionner les chiffres un par un, comme pour les boutons plus haut, j'ai utilisé le pseudo-sélecteur "nth-of-type". Puis j'ai modifié les angles dans chaque sélection :
- 1 : angle en degré = 60° vers le haut par rapport à l'axe des x, ce qui équivaut à
-π/3. Attention, je vous ai dit, en préambule de cette partie, que le sens des angles était inversé en CSS, donc dans le sens antihoraire, ils sont négatifs. - 2 :
-30° = -π/6. - 3 :
0 - 4 :
30° = π/6. - 5 :
60° = π/3. - 6 :
90° = π/2. - 7 :
120° = 2π/3. - 8 :
150° = 5π/6. - 9 :
180° = π. - 10 : à partir de là on peut simplifier en partant dans le sens antihoraire, 210° vers le bas ou -150° vers le haut, donc
-5π/6. - 11 :
240° ou -120° = -2π/3. - 12 :
270° ou -90° = -π/2.
Et voilà, on a créé notre horloge ! Pour ce qui est de son fonctionnement, on utilisera du Javascript, mais ça, c'est une autre histoire !
L'aiguille
Comme pour les boutons "12" et "24", pour passer les chiffres en mode "selected", on aura besoin de Javascript. Pour l'heure, on peut simuler que le 12 de notre horloge est sélectionné, placer le rond autour de ce chiffre et créer l'aiguille ainsi le centre de notre horloge. Voici les consignes :
- Le rond central sera de la même couleur que le bleu des boutons "12" et "24" en mode "selected". Il sera rond avec un diamètre de 10px et centré par rapport à l'horloge.
- L'aiguille ira du centre du rond central au centre du chiffre sélectionné. Elle aura une largeur de 2px et sera de la même couleur que ci-dessus.
- Le chiffre "12" aura une classe "selected" qui lui donnera la même couleur et fera passer la police en noir.
Dernière correction :
#clock div {
position: absolute;
background-color: var(--button-selected-bgc);
}
#clock div:nth-child(1) {
width: 10px;
height: 10px;
border-radius: 50%;
}
#clock div:nth-child(2) {
--hand-length: calc(var(--clock-radius) - var(--number-size) - 10px);
width: 2px;
height: var(--hand-length);
top: calc(var(--clock-radius) - var(--hand-length));
}Pour le détail :
- Dans le premier bloc, je mets les modifications communes aux deux
div: la couleur et le fait que celles-ci soient enabsolute. - Pour la partie centrale, la première
div, je lui mets la même taille en largeur et en hauteur, ainsi que leborder-radiuspour qu'elle soit ronde. - Pour l'aiguille, la seconde
div, j'en règle la largeur à 2px.- Ensuite, pour la longueur, comme dit dans l'énoncé, je veux que ce soit la longueur entre le cercle central et le chiffre, c'est donc le rayon de notre cercle moins la taille de notre chiffre ; je mets donc les deux variables dans un
calcet j'enlève en plus 10px, ce qui correspond à la marge qu'on a mise autour des chiffres. - Enfin, pour le
top, définissant le placement, je mets le rayon du cercle moins la taille de l'aiguille, ce qui permet de ne garder que l'espace au-dessus de l'aiguille.
- Ensuite, pour la longueur, comme dit dans l'énoncé, je veux que ce soit la longueur entre le cercle central et le chiffre, c'est donc le rayon de notre cercle moins la taille de notre chiffre ; je mets donc les deux variables dans un