Creare animazioni di testo diviso

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

In questo post voglio condividere alcune idee su come risolvere le animazioni e le interazioni del testo diviso per il web in modo che siano minimali, accessibili e funzionino su tutti i browser. Prova la demo.

Demo

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

Panoramica

Le animazioni del testo diviso possono essere incredibili. In questo post toccheremo appena la superficie del potenziale dell'animazione, ma forniamo una base su cui costruire. L'obiettivo è animare progressivamente. Il testo deve essere leggibile per default, con l'animazione in primo piano. Gli effetti di movimento del testo divisi possono diventare stravaganti e potenzialmente fastidiosi, quindi manipoleremo il codice 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 per il movimento ridotto per CSS e JS.
  2. Preparare le utilità di testo diviso in JavaScript.
  3. Orchestra le espressioni condizionali e le utilità al caricamento della pagina.
  4. Scrivi transizioni e animazioni CSS per lettere e parole (la parte più interessante).

Ecco un'anteprima dei risultati condizionali che abbiamo pensato:

screenshot di Chrome DevTools con il riquadro Elementi aperto e ridotto impostato su "Reduce" e h1 mostrato non diviso
L'utente preferisce ridurre il movimento: il testo è leggibile / non suddiviso

Se un utente preferisce la riduzione del movimento, lasciamo solo il documento HTML e non verrà eseguita l'animazione. Se il movimento è OK, procediamo e lo dividiamo in pezzi. Ecco un'anteprima del codice HTML dopo che JavaScript ha suddiviso il testo per lettera.

screenshot di Strumenti per sviluppatori di Chrome con il riquadro Elementi aperto e il movimento ridotto impostato su "Riduci" e l'elemento h1 non è suddiviso
L'utente accetta il movimento; il testo è suddiviso in più elementi <span>

Preparazione delle condizioni di movimento

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

Preparazione del CSS condizionale

Ho utilizzato PostCSS per abilitare la sintassi delle query multimediali di livello 5, dove posso memorizzare un valore multimediale di query booleana in una variabile:

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

Preparazione dell'istruzione condizionale JS

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

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

Posso quindi verificare la presenza di motionOK e modificare il documento solo se l'utente non ha richiesto di ridurre il movimento.

if (motionOK) {
  // document split manipulations
}

Posso verificare lo stesso valore utilizzando PostCSS per attivare la sintassi @nest da Nesting Draft 1. In questo modo posso memorizzare tutta la logica dell'animazione e i relativi requisiti di stile per gli elementi principali e secondari in un'unica posizione:

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

Con la proprietà personalizzata PostCSS e un booleano JavaScript, siamo pronti per eseguire l'upgrade dell'effetto in modo condizionale. Passiamo alla sezione successiva, dove analizzerò il codice JavaScript per la trasformazione delle stringhe in elementi.

Suddivisione del testo

Le lettere, le parole, le 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. Creare funzioni di utilità JavaScript per suddividere le stringhe in elementi
  2. Orchestra l'utilizzo di queste utilità

Funzione di utilità per la suddivisione delle 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 di spread di ES6 ha contribuito a semplificare notevolmente l'operazione.

Funzione di utilità per la suddivisione delle parole

Analogamente alla suddivisione 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 base ai quali eseguire l'operazione di slicing. Ho passato uno spazio vuoto, che indica una separazione tra le parole.

Creazione di una funzione di utilità per le caselle

L'effetto richiede caselle per ogni lettera e in queste funzioni vediamo 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 viene impostata una proprietà personalizzata denominata --index 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. La caratteristica più evidente di questo impatto elevato è la stagionalità. Potremo utilizzare --index per compensare le animazioni per un aspetto sfalsato.

Conclusione: utilità

Il modulo splitting.js in fase di completamento:

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)

A questo punto, devi importare e utilizzare queste funzioni byLetter() e byWord().

Orchestrazione suddivisa

Con gli strumenti di suddivisione pronti per l'uso, mettere tutto insieme significa:

  1. Trovare gli elementi da suddividere
  2. Suddividendo e sostituendo il testo con HTML

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

Trovare elementi

Ho scelto di utilizzare attributi e valori per memorizzare le informazioni sull'animazione e su come suddividere il testo. Mi è piaciuto inserire queste opzioni declarative in 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 secondari e applicare trasformazioni e animazioni.

Ecco un esempio di codice 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 degli elementi di cui si vuole suddividere il testo:

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

Ricerca di elementi da CSS

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

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

Suddivisione del testo sul posto

Per ogni target di suddivisione che troviamo in JavaScript, suddividiamo 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 fase di 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 testo in inglese:

  1. Importa alcune funzioni di utilità di supporto.
  2. Controlla se il movimento è OK per questo utente, altrimenti non fare nulla.
  3. Per ogni elemento da suddividere.
    1. Suddividerli in base a come vogliono essere suddivisi.
    2. Sostituisci il testo con elementi.

Suddividere 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. Di seguito sono riportati alcuni link per aiutarti a trovare idee per la suddivisione.

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

Lettere separate

Come base per gli effetti delle lettere divisa, ho trovato utile il seguente CSS. Inserisco tutte le transizioni e le animazioni dietro la query sui contenuti multimediali in movimento e poi associo a ogni nuova lettera secondaria span una proprietà di visualizzazione e uno stile per le operazioni da eseguire sugli spazi bianchi:

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

Lo stile degli spazi bianchi è importante affinché gli spazi costituiti solo da uno spazio non vengano compressi dal motore di layout. E ora passiamo alle cose divertenti e divertenti.

Esempio di combinazione di lettere di transizione

Questo esempio utilizza le transizioni CSS per l'effetto di testo diviso. Con le transizioni abbiamo bisogno di stati tra cui il motore può eseguire l'animazione 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, riduco tutte le figlie come se l'utente le avesse allontanate. Poi, quando l'utente passa il mouse su una lettera, la porto in avanti.

@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 infinitamente ogni lettera e sfrutta l'indice della proprietà personalizzata in linea per creare un effetto di sfasamento.

@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

In questi esempi, Flexbox ha funzionato come tipo di contenitore, sfruttando in modo efficace l'unità ch come lunghezza ottimale per gli spazi.

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

Esempio di transizione di parole divise

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

@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 il CSS @keyframes per creare un'animazione infinita con spaziatura 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 ci ho fatto, come lo faresti?! 🙂

Diversificaamo i nostri approcci e impariamo tutti i modi per creare sul web. Crea un codepen o ospita la tua demo, twittami e lo aggiungerò alla sezione Remix della community qui sotto.

Origine

Altre demo e ispirazione

Remix della community