Créer des animations de texte fractionné

Présentation de base sur la création d'animations de lettres et de mots fractionnés.

Dans cet article, je vais vous expliquer comment résoudre les interactions et les animations de texte fractionné sur le Web qui soient minimales, accessibles et qui fonctionnent sur tous les navigateurs. Essayez la démo.

Démo

Si vous préférez la vidéo, voici une version YouTube de cet article:

Présentation

Les animations de texte fractionné peuvent être incroyables. Nous n'aborderons que partiellement le potentiel de l'animation dans cet article, mais il fournit une base sur laquelle s'appuyer. L'objectif est d'animer progressivement. Le texte doit être lisible par défaut, avec l'animation intégrée par-dessus. Les effets de mouvement du texte fractionné peuvent devenir extravagants et potentiellement gênants. Par conséquent, nous ne manipulons le code HTML ou n'appliquerons des styles de mouvement que si l'utilisateur est d'accord avec le mouvement.

Voici un aperçu général du workflow et des résultats:

  1. Préparez des variables conditionnelles de mouvement réduit pour CSS et JS.
  2. Préparez les utilitaires de fractionnement du texte en JavaScript.
  3. Orchestrez les conditions et les utilitaires au chargement de la page.
  4. Rédigez des transitions et des animations CSS pour les lettres et les mots (la partie spéciale !).

Voici un aperçu des résultats conditionnels que nous souhaitons obtenir :

Capture d'écran des outils de développement Chrome avec le panneau "Elements" ouvert et le mouvement réduit défini sur "reduce" (réduction), et le cache h1 s'affiche non fractionné
La préférence de l'utilisateur est de réduire le mouvement : le texte est lisible/non fractionné

Si un utilisateur préfère réduire le mouvement, nous laissons le document HTML tel quel et n'effectuons aucune animation. Si le mouvement est correct, nous pouvons le découper en morceaux. Voici un aperçu du code HTML après que JavaScript a divisé le texte par lettre.

Capture d'écran des outils de développement Chrome avec le panneau "Éléments" ouvert, la réduction du mouvement définie sur "Réduire" et l'élément h1 affiché sans fractionnement
Le mouvement est accepté par l'utilisateur. Le texte est divisé en plusieurs éléments <span>.

Préparer des conditions de mouvement

La requête multimédia @media (prefers-reduced-motion: reduce), qui est disponible, sera utilisée à partir de CSS et de JavaScript dans ce projet. Cette requête média est notre condition principale pour décider de diviser le texte ou non. La requête média CSS permet de bloquer les transitions et les animations, tandis que la requête média JavaScript permet de bloquer la manipulation HTML.

Préparer l'instruction CSS conditionnelle

J'ai utilisé PostCSS pour activer la syntaxe des requêtes multimédias de niveau 5, où je peux stocker une requête multimédia booléenne dans une variable:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

Préparer la condition JS

En JavaScript, le navigateur permet de vérifier les requêtes multimédias. J'ai utilisé la déstructuration pour extraire et renommer le résultat booléen de la vérification des requêtes multimédias :

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

Je peux ensuite tester motionOK et ne modifier le document que si l'utilisateur n'a pas demandé à réduire le mouvement.

if (motionOK) {
  // document split manipulations
}

Je peux vérifier la même valeur à l'aide de PostCSS pour activer la syntaxe @nest de la version préliminaire 1 de l'imbrication. Cela me permet de stocker toute la logique de l'animation et ses exigences de style pour le parent et les enfants au même endroit :

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

Avec la propriété personnalisée PostCSS et une valeur booléenne JavaScript, nous sommes prêts à mettre à niveau l'effet de manière conditionnelle. Cela nous amène à la section suivante, où je décompose le JavaScript pour transformer des chaînes en éléments.

Scinder le texte

Vous ne pouvez pas animer individuellement les lettres, les mots, les lignes, etc. d'un texte avec CSS ou JS. Pour obtenir cet effet, nous avons besoin de cases. Si nous voulons animer chaque lettre, chaque lettre doit être un élément. Si nous voulons animer chaque mot, chaque mot doit être un élément.

  1. Créer des fonctions utilitaires JavaScript pour diviser des chaînes en éléments
  2. Orchestration de l'utilisation de ces utilitaires

Fonction utilitaire de fractionnement des lettres

