Creare animazioni di testo diviso

Una panoramica di base su come creare animazioni di lettere e parole divise.

In questo post voglio condividere alcune idee su come risolvere le animazioni e le interazioni di testo diviso per il web in modo minimale, accessibile e funzionante su tutti i browser. Prova la demo.

Demo

Se preferisci i video, ecco una versione di questo post su YouTube:

Panoramica

Le animazioni di testo suddiviso possono essere straordinarie. In questo post, tratteremo solo una piccola parte del potenziale dell'animazione, ma forniremo una base su cui costruire. L'obiettivo è animare in modo progressivo. Il testo deve essere leggibile per impostazione predefinita, con l'animazione creata sopra. Gli effetti di movimento del testo diviso possono diventare stravaganti e potenzialmente di disturbo, quindi manipoleremo solo l'HTML o applicheremo stili di movimento solo se l'utente è d'accordo con il movimento.

Ecco una panoramica generale del flusso di lavoro e dei risultati:

  1. Prepara le variabili condizionali di movimento ridotto per CSS e JS.
  2. Prepara le utilità di divisione del testo in JavaScript.
  3. Orchestra le condizioni e le utilità al caricamento della pagina.
  4. Scrivi transizioni e animazioni CSS per lettere e parole (la parte più bella).

Ecco un'anteprima dei risultati condizionali che vogliamo ottenere:

Screenshot degli Strumenti per sviluppatori di Chrome con il riquadro Elementi aperto e il movimento ridotto impostato su "Riduci" e l'intestazione h1 mostrata non suddivisa
L'utente preferisce un movimento ridotto: il testo è leggibile / non diviso

Se un utente preferisce un movimento ridotto, lasciamo il documento HTML così com'è e non aggiungiamo animazioni. Se il movimento è ok, procediamo a suddividerlo in parti. Ecco un'anteprima dell'HTML dopo che JavaScript ha suddiviso il testo per lettera.

Screenshot degli Strumenti per sviluppatori di Chrome con il riquadro Elementi aperto e il movimento ridotto impostato su "Riduci" e l'intestazione h1 mostrata non suddivisa
L'utente non ha problemi con il movimento; il testo è suddiviso in più elementi <span>

Preparazione delle condizioni di movimento

La media query disponibile @media (prefers-reduced-motion: reduce) verrà utilizzata da CSS e JavaScript in questo progetto. Questa media query è la nostra condizione principale per decidere se dividere o meno il testo. La media query CSS verrà utilizzata per sospendere transizioni e animazioni, mentre la media query JavaScript verrà utilizzata per sospendere la manipolazione HTML.

Preparazione della condizione CSS

Ho utilizzato PostCSS per attivare la sintassi di Media Queries Level 5, in cui posso memorizzare un valore booleano della media query in una variabile:

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

Preparazione della condizione JS

In JavaScript, il browser fornisce un modo per controllare le media query. Ho utilizzato la destrutturazione per estrarre e rinominare il risultato booleano dal controllo delle media query:

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

Posso quindi eseguire il test per motionOK e modificare il documento solo se l'utente non ha richiesto di ridurre il movimento.

if (motionOK) {
  // document split manipulations
}

Posso controllare lo stesso valore utilizzando PostCSS per attivare la sintassi @nest di Nesting Draft 1. Ciò mi consente di memorizzare tutta la logica relativa all'animazione e ai requisiti di stile per il genitore e i figli in un unico posto:

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

Con la proprietà personalizzata PostCSS e un valore booleano JavaScript, siamo pronti ad aggiornare l'effetto in modo condizionale. Questo ci porta alla sezione successiva, in cui analizzo il codice JavaScript per trasformare le stringhe in elementi.

Dividere il testo

Lettere, parole, righe di testo e così via non possono essere animate singolarmente con CSS o JS. Per ottenere l'effetto, abbiamo bisogno di scatole. Se vogliamo animare ogni lettera, ogni lettera deve essere un elemento. Se vogliamo animare ogni parola, ogni parola deve essere un elemento.

  1. Crea funzioni di utilità JavaScript per dividere le stringhe in elementi
  2. Orchestrare l'utilizzo di queste utilità

Funzione di utilità per dividere le lettere

Un buon punto di partenza è una funzione che prende una stringa e restituisce ogni lettera in un array.

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

La sintassi spread di ES6 ha reso questa attività molto rapida.

Funzione di utilità per la divisione delle parole

Simile alla divisione delle lettere, questa funzione prende una stringa e restituisce ogni parola in un array.

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

Il metodo split() sulle stringhe JavaScript ci consente di specificare i caratteri in corrispondenza dei quali eseguire il taglio. Ho superato uno spazio vuoto, che indica una divisione tra le parole.

Funzione di utilità per la creazione di caselle

L'effetto richiede caselle per ogni lettera e vediamo in queste funzioni che map() viene chiamato con una funzione span(). Ecco la funzione span().

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

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

  return node
}

È fondamentale notare che una proprietà personalizzata chiamata --index viene impostata con la posizione dell'array. Avere le caselle per le animazioni delle lettere è fantastico, ma avere un indice da utilizzare in CSS è un'aggiunta apparentemente piccola con un grande impatto. L'impatto più notevole è sbalorditivo. Potremo utilizzare --index per compensare le animazioni e ottenere un aspetto sfalsato.

