CSS / Animation de texte à l'infini

Ne perdons pas de temps, voici l’effet que je cherche à reproduire.

J’ai trouvé des exemples similaires sur la toile, par exemple le premier sur cette page. Mais le côté infini du mouvement n’est pas très convaincant. On y voit un blanc avant de reprendre.

Je me suis donc mis en tête de reproduire cet effet par moi-même.

Les resources

Si vous êtes impatient, vous trouverez les sources du résultat final sur ce repository.

Structuration du DOM

Dans cet effet, nous avons un titre principal et plusieurs sous-titres.

Pour ne faire apparaitre qu’un seul sous-titre à la fois, je veux pouvoir les placer comme bon me semble dans un <div>, y compris en dehors de l’espace visible de ce <div>. Et bien sûr, si le sous-titre déborde, il doit être caché.

Je vais donc mettre en place la structure suivante :


<div class="title-box"> <!-- J'entoure avec cet autre div uniquement pour pouvoir le placer dans une page -->
    <h1>Titre principal</h1>
    <div> <!-- Ce div va définir l'espace visible, je l'appellerai parfois <div> encadrant -->
        <div>Sous-titre 1</div>
        <div>Sous-titre 2</div>
        <div>Sous-titre 3</div>
    </div>
</div>

En l’état, voici ce que ça donne et on est d’accord, c’est moche pour le moment :

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

Un peu de style

Tout d’abord, on va resserrer un peu tout ça en enlevant les marges sur le titre :

h1 {
    margin: 0;
}

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

L’étape suivante est importante. Elle va me donner le pouvoir de placer mes sous-tires comme bon me semble dans le <div> qui les entoure. Et en fait, c’est assez simple, le <div> en question va prendre une position: relative; et chaque sous-titre va prendre une position position: absolute;. Les sous-titres auront une position qui est absolue relativement au parent.

Le seul hic est qu’en spécifiant la position: relative;, le <div> ne prend plus tout seul toute la place dont il a besoin et donc, on va l’aider un peu en lui spécifiant une hauteur.

.title-box > div {
    position: relative;
    height: 1.5em;
}

.title-box > div > div {
    position: absolute;
}

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

Et c’est encore plus moche qu’avant car tous les sous-titres sont les uns sur les autres. Mais c’est exactement le pouvoir que je voulais acquérir. C’est-à-dire que j’ai le pouvoir de rendre tout ça encore plus moche avec la propriété top par exemple.

.title-box > div > div {
    position: absolute;
    top: -1.5em;
}

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

Et voilà, les sous-titres sont non seulement les uns sur les autres et en plus ils chevauchent le titre principal. La bonne nouvelle, c’est qu’ils ne sont plus dans la zone visible de notre <div> encadrant. On peut donc les cacher avec un petit overflow: hidden;.

.title-box > div {
    position: relative;
    height: 1.5em;
    overflow: hidden;
}

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

Nous voilà avec des sous-titres bien présents dans le DOM, mais caché et prêt à se faire animer.

Une petite précision, vous aurez remarqué que j’utilise des em comme unité pour définir la hauteur du <div> et pour la position des sous-titres. Cette unité permet de s’adapter à la taille de la font utilisée pour les sous-titres. Par contre, pour que celà fonctionne, la taille de la font devra préciser sur le <div> encadrant.

Animations

Le but du jeu, pour que le code ne devienne pas trop compliqué, est de définir une seule animation qui fonctionnera pour les 3 sous-titres. Et grâce à un délai au démarrage, on obtiendra une fluidité dans le mouvement. Voici à quoi peut ressembler l’animation CSS :

@keyframes subtitle-rolling {
    0% {
        top: -1.5em;
    }
    10% {
        top: 0;
    }
    33% {
        top: 0;
    }
    40% {
        top: 1.5em;
    }
    100% {
        top: 1.5em;
    }
}

L’idée de l’animation est de partir depuis une position qui est au-dessus de la partie visible (top: -1.5em;), de rapidement le faire défiler sur la surface visible (top: 0;). Ensuite, on le laisse un peu sur la surface visible (le temps que des internautes puissent le lire) avant de faire le faire disparaitre mais en dessous de l’espace visible (top: 1.5em;). Lorsque l’animation redémarrera, le sous-titre sera à nouveau caché instantanément au-dessus de la surface visible.

On applique cette animation à chaque sous-titre :

.title-box > div > div {
    position: absolute;
    top: -1.5em;
    animation: subtitle-rolling 6s linear infinite;
}

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

On est content, tous nos sous-titres s’animent, mais ils défilent en même temps et toujours les uns sur les autres. Il va falloir décaler le démarrage de l’animation pour chacun. Comme je suis fainéant, je vais essayer de rendre ça un peu générique.

Je vais utiliser les variables CSS pour m’aider, sur chaque sous-titre, je vais définir une variable CSS --subtitle-position qui va être un simple index qui va de 0 à 2.


<div class="title-box"> <!-- J'entoure avec cet autre div uniquement pour pouvoir le placer dans une page -->
    <h1>Titre principal</h1>
    <div> <!-- Ce div va définir l'espace visible, je l'appellerai parfois <div> encadrant -->
        <div style="--subtitle-position: 0;">Sous-titre 1</div>
        <div style="--subtitle-position: 1;">Sous-titre 2</div>
        <div style="--subtitle-position: 2;">Sous-titre 3</div>
    </div>
</div>

Je veux faire démarrer chaque sous-titre à 2s d’écart. Pour pouvoir le faire, j’ai à ma disposition la propriété animation-delay. Dans cette propriété, je vais réutiliser la variable que nous venons de définir en multipliant sa valeur par 2. Pour cette partie, le code est nettement plus parlant :

.title-box > div > div {
    position: absolute;
    top: -1.5em;
    animation: subtitle-rolling 6s linear infinite;
    animation-delay: calc(var(--subtitle-position) * 2s);
}

Ainsi, l’animation du premier sous-titre commencera immédiatement, le second commencera au bout de 1x2s et le dernier au bout de 2x2s. Et voici le résultat :

Titre principal

Sous-titre 1
Sous-titre 2
Sous-titre 3

Voilà, j’espère que cet article vous aura été utile pour comprendre en détail ce qui se passe sur cet effet. J’ai essayé de reproduire le cheminement qui a été le mien tout au long. N’hésitez pas à me contacter, je prends toute forme de remarque.


HTMLCSS

1023 Words

2023-05-25 00:00 +0000