Cómo crear animaciones de texto dividido

Una descripción general básica de cómo crear animaciones de letras y palabras divididas.

En esta publicación, quiero compartir ideas sobre formas de resolver animaciones e interacciones de texto dividido para la Web que sean mínimas, accesibles y funcionen en todos los navegadores. Prueba la demostración.

Demostración

Si prefieres el formato de video, aquí tienes una versión de YouTube de esta publicación:

Descripción general

Las animaciones de texto dividido pueden ser increíbles. En esta publicación, apenas abordaremos el potencial de la animación, pero te proporcionaremos una base para que puedas seguir aprendiendo. El objetivo es animar de forma progresiva. El texto debe ser legible de forma predeterminada, con la animación integrada en la parte superior. Los efectos de movimiento de texto dividido pueden volverse extravagantes y potencialmente disruptivos, por lo que solo manipularemos HTML o aplicaremos estilos de movimiento si el usuario está de acuerdo con el movimiento.

A continuación, se incluye una descripción general del flujo de trabajo y los resultados:

  1. Prepara variables condicionales de movimiento reducido para CSS y JS.
  2. Prepara las utilidades de división de texto en JavaScript.
  3. Organiza las condicionales y las utilidades en la carga de la página.
  4. Escribir transiciones y animaciones de CSS para letras y palabras (la parte genial)

Aquí tienes una vista previa de los resultados condicionales que buscamos:

Captura de pantalla de las Herramientas para desarrolladores de Chrome con el panel Elementos abierto, el movimiento reducido establecido en "reducir" y el h1 sin dividir
El usuario prefiere el movimiento reducido: el texto es legible o no está dividido

Si un usuario prefiere un movimiento reducido, dejamos el documento HTML como está y no realizamos ninguna animación. Si el movimiento es correcto, seguimos adelante y lo dividimos en partes. Aquí tienes una vista previa del HTML después de que JavaScript dividió el texto por letra.

Captura de pantalla de las Herramientas para desarrolladores de Chrome con el panel Elementos abierto, el movimiento reducido establecido en "reducir" y el h1 sin dividir
El usuario acepta el movimiento; el texto se divide en varios elementos <span>

Preparando condicionales de movimiento

La consulta de medios @media (prefers-reduced-motion: reduce), que está convenientemente disponible, se usará desde CSS y JavaScript en este proyecto. Esta consulta de medios es nuestra condicional principal para decidir si dividir el texto o no. La consulta de medios de CSS se usará para retener las transiciones y animaciones, mientras que la consulta de medios de JavaScript se usará para retener la manipulación de HTML.

Prepara el condicional de CSS

Usé PostCSS para habilitar la sintaxis de las consultas de medios de nivel 5, en las que puedo almacenar un valor booleano de consulta de medios en una variable:

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

Prepara la condicional de JS

En JavaScript, el navegador proporciona una forma de verificar las consultas de medios. Usé la desestructuración para extraer y cambiar el nombre del resultado booleano de la verificación de la consulta de medios:

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

Luego, puedo probar motionOK y cambiar el documento solo si el usuario no solicitó reducir el movimiento.

if (motionOK) {
  // document split manipulations
}

Puedo verificar el mismo valor con PostCSS para habilitar la sintaxis @nest de Nesting Draft 1. Esto me permite almacenar toda la lógica sobre la animación y sus requisitos de estilo para el elemento superior y los secundarios en un solo lugar:

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

Con la propiedad personalizada de PostCSS y un booleano de JavaScript, estamos listos para actualizar el efecto de forma condicional. Eso nos lleva a la siguiente sección, en la que desgloso el código de JavaScript para transformar cadenas en elementos.

Dividiendo texto

Las letras, palabras, líneas, etc., del texto no se pueden animar de forma individual con CSS o JS. Para lograr el efecto, necesitamos cajas. Si queremos animar cada letra, cada una debe ser un elemento. Si queremos animar cada palabra, cada una debe ser un elemento.

  1. Crear funciones de utilidad de JavaScript para dividir cadenas en elementos
  2. Organizar el uso de estas utilidades

Función de utilidad para dividir letras

Un buen punto de partida es una función que toma una cadena y devuelve cada letra en un array.

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

La sintaxis de propagación de ES6 ayudó mucho a que fuera una tarea rápida.

Función de utilidad para dividir palabras

Al igual que con la división de letras, esta función toma una cadena y devuelve cada palabra en un array.

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

El método split() en las cadenas de JavaScript nos permite especificar en qué caracteres se debe realizar el corte. Pasé un espacio vacío, lo que indica una división entre palabras.

Función de utilidad para crear cuadros

El efecto requiere cuadros para cada letra, y vemos en esas funciones que se llama a map() con una función span(). Esta es la función span().

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

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

  return node
}

Es fundamental tener en cuenta que se establece una propiedad personalizada llamada --index con la posición del array. Tener las cajas para las animaciones de letras es genial, pero tener un índice para usar en CSS es una adición aparentemente pequeña con un gran impacto. Lo más notable de este gran impacto es el escalonamiento. Podremos usar --index como una forma de compensar las animaciones para lograr un aspecto escalonado.

Conclusión sobre las utilidades

El módulo splitting.js completo:

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 continuación, se importan y usan estas funciones byLetter() y byWord().

Organización de divisiones

Con las utilidades de división listas para usar, unirlas significa lo siguiente:

  1. Cómo encontrar qué elementos dividir
  2. Dividir y reemplazar texto con HTML

Después de eso, CSS toma el control y anima los elementos o cuadros.

Cómo encontrar elementos

Elegí usar atributos y valores para almacenar información sobre la animación deseada y cómo dividir el texto. Me gustó colocar estas opciones declarativas en el HTML. El atributo split-by se usa desde JavaScript para encontrar elementos y crear cuadros para letras o palabras. El atributo letter-animation o word-animation se usa desde CSS para orientar elementos secundarios y aplicar transformaciones y animaciones.

Este es un ejemplo de HTML que muestra los dos atributos:

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

Cómo encontrar elementos de JavaScript

Usé la sintaxis del selector CSS para la presencia de atributos con el objetivo de recopilar la lista de elementos que quieren que se divida su texto:

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

Cómo encontrar elementos de CSS

También usé el selector de presencia de atributos en CSS para darles a todas las animaciones de letras los mismos estilos base. Más adelante, usaremos el valor del atributo para agregar estilos más específicos y lograr un efecto.

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

Cómo dividir texto en el lugar

Para cada uno de los objetivos de división que encontremos en JavaScript, dividiremos su texto en función del valor del atributo y asignaremos cada cadena a un <span>. Luego, podemos reemplazar el texto del elemento por los cuadros que creamos:

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

Conclusión de la organización

index.js en completarse:

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

El código JavaScript se podría leer de la siguiente manera:

  1. Importa algunas funciones de utilidad auxiliares.
  2. Verifica si el movimiento es adecuado para este usuario. De lo contrario, no hagas nada.
  3. Para cada elemento que se quiera dividir.
    1. Divídelos según cómo quieran dividirse.
    2. Reemplazar el texto por elementos

Cómo dividir animaciones y transiciones

La manipulación de división de documentos anterior acaba de desbloquear una gran cantidad de animaciones y efectos potenciales con CSS o JavaScript. Al final de este artículo, encontrarás algunos vínculos que te ayudarán a inspirar tu potencial de división.

Es hora de mostrar lo que puedes hacer con esto. Compartiré 4 animaciones y transiciones basadas en CSS. 🤓

Dividir letras

Como base para los efectos de letras divididas, encontré que el siguiente CSS es útil. Coloco todas las transiciones y animaciones detrás de la consulta de medios de movimiento y, luego, le doy a cada nueva letra secundaria span una propiedad de visualización y un estilo para lo que se debe hacer con los espacios en blanco:

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

El estilo de espacios en blanco es importante para que el motor de diseño no contraiga los intervalos que solo son un espacio. Ahora, pasemos a la parte divertida con estado.

Ejemplo de transición de letras divididas

En este ejemplo, se usan transiciones de CSS para el efecto de texto dividido. Con las transiciones, necesitamos estados para que el motor anime entre ellos, y elegí tres estados: sin desplazamiento, desplazamiento en la oración y desplazamiento en una letra.

Cuando el usuario coloca el cursor sobre la oración, es decir, el contenedor, reduzco todos los elementos secundarios como si el usuario los alejara. Luego, cuando el usuario coloca el cursor sobre una letra, la pongo en primer plano.

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

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

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

Ejemplo de animación de letras divididas

En este ejemplo, se usa una animación @keyframe predefinida para animar de forma infinita cada letra y se aprovecha el índice de propiedad personalizada intercalado para crear un efecto de escalonamiento.

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

Dividir palabras

Flexbox funcionó como un tipo de contenedor para mí en estos ejemplos, ya que aprovechó la unidad ch como una longitud de espacio saludable.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Herramientas para desarrolladores de Flexbox que muestran el espacio entre las palabras

Ejemplo de palabras de división de transición

En este ejemplo de transición, vuelvo a usar el desplazamiento. Como el efecto oculta inicialmente el contenido hasta que se coloca el cursor sobre él, me aseguré de que la interacción y los estilos solo se aplicaran si el dispositivo tenía la capacidad de colocar el cursor sobre él.

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

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

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

Ejemplo de animación de palabras divididas

En este ejemplo de animación, vuelvo a usar @keyframes de CSS para crear una animación infinita escalonada en un párrafo de texto 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;
  }
}

Conclusión

Ahora que sabes cómo lo hice, ¿cómo lo harías tú? 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de crear en la Web. Crea un Codepen o aloja tu propia demostración, envíame un tweet con ella y la agregaré a la sección Remixes de la comunidad que aparece a continuación.

Fuente

Más demostraciones y ejemplos de inspiración

Remixes de la comunidad