Conclusione di Utilità

Il modulo splitting.js completato:

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)

Il passaggio successivo consiste nell'importazione e nell'utilizzo di queste funzioni byLetter() e byWord().

Orchestrazione split

Con le utilità di divisione pronte all'uso, mettere tutto insieme significa:

  1. Individuare gli elementi da dividere
  2. Dividere e sostituire il testo con HTML

Dopodiché, il CSS prende il sopravvento e anima gli elementi / le caselle.

Trovare gli elementi

Ho scelto di utilizzare attributi e valori per archiviare informazioni sull'animazione desiderata e su come dividere il testo. Mi è piaciuto inserire queste opzioni dichiarative nell'HTML. L'attributo split-by viene utilizzato da JavaScript per trovare elementi e creare caselle per lettere o parole. L'attributo letter-animation o word-animation viene utilizzato da CSS per scegliere come target gli elementi figli e applicare trasformazioni e animazioni.

Ecco un esempio di HTML che mostra i due attributi:

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

Trovare elementi da JavaScript

Ho utilizzato la sintassi del selettore CSS per la presenza di attributi per raccogliere l'elenco di elementi di cui voglio dividere il testo:

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

Trovare elementi da CSS

Ho utilizzato anche il selettore di presenza degli attributi in CSS per dare a tutte le animazioni delle lettere gli stessi stili di base. In un secondo momento, utilizzeremo il valore dell'attributo per aggiungere stili più specifici per ottenere un effetto.

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

Dividere il testo sul posto

Per ciascuno dei target suddivisi trovati in JavaScript, dividiamo il testo in base al valore dell'attributo e mappiamo ogni stringa a un <span>. Possiamo quindi sostituire il testo dell'elemento con le caselle che abbiamo creato:

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)
  }
})

Conclusione dell'orchestrazione

index.js in completamento:

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)
  })
}

Il codice JavaScript potrebbe essere letto nel seguente modo in inglese:

  1. Importa alcune funzioni di utilità di supporto.
  2. Controlla se il movimento è accettabile per questo utente, in caso contrario non fare nulla.
  3. Per ogni elemento da dividere.
    1. Dividili in base a come devono essere suddivisi.
    2. Sostituisci il testo con gli elementi.

Dividere animazioni e transizioni

La manipolazione del documento di suddivisione sopra descritta ha appena sbloccato una moltitudine di potenziali animazioni ed effetti con CSS o JavaScript. Nella parte inferiore di questo articolo sono riportati alcuni link per aiutarti a trovare l'ispirazione per dividere le tue risorse.

È il momento di mostrare cosa puoi fare con questa funzionalità. Ti mostrerò 4 animazioni e transizioni basate su CSS. 🤓

Dividere le lettere

Come base per gli effetti di divisione delle lettere, ho trovato utile il seguente CSS. Ho inserito tutte le transizioni e le animazioni dietro la query multimediale di movimento e poi ho assegnato a ogni nuova lettera secondaria span una proprietà di visualizzazione e uno stile per gestire gli spazi bianchi:

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

Lo stile degli spazi bianchi è importante per evitare che gli span che contengono solo uno spazio vengano compressi dal motore di layout. Ora passiamo alle cose divertenti con stato.

Esempio di lettere divise in transizione

Questo esempio utilizza le transizioni CSS per l'effetto di divisione del testo. Con le transizioni abbiamo bisogno di stati tra cui il motore possa animare e ho scelto tre stati: nessun passaggio del mouse, passaggio del mouse nella frase, passaggio del mouse su una lettera.

Quando l'utente passa il mouse sopra la frase, ovvero il contenitore, ridimensiono tutti i figli come se l'utente li avesse allontanati. Poi, quando l'utente passa il mouse sopra una lettera, la porto in primo piano.

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

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

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

Esempio di animazione di lettere divise

Questo esempio utilizza un'animazione @keyframe predefinita per animare all'infinito ogni lettera e sfrutta l'indice della proprietà personalizzata incorporata per creare un effetto sfalsato.

@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;
  }
}

Dividi parole

Flexbox ha funzionato come tipo di contenitore in questi esempi, sfruttando in modo ottimale l'unità ch come lunghezza di spazio adeguata.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
DevTools di Flexbox che mostrano lo spazio tra le parole

Esempio di parole di transizione suddivise

In questo esempio di transizione utilizzo di nuovo il passaggio del mouse. Poiché l'effetto inizialmente nasconde il contenuto fino al passaggio del mouse, ho fatto in modo che l'interazione e gli stili venissero applicati solo se il dispositivo aveva la capacità di passare il mouse sopra.

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

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

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

Esempio di animazione di parole divise

In questo esempio di animazione utilizzo di nuovo CSS @keyframes per creare un'animazione infinita sfalsata su un normale paragrafo di testo.

[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;
  }
}

Conclusione

Ora che sai come ho fatto, come faresti tu? 🙂

Diversifichiamo i nostri approcci e impariamo tutti i modi per creare sul web. Crea un Codepen o ospita la tua demo, inviami un tweet e la aggiungerò alla sezione Remix della community di seguito.

Origine

Altre demo e fonti d'ispirazione

Remix della community