Como criar animações de texto dividido

Uma visão geral básica sobre como criar animações de letras de divisão e palavras.

Nesta postagem, quero compartilhar ideias sobre maneiras de resolver animações de divisão de texto e para a Web que são mínimas, acessíveis e funcionam em vários navegadores. Confira a demonstração.

Demonstração

Se você preferir o vídeo, aqui está uma versão do YouTube desta postagem:

Visão geral

Animações de texto dividido podem ser incríveis. É quase impossível não conhecer potencial de animação nesta postagem, mas fornece uma base para criar O objetivo é fazer animações progressivamente. O texto deve ser legível padrão, com a animação criada sobre ele. Os efeitos de movimento de texto dividido podem extravagantes e potencialmente disruptivos, portanto, manipularemos apenas HTML, ou aplicar estilos de movimento se o usuário concordar com o movimento.

Confira uma visão geral do fluxo de trabalho e dos resultados:

  1. Preparar condicional de movimento reduzido variáveis para CSS e JS.
  2. Preparar utilitários de texto dividido na JavaScript.
  3. Orquestrar os condicionais e os utilitários na página carregar.
  4. Criar transições e animações CSS para letras e palavras (a parte mais radiante!).

Esta é uma prévia dos resultados condicionais que estamos buscando:

captura de tela do Chrome DevTools com o painel Elements aberto e movimento reduzido definido como "reduzir" e o h1 é mostrado inteiro
O usuário prefere movimento reduzido: o texto está legível / não dividido

Se um usuário preferir a movimentação reduzida, deixamos o documento HTML em paz e não fazemos animação. Se estiver tudo bem com o movimento, vamos cortar em pedaços. Confira visualização do HTML depois que o JavaScript dividiu o texto por letra.

captura de tela do Chrome DevTools com o painel Elements aberto e movimento reduzido definido como "reduzir" e o h1 é mostrado inteiro
O usuário está satisfeito com o movimento. texto dividido em vários <span> elementos

Preparando condicionais de movimento

A consulta de mídia @media (prefers-reduced-motion: reduce) disponível será usada no CSS e JavaScript neste projeto. Essa consulta de mídia é nossa principal condição decidir dividir o texto ou não. A consulta de mídia CSS será usada para reter dados transições e animações, enquanto a consulta de mídia JavaScript será usada para não manipulam o HTML.

Como preparar a condicional do CSS

Usei PostCSS para ativar a sintaxe das Consultas de mídia de nível 5, onde posso armazenar um booleano de consulta de mídia em uma variável:

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

Como preparar a condicional do JS

Em JavaScript, o navegador oferece uma forma de verificar consultas de mídia que eu usei desestruturação para extrair e renomear o resultado booleano da verificação de consulta de mídia:

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

Posso testar motionOK e só alterar o documento se o usuário não tiver solicitado para reduzir o movimento.

if (motionOK) {
  // document split manipulations
}

Posso verificar o mesmo valor usando PostCSS para ativar a sintaxe @nest da Aninhamento do rascunho 1. Isso me permite armazenar toda a lógica da animação e seus requisitos de estilo para o mães e filhos, em um só lugar:

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

Com a propriedade personalizada PostCSS e um booleano JavaScript, estamos prontos para atualizar condicionalmente o efeito. Isso nos leva à próxima seção, em que analisar o JavaScript para transformar strings em elementos.

Como dividir o texto

Letras de texto, palavras, linhas etc. não podem ser animadas individualmente com CSS ou JS. Para conseguir o efeito, precisamos de caixas. Se quisermos animar cada letra, cada letra precisa ser um elemento. Se quisermos animar cada palavra, então cada palavra precisa ser um elemento.

  1. Criar funções utilitárias JavaScript para dividir strings em elementos
  2. Orquestrar o uso desses utilitários
.

Função utilitária de divisão de letras

Um bom jeito de começar é com uma função que usa uma string e retorna cada letra em uma matriz.

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

A espalhada do ES6 realmente ajudou a tornar essa tarefa mais rápida.

Função utilitária de divisão de palavras

Semelhante à divisão de letras, essa função usa uma string e retorna cada palavra em uma matriz.

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

A split() em strings JavaScript nos permite especificar em quais caracteres dividir. Passei um espaço vazio, indicando uma divisão entre as palavras.

Transformando as caixas em uma função utilitária

O efeito requer caixas para cada letra, e vemos nessas funções que map() está sendo chamado com uma função span(). Esta é a função span().

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

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

  return node
}