Un bon point de départ est une fonction qui prend une chaîne et renvoie chaque lettre dans un tableau.

export const byLetter = text =>
  [...text].map(span)

La syntaxe de propagation d'ES6 a vraiment contribué à accélérer cette tâche.

Fonction utilitaire de division des mots

Comme pour la division des lettres, cette fonction prend une chaîne et renvoie chaque mot dans un tableau.

export const byWord = text =>
  text.split(' ').map(span)

La méthode split() sur les chaînes JavaScript nous permet de spécifier les caractères à couper. J'ai inséré un espace vide, indiquant une division entre les mots.

Créer une fonction utilitaire de boîtes

L'effet nécessite des cases pour chaque lettre. Dans ces fonctions, nous voyons que map() est appelé avec une fonction span(). Voici la fonction span().

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

Il est essentiel de noter qu'une propriété personnalisée appelée --index est définie avec la position du tableau. Les cases pour les animations de lettres sont très utiles, mais disposer d'un indice à utiliser dans le CSS est un ajout apparemment mineur qui a un impact important. L'impact de cette augmentation est particulièrement notable. Nous pourrons utiliser --index pour décaler les animations afin d'obtenir un effet étalé.

Conclusion sur les utilitaires

Module splitting.js terminé:

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

export const byLetter = text =>
  [...text].map(span)

export const byWord = text =>
  text.split(' ').map(span)

Importez et utilisez ensuite ces fonctions byLetter() et byWord().

Orchestration partagée

Une fois les utilitaires de fractionnement prêts à l'emploi, voici ce que vous devez faire :

  1. Identifier les éléments à diviser
  2. Diviser ces éléments et remplacer le texte par du code HTML

Ensuite, le CSS prend le relais et anime les éléments / zones.

Rechercher des éléments

J'ai choisi d'utiliser des attributs et des valeurs pour stocker des informations sur l'animation souhaitée et la façon de diviser le texte. J'ai aimé mettre ces options déclaratives dans le code HTML. L'attribut split-by est utilisé à partir de JavaScript pour rechercher des éléments et créer des zones pour des lettres ou des mots. L'attribut letter-animation ou word-animation est utilisé à partir du CSS pour cibler les enfants d'un élément et appliquer des transformations et des animations.

Voici un exemple de code HTML illustrant les deux attributs :

<h1 split-by="letter" letter-animation="breath">animated letters</h1>
<h1 split-by="word" word-animation="trampoline">hover the words</h1>

Rechercher des éléments à partir de JavaScript

J'ai utilisé la syntaxe du sélecteur CSS pour la présence d'attributs afin de rassembler la liste des éléments dont le texte doit être divisé :

const splitTargets = document.querySelectorAll('[split-by]')

Rechercher des éléments à partir du CSS

J'ai également utilisé le sélecteur de présence d'attributs en CSS pour attribuer les mêmes styles de base à toutes les animations de lettres. Plus tard, nous utiliserons la valeur de l'attribut pour ajouter des styles plus spécifiques afin d'obtenir un effet.

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

Scinder le texte en place

Pour chacune des cibles de fractionnement que nous trouvons dans JavaScript, nous allons diviser leur texte en fonction de la valeur de l'attribut et mapper chaque chaîne sur un <span>. Nous pouvons ensuite remplacer le texte de l'élément par les cases que nous avons créées:

splitTargets.forEach(node => {
  const type = node.getAttribute('split-by')
  let nodes = null

  if (type === 'letter') {
    nodes = byLetter(node.innerText)
  }
  else if (type === 'word') {
    nodes = byWord(node.innerText)
  }

  if (nodes) {
    node.firstChild.replaceWith(...nodes)
  }
})

Conclusion de l'orchestration

index.js terminé:

import {byLetter, byWord} from './splitting.js'

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

if (motionOK) {
  const splitTargets = document.querySelectorAll('[split-by]')

  splitTargets.forEach(node => {
    const type = node.getAttribute('split-by')
    let nodes = null

    if (type === 'letter')
      nodes = byLetter(node.innerText)
    else if (type === 'word')
      nodes = byWord(node.innerText)

    if (nodes)
      node.firstChild.replaceWith(...nodes)
  })
}

Le code JavaScript peut être lu en anglais comme suit :

  1. Importez des fonctions utilitaires d'assistance.
  2. Vérifiez si le mouvement est autorisé pour cet utilisateur. Si ce n'est pas le cas, ne faites rien.
  3. Pour chaque élément à diviser.
    1. Les diviser en fonction de la façon dont ils veulent être divisés.
    2. Remplacez le texte par des éléments.

Diviser les animations et les transitions

La manipulation de document de fractionnement ci-dessus vient de débloquer une multitude d'animations et d'effets potentiels avec CSS ou JavaScript. Vous trouverez quelques liens en bas de cet article pour vous aider à trouver des idées de scission.

Il est temps de montrer ce que vous pouvez faire avec ! Je vais vous présenter quatre animations et transitions basées sur CSS. 🤓

Diviser les lettres

En tant que base pour les effets de lettres fractionnées, le CSS suivant s'est avéré utile. J'ai placé toutes les transitions et animations derrière la requête de contenu multimédia de mouvement, puis j'ai attribué à chaque nouvelle lettre enfant span une propriété d'affichage et un style pour indiquer ce qu'il faut faire avec les espaces blancs :

[letter-animation] > span {
  display: inline-block;
  white-space: break-spaces;
}

Le style des espaces blancs est important pour que les plages qui ne sont qu'un espace ne soient pas réduites par le moteur de mise en page. Passons maintenant aux choses amusantes avec l'état.

Exemple de lettres fractionnées de transition

Cet exemple utilise des transitions CSS pour l'effet de texte fractionné. Avec les transitions, nous avons besoin d'états entre lesquels le moteur peut animer. J'ai choisi trois états: pas de survol, survol dans la phrase, survol d'une lettre.

Lorsque l'utilisateur pointe sur la phrase, c'est-à-dire le conteneur, je réduis la taille de tous les enfants comme si l'utilisateur les avait éloignés. Ensuite, lorsque l'utilisateur pointe sur une lettre, je la mets en avant.

@media (--motionOK) {
  [letter-animation="hover"] {
    &:hover > span {
      transform: scale(.75);
    }

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:hover {
        transform: scale(1.25);
      }
    }
  }
}

Exemple d'animation de lettres fractionnées

Cet exemple utilise une animation @keyframe prédéfinie pour animer chaque lettre à l'infini et exploite l'index de propriété personnalisée intégrée pour créer un effet de décalage.

@media (--motionOK) {
  [letter-animation="breath"] > span {
    animation:
      breath 1200ms ease
      calc(var(--index) * 100 * 1ms)
      infinite alternate;
  }
}

@keyframes breath {
  from {
    animation-timing-function: ease-out;
  }
  to {
    transform: translateY(-5px) scale(1.25);
    text-shadow: 0 0 25px var(--glow-color);
    animation-timing-function: ease-in-out;
  }
}

Diviser en mots

Dans ces exemples, Flexbox a fonctionné comme type de conteneur, en exploitant de manière appropriée l'unité ch comme longueur d'espace appropriée.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Outils de développement Flexbox montrant l'écart entre les mots

Exemple de mots séparés par une transition

Dans cet exemple de transition, j'utilise à nouveau le survol. Comme l'effet masque initialement le contenu jusqu'au pointage, j'ai veillé à ce que l'interaction et les styles ne soient appliqués que si l'appareil pouvait pointer dessus.

@media (hover) {
  [word-animation="hover"] {
    overflow: hidden;
    overflow: clip;

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:not(:hover) {
        transform: translateY(50%);
      }
    }
  }
}

Exemple d'animation de mots divisés

Dans cet exemple d'animation, j'utilise à nouveau le CSS @keyframes pour créer une animation infinie décalée sur un paragraphe de texte standard.

[word-animation="trampoline"] > span {
  display: inline-block;
  transform: translateY(100%);
  animation:
    trampoline 3s ease
    calc(var(--index) * 150 * 1ms)
    infinite alternate;
}

@keyframes trampoline {
  0% {
    transform: translateY(100%);
    animation-timing-function: ease-out;
  }
  50% {
    transform: translateY(0);
    animation-timing-function: ease-in;
  }
}

Conclusion

Maintenant que vous savez comment j'ai fait, comment pourriez-vous faire ? 🙂

Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez un Codepen ou hébergez votre propre démonstration, tweetez-moi avec, et je l'ajouterai à la section "Remix de la communauté" ci-dessous.

Source

Plus de démonstrations et d'inspiration

Remix de la communauté