Cómo crear animaciones de texto dividido

Descripción general de cómo crear animaciones con letras y palabras divididas

En esta publicación, quiero compartir mis ideas sobre cómo resolver animaciones y interacciones de texto divididas para la Web que sean minimalistas, accesibles y funcionen en todos los navegadores. Prueba la demostración.

Demo

Si prefieres ver un video, aquí tienes una versión de esta publicación en YouTube:

Descripción general

Las animaciones de texto dividido pueden ser increíbles. En esta publicación, apenas analizaremos la superficie del potencial de animación, pero proporciona una base sobre la cual desarrollar. 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 ser extravagantes y potencialmente disruptivos, por lo que solo manipularemos el 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 las variables condicionales de movimiento reducido para CSS y JS.
  2. Prepara las utilidades de texto dividido en JavaScript.
  3. Organiza las condiciones y las utilidades cuando se carga la página.
  4. Escribe transiciones y animaciones de CSS para letras y palabras (la parte genial).

Esta es una vista previa de los resultados condicionales que buscamos:

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

Si un usuario prefiere reducir el movimiento, dejamos el documento HTML intacto y no realizamos ninguna animación. Si el movimiento está bien, podemos continuar y cortarlo en pedazos. A continuación, se muestra una vista previa del código HTML después de que JavaScript haya dividido el texto por letra.

captura de pantalla de las Herramientas para desarrolladores de Chrome con el panel de Elements abierto y un movimiento reducido configurado como “reduce”, y el h1 se muestra sin dividir
El usuario está de acuerdo con el movimiento; el texto se divide en varios elementos <span>

Prepara condiciones de movimiento

La consulta de medios @media (prefers-reduced-motion: reduce) disponible de forma conveniente se usará desde CSS y JavaScript en este proyecto. Esta consulta de medios es nuestra condición principal para decidir si dividir el texto o no. La consulta de medios de CSS se usará para retener las transiciones y las 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 del nivel 5 de las consultas de medios, en el 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 forma de verificar las consultas multimedia. Usé la desestructuración para extraer y cambiar el nombre del resultado booleano de la verificación de la consulta multimedia:

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 del borrador de anidación 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. Esto nos lleva a la siguiente sección, en la que desgloso JavaScript para transformar cadenas en elementos.

Cómo dividir texto

Las letras, palabras, líneas, etc., de texto no se pueden animar de forma individual 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 una de ellas debe ser un elemento.

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

Función de utilidad de división de letras

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

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

La sintaxis desplegada de ES6 realmente ayudó a que esa tarea fuera una tarea rápida.

Función de utilidad para dividir palabras

Al igual que dividir letras, esta función toma una cadena y muestra 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 cortar. Pasé un espacio vacío, lo que indica una división entre palabras.

Cómo hacer que las cajas funcionen como utilidad

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 está configurando una propiedad personalizada llamada --index con la posición del array. Tener los cuadros 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 que es desordenado. Podremos usar --index como una forma de compensar animaciones para lograr un aspecto escalonado.

Conclusión de 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, importaremos y usaremos estas funciones byLetter() y byWord().

Organización dividida

Con las utilidades de división listas para usar, poner todo en orden significa lo siguiente:

  1. Encuentra qué elementos dividir
  2. Dividiéndolos y reemplazando el texto con HTML

Después de eso, el CSS se hará cargo y animará 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 HTML. El atributo split-by se usa desde JavaScript para buscar elementos y crear cuadros para letras o palabras. El atributo letter-animation o word-animation se usa desde CSS para orientarse a elementos secundarios y aplicar transformaciones y animaciones.

Este es un ejemplo de código 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 desde JavaScript

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

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

Cómo encontrar elementos desde CSS

También usé el selector de presencia de atributos en CSS para dar a todas las animaciones de letras los mismos estilos base. 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 */
  }
}

Cómo dividir el texto en su lugar

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

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 completado:

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

  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 quiere dividir.
    1. Dividirlos según cómo quieran dividirlos
    2. Reemplaza el texto por elementos.

Cómo dividir animaciones y transiciones

La manipulación de documentos de división anterior acaba de desbloquear una gran cantidad de animaciones y efectos potenciales con CSS o JavaScript. Hay algunos vínculos en la parte inferior de este artículo 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 impulsadas por CSS. 🤓

Dividir letras

Como base para los efectos de letras divididas, el siguiente CSS me resultó útil. Puse todas las transiciones y animaciones detrás de la consulta de medios de movimiento y, luego, le di a cada letra secundaria nueva 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 los espacios en blanco es importante para que el motor de diseño no contraiga los tramos que son solo un espacio. Ahora, vamos a lo divertido con el estado.

Ejemplo de letras divididas de transición

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 del mouse, desplazamiento del mouse en una oración y desplazamiento del mouse sobre una letra.

Cuando el usuario coloca el cursor sobre la oración, también conocida como contenedor, reduzco todos los elementos secundarios como si el usuario los hubiera alejado. Luego, cuando el usuario coloca el cursor sobre una letra, la muestro.

@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 cada letra de forma infinita y se aprovecha el índice de propiedad personalizada intercalada para crear un efecto de espaciado.

@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 tipo de contenedor para mí en estos ejemplos, lo que aprovechó bien 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 la brecha entre palabras

Ejemplo de palabras divididas de transición

En este ejemplo de transición, vuelvo a usar el cursor sobre un elemento. Como el efecto inicialmente oculta el contenido hasta que se coloca el cursor sobre un elemento, me aseguré de que la interacción y los diseños solo se aplicaran si el dispositivo tuviera la capacidad de 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, vuelvo a usar CSS @keyframes para crear una animación infinita intercalada 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 compilar en la Web. Crea una cuenta de Codepen o aloja tu propia demo, envíame un tweet 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