É crucial observar que uma propriedade personalizada chamada --index está sendo definida com a posição da matriz. Ter as caixas para as animações das letras é ótimo, mas usar um índice no CSS é uma adição aparentemente pequena com grande impacto. O mais notável nesse grande impacto é surpreendente. Poderemos usar --index como uma forma de deslocar animações para um olhar

Conclusão sobre serviços públicos

O módulo splitting.js foi concluído:

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)

Agora, vamos importar e usar as funções byLetter() e byWord().

Orquestração dividida

Com os utilitários de divisão prontos para uso, juntar tudo isso significa que:

  1. Como descobrir quais elementos dividir
  2. Separando e substituindo o texto por HTML

Depois disso, o CSS assume o controle e anima os elementos / caixas.

Elementos da descoberta

Optei por usar atributos e valores para armazenar informações sobre os e como dividir o texto. Eu gostava de colocar essas opções declarativas no HTML. O atributo split-by é usado no JavaScript para localizar e criar caixas para letras ou palavras. O atributo letter-animation ou word-animation é usado no CSS para o elemento de destino filhos e aplicar transformações e animações.

Aqui está um exemplo de HTML que demonstra os dois atributos:

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

Encontrar elementos no JavaScript

Usei a sintaxe do seletor de CSS para a presença de atributos e reunir a lista de que precisam dividir o texto:

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

Como encontrar elementos no CSS

Também usei o seletor de presença de atributo no CSS para fornecer animações com letras os mesmos estilos base. Mais tarde, usaremos o valor do atributo para adicionar informações para obter um efeito.

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

Dividindo o texto no lugar

Para cada segmento de divisão encontrado em JavaScript, dividiremos o texto com base no valor do atributo e mapear cada string para um <span>. Podemos em seguida, substitua o texto do elemento pelas caixas que fizemos:

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

Conclusão da orquestração

index.js na conclusão:

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

O JavaScript pode ser lido no seguinte inglês:

  1. Importe algumas funções de utilitário auxiliares.
  2. Verifique se o usuário pode se mover corretamente ou não fazer nada.
  3. Para cada elemento que você quer dividir.
    1. Divida-os com base em como eles serão divididos.
    2. Substituir o texto por elementos.

Como dividir animações e transições

A manipulação de documentos de divisão acima desbloqueou uma infinidade de possíveis animações e efeitos com CSS ou JavaScript. Existem alguns links no final deste artigo para ajudar a inspirar seu potencial de divisão.

É hora de mostrar do que você é capaz! vou compartilhar 4 animações orientadas por CSS e transições. 🤓

Dividir letras

Como base para os efeitos de divisão de letras, descobri que o CSS a seguir é prestativos. Coloquei todas as transições e animações por trás da consulta de mídia de movimento e em seguida, atribua a cada nova letra secundária span uma propriedade de exibição e um estilo para qual a ver com espaços em branco:

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

O estilo de espaços em branco é importante para que os períodos, que são apenas um espaço, não são recolhidos pelo mecanismo de layout. Agora vamos às coisas divertidas com estado.

Exemplo de letras divididas de transição

Este exemplo usa transições CSS para o efeito de divisão de texto. Com as transições, preciso de estados para a animação do mecanismo, e escolhi três estados: não passar o cursor sobre uma frase ou sobre uma letra.

Quando o usuário passa o mouse sobre a frase, também conhecida como contêiner, eu reduzo todos os como se o usuário os empurrasse para longe. Em seguida, quando o usuário passa o mouse sobre um eu a progredir.

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

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

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

Exemplo de animação com letras divididas

Este exemplo usa uma animação @keyframe predefinida para animar infinitamente cada e aproveita o índice de propriedade personalizada inline para criar um bloco efeito

@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 palavras

O Flexbox funcionou como um tipo de contêiner para mim nestes exemplos, muito bem use a unidade ch como um comprimento de lacuna saudável.

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
DevTools do Flexbox mostrando a lacuna entre as palavras

Exemplo de transição de palavras divididas

Neste exemplo de transição, uso o cursor novamente. Como o efeito inicialmente oculta antes de passar o cursor, garantimos que a interação e os estilos só foram aplicados se o dispositivo pudesse passar o cursor.

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

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

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

Exemplo de animação de palavras divididas

Neste exemplo de animação, uso o CSS @keyframes novamente para criar um animação infinita em um parágrafo normal de texto.

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

Conclusão

Agora que você sabe como eu fiz isso, como faria?! 🙂

Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web. Crie um Codepen ou hospede sua própria demonstração, envie um tweet para mim e eu a adicionarei ao seção "Remixes da comunidade" abaixo.

Origem

Mais demonstrações e inspirações

Remixes da comunidade