Créer des animations de texte fractionné

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

Dans cet article, je souhaite partager des idées sur la façon de résoudre les animations et les interactions de texte fractionné pour le Web qui sont minimales, accessibles et fonctionnent sur tous les navigateurs. Essayez la démonstration.

Démo

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

Présentation

Les animations de texte fractionné peuvent être incroyables. Dans cet article, nous n'aborderons que très brièvement le potentiel d'animation, 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 superposée. Les effets de mouvement de fractionnement de texte peuvent devenir extravagants et potentiellement perturbateurs. Nous ne manipulerons donc le code HTML ni n'appliquerons de 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 les variables conditionnelles de mouvement réduit pour CSS et JS.
  2. Préparez les utilitaires de fractionnement de texte en JavaScript.
  3. Orchestrez les conditions et les utilitaires lors du chargement de la page.
  4. Écrivez des transitions et des animations CSS pour les lettres et les mots (la partie cool !).

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

Capture d'écran des outils de développement Chrome avec le panneau "Éléments" ouvert et le paramètre "Réduire les animations" défini sur "Réduire", et le h1 affiché sans fractionnement
L'utilisateur préfère les animations réduites : le texte est lisible / non divisé

Si un utilisateur préfère les mouvements réduits, nous laissons le document HTML tel quel et n'ajoutons aucune animation. Si le mouvement est correct, nous le découpons en plusieurs parties. 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 et le paramètre "Réduire les animations" défini sur "Réduire", et le h1 affiché sans fractionnement
L'utilisateur accepte le mouvement. Le texte est divisé en plusieurs éléments <span>.

Préparer les conditions de mouvement

La requête média available @media (prefers-reduced-motion: reduce) 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 fractionner ou non le texte. La requête média CSS sera utilisée pour retenir les transitions et les animations, tandis que la requête média JavaScript sera utilisée pour retenir la manipulation HTML.

Préparer la condition CSS

J'ai utilisé PostCSS pour activer la syntaxe de Media Queries Level 5, où je peux stocker un booléen de requête média 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 média. J'ai utilisé la déstructuration pour extraire et renommer le résultat booléen de la vérification de la requête média :

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 en utilisant PostCSS pour activer la syntaxe @nest à partir de Nesting Draft 1. Cela me permet de stocker toute la logique concernant l'animation et ses exigences de style pour le parent et les enfants, en un seul endroit :

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

Avec la propriété personnalisée PostCSS et un booléen JavaScript, nous sommes prêts à mettre à niveau l'effet de manière conditionnelle. Nous allons passer à la section suivante, dans laquelle je vais vous expliquer le code JavaScript permettant de transformer des chaînes en éléments.

Fractionner du texte

Les lettres, les mots, les lignes, etc. ne peuvent pas être animés individuellement avec CSS ou JS. Pour obtenir cet effet, nous avons besoin de boîtes. Si nous voulons animer chaque lettre, chacune d'elles doit être un élément. Si nous voulons animer chaque mot, chacun d'eux doit être un élément.

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

Fonction utilitaire de fractionnement des lettres

Pour commencer, vous pouvez créer 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 permis d'accélérer cette tâche.

Fonction utilitaire de fractionnement 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 à partir desquels effectuer le découpage. J'ai dépassé un espace vide, indiquant une séparation entre les mots.

Fonction utilitaire de création de boîtes

L'effet nécessite des boîtes 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 important de noter qu'une propriété personnalisée appelée --index est définie avec la position du tableau. Les boîtes pour les animations de lettres sont un bon début, mais l'ajout d'un index à utiliser dans le code CSS est une petite modification qui a un impact important. L'impact le plus notable est stupéfiant. Nous pourrons utiliser --index pour décaler les animations et obtenir un effet échelonné.

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)

Ensuite, importez et utilisez ces fonctions byLetter() et byWord().

Orchestration de fractionnement

Maintenant que les utilitaires de fractionnement sont prêts à l'emploi, voici comment les combiner :

  1. Identifier les éléments à fractionner
  2. Fractionner et remplacer du texte par du code HTML

Ensuite, le CSS prend le relais et anime les éléments / boîtes.

Rechercher des éléments

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

Voici un exemple de code HTML qui illustre 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'attribut dans CSS pour donner à toutes les animations de lettres les mêmes styles de base. Nous utiliserons ensuite la valeur de l'attribut pour ajouter des styles plus spécifiques afin d'obtenir un effet.

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

Scinder du texte sur place

Pour chacune des cibles de fractionnement que nous trouvons dans JavaScript, nous fractionnons leur texte en fonction de la valeur de l'attribut et mappons chaque chaîne à un <span>. Nous pouvons ensuite remplacer le texte de l'élément par les boîtes 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 sur l'orchestration

index.js en cours :

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 pourrait se lire comme suit :

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

Fractionner des animations et des transitions

La manipulation de la division de document 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 l'inspiration.

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

Diviser les lettres

J'ai trouvé le CSS suivant utile comme base pour les effets de fractionnement des lettres. J'ai placé toutes les transitions et animations derrière la requête média de mouvement, puis j'ai attribué à chaque nouvelle lettre enfant span une propriété d'affichage et un style pour gérer les espaces blancs :

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

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

Exemple de lettres divisées avec transition

Cet exemple utilise des transitions CSS pour l'effet de texte fractionné. Avec les transitions, nous avons besoin d'états pour que le moteur puisse animer les éléments. J'ai choisi trois états : aucun survol, survol dans la phrase et 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 fais passer au premier plan.

@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 séparé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

Flexbox a fonctionné comme type de conteneur pour moi dans ces exemples, en tirant bien parti de l'unité ch comme longueur d'espace saine.

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

Exemple de mots fractionnés lors d'une transition

Dans cet exemple de transition, j'utilise à nouveau le survol. Comme l'effet masque initialement le contenu jusqu'à ce que le curseur le survole, je me suis assuré que l'interaction et les styles n'étaient appliqués que si l'appareil était capable de survoler le contenu.

@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 échelonnée sur un paragraphe de texte normal.

[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 feriez-vous ? 🙂

Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez une démo sur CodePen ou hébergez-la vous-même, tweetez-la moi et je l'ajouterai à la section "Remix de la communauté" ci-dessous.

Source

Plus de démonstrations et d'inspiration

Remix de la communauté