Cómo crear animaciones de texto dividido

Una descripción general fundamental de cómo crear animaciones de letras y palabras divididas.

En esta publicación, quiero compartir algunas ideas para resolver interacciones y animaciones de texto dividido en la Web que son mínimas, accesibles y funcionan en todos los navegadores. Prueba la demostración.

Demostración

Si prefieres ver un 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 hablaremos del potencial de animación, pero brinda una base sobre la cual desarrollar. El objetivo es animar progresivamente. El texto debe ser legible de forma predeterminada, y la animación se compila sobre la parte superior. Los efectos de movimiento de texto dividido pueden resultar extravagantes y potencialmente disruptivos, por lo que solo manipularemos HTML o aplicaremos estilos de movimiento si el usuario está de acuerdo con el movimiento.

Esta es una descripción general del flujo de trabajo y los resultados:

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

A continuación, se muestra una vista previa de los resultados condicionales que buscamos:

captura de pantalla de Herramientas para desarrolladores de Chrome con el panel Elements abierto y con movimiento reducido configurado en “reducir” y la h1 se muestra sin dividir
El usuario prefiere el movimiento reducido, ya que el texto es legible y no se divide

Si un usuario prefiere un movimiento reducido, dejaremos solo el documento HTML y no realizaremos ninguna animación. Si el movimiento está bien, lo cortamos en pedazos. A continuación, se muestra una vista previa del código HTML después de que JavaScript dividió el texto por letra.

captura de pantalla de Herramientas para desarrolladores de Chrome con el panel Elements abierto y con movimiento reducido configurado en “reducir” y la h1 se muestra sin dividir
El usuario acepta el movimiento; texto dividido en varios elementos <span>

Cómo preparar condicionales de movimiento

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

Cómo preparar el condicional de CSS

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

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

Prepara el condicional de JS

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

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

Luego, puedo probar motionOK y solo cambiar el documento 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 desde Anidar borrador 1. Esto me permite almacenar toda la lógica de la animación y sus requisitos de estilo para el elemento superior y el secundario, en un solo lugar:

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

Con la propiedad personalizada PostCSS y un valor booleano de JavaScript, estamos listos para actualizar condicionalmente el efecto. Eso nos lleva a la siguiente sección, en la que describo JavaScript para transformar cadenas en elementos.

División del texto

Las letras de texto, las palabras, las líneas, etc., no se pueden animar individualmente con CSS o JS. Para lograr el efecto, necesitamos cuadros. Si queremos animar cada letra, cada letra debe ser un elemento. Si queremos animar cada palabra, cada palabra 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 de división de letras

Un lugar divertido para comenzar es una función que toma una cadena y devuelve cada letra de un array.

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

La sintaxis extendida de ES6 realmente ayudó a que esa tarea fuera más rápida.

Función de utilidad de división de palabras

De manera similar a la división de letras, esta función toma una string y muestra cada palabra del array.

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

El método split() en las strings de JavaScript nos permite especificar en qué caracteres segmentar. Pasé un espacio vacío, que indica una división entre palabras.

Cómo crear la utilidad de las cajas

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 configura una propiedad personalizada llamada --index con la posición del array. Es excelente tener los cuadros para las animaciones de las letras, pero tener un índice para usar en CSS es una adición aparentemente pequeña con un gran impacto. Lo más notable en este gran impacto es el asombroso. Podremos usar --index como forma de desplazar animaciones para lograr una apariencia escalonada.

Conclusión de las utilidades

Completa el módulo splitting.js:

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, importa y usa estas funciones byLetter() y byWord().

Organización dividida

Con las utilidades de división listas para usar, reunir todo implica lo siguiente:

  1. Buscar qué elementos dividir
  2. Dividir y reemplazar texto por HTML

Luego, CSS se hace cargo de los elementos y los cuadros, y los anima.

Busca elementos

Elegí usar atributos y valores para almacenar información sobre la animación deseada y cómo dividir el texto. Me gustó incluir 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 de CSS para orientarse a los elementos secundarios y aplicar transformaciones y animaciones.

A continuación, te mostramos un ejemplo de HTML que demuestra 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 buscar elementos en JavaScript

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

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

Cómo buscar elementos de CSS

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

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

División del texto en el lugar

Para cada una de las segmentaciones de división que encontramos en JavaScript, dividiremos su texto según el valor del atributo y asignaremos cada cadena a una <span>. Luego, podemos reemplazar el texto del elemento con 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 orquestación

index.js para completar:

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 puede leer en el siguiente inglés:

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

Cómo dividir animaciones y transiciones

La manipulación de documentos anterior acaba de desbloquear una gran cantidad de posibles animaciones y efectos con CSS o JavaScript. Hay algunos vínculos en la parte inferior de este artículo que te ayudarán a expandir tu potencial de división.

Es momento de demostrar lo que puedes hacer con esto. compartiré 4 animaciones y transiciones basadas en CSS. 🤓

Letras divididas

Como base para los efectos de letra dividida, me pareció útil el siguiente CSS. Pongo todas las transiciones y animaciones detrás de la consulta de medios de movimiento y, luego, le asigno a cada letra secundaria span una propiedad de pantalla y un estilo para qué hacer con los espacios en blanco:

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

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

Ejemplo de letras divididas en la transición

En este ejemplo, se usan transiciones CSS para el efecto de texto dividido. Con las transiciones, necesitamos estados entre los que debe animar el motor. Elegí tres estados: no colocar el cursor sobre un elemento, colocar el cursor sobre una oración y colocar el cursor sobre una letra.

Cuando el usuario coloca el cursor sobre la oración, también conocida como el contenedor, redujo la escala de todos los elementos secundarios como si el usuario los alejara más. Luego, a medida que el usuario coloca el cursor sobre una letra, la llevo hacia adelante.

@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 propiedades personalizadas intercalado para crear un efecto escalonado.

@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 aquí en estos ejemplos, lo que aprovechó muy bien la unidad ch como una longitud de intervalo adecuada.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
Herramientas para desarrolladores de Flexbox mostrando la brecha entre las palabras

Ejemplo de transición de palabras divididas

En este ejemplo de transición, utilizo el desplazamiento de nuevo. Como el efecto oculta inicialmente el contenido hasta que colocas el cursor sobre él, me aseguré de que la interacción y los estilos solo se aplicaran si el dispositivo podía colocar el cursor sobre ellos.

@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, utilizo CSS @keyframes de nuevo 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? 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea una Codepen o aloja tu propia demostración, twittea con ella y la agregaré a la sección de remixes de la comunidad a continuación.

Fuente

Más inspiración y demostraciones

Remixes de la comunidad