Uma visão geral básica de como criar animações de letras e palavras divididas.
Nesta postagem, quero compartilhar ideias sobre como resolver animações e interações de texto dividido para a Web que sejam mínimas, acessíveis e funcionem em todos os navegadores. Teste a demonstração.
Se preferir vídeo, confira uma versão desta postagem no YouTube:
Visão geral
As animações de texto dividido podem ser incríveis. Nesta postagem, vamos apenas arranhar a superfície do potencial de animação, mas ela oferece uma base para construir. O objetivo é animar progressivamente. O texto precisa estar legível por padrão, com a animação criada em cima. Os efeitos de movimento de texto dividido podem ficar extravagantes e potencialmente disruptivos. Por isso, só vamos manipular HTML ou aplicar estilos de movimento se o usuário concordar com isso.
Confira uma visão geral do fluxo de trabalho e dos resultados:
- Prepare variáveis condicionais de movimento reduzido para CSS e JS.
- Prepare utilitários de texto dividido em JavaScript.
- Orquestre as condições e os utilitários no carregamento da página.
- Escreva transições e animações CSS para letras e palavras (a parte radical!).
Confira uma prévia dos resultados condicionais que queremos:

Se um usuário preferir movimento reduzido, deixaremos o documento HTML intacto e não faremos animação. Se o movimento estiver OK, vamos em frente e dividimos em partes. Confira uma prévia do HTML depois que o JavaScript divide o texto por letra.

Como preparar condicionais de movimento
A consulta de mídia disponível @media
(prefers-reduced-motion: reduce)
será usada em CSS e
JavaScript neste projeto. Essa consulta de mídia é nossa principal condição para decidir se o texto será dividido ou não. A consulta de mídia CSS será usada para reter transições e animações, enquanto a consulta de mídia JavaScript será usada para reter a manipulação de HTML.
Como preparar a condição do CSS
Usei o PostCSS para ativar a sintaxe de Consultas de mídia nível 5, em que posso armazenar um booleano de consulta de mídia em uma variável:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
Como preparar a condição JS
Em JavaScript, o navegador oferece uma maneira de verificar consultas de mídia. Usei a 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)'
)
Em seguida, posso testar motionOK
e mudar o documento apenas se o usuário não tiver
solicitado a redução do movimento.
if (motionOK) {
// document split manipulations
}
Posso verificar o mesmo valor usando o PostCSS para ativar a sintaxe @nest
do
Nesting Draft 1. Isso me permite
armazenar toda a lógica sobre a animação e os requisitos de estilo para o
elemento pai e os filhos em um só lugar:
letter-animation {
@media (--motionOK) {
/* animation styles */
}
}
Com a propriedade personalizada do PostCSS e um booleano JavaScript, estamos prontos para atualizar o efeito condicionalmente. Isso nos leva à próxima seção, em que detalho o JavaScript para transformar strings em elementos.
Dividir texto
Letras, palavras, linhas de texto etc. não podem ser animados individualmente com CSS ou JS. Para conseguir o efeito, precisamos de caixas. Se quisermos animar cada letra, cada uma delas precisa ser um elemento. Se quisermos animar cada palavra, cada uma delas precisa ser um elemento.
- Criar funções utilitárias do JavaScript para dividir strings em elementos
- Orquestrar o uso desses utilitários
Função utilitária para dividir letras
Um bom ponto de partida é uma função que recebe uma string e retorna cada letra em uma matriz.
export const byLetter = text =>
[...text].map(span)
A sintaxe de propagação do ES6 ajudou muito a tornar essa tarefa rápida.
Função utilitária para dividir palavras
Semelhante à divisão de letras, essa função recebe uma string e retorna cada palavra em uma matriz.
export const byWord = text =>
text.split(' ').map(span)
O método
split()
em strings JavaScript permite especificar em quais caracteres fazer o corte.
Passei um espaço vazio, indicando uma divisão entre as palavras.
Função utilitária para criar caixas
O efeito exige 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
}
É importante observar que uma propriedade personalizada chamada --index
está sendo definida com
a posição da matriz. Ter as caixas para as animações de letras é ótimo, mas
ter um índice para usar em CSS é uma adição aparentemente pequena com um grande impacto.
O mais notável nesse grande impacto é o escalonamento.
Vamos usar --index
como uma forma de compensar animações para uma aparência
escalonada.
Conclusão dos utilitários
O módulo splitting.js
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)
Em seguida, importe e use essas funções byLetter()
e byWord()
.
Orquestração de divisão
Com as utilidades de divisão prontas para uso, juntar tudo significa:
- Encontrar quais elementos dividir
- Dividir e substituir texto por HTML
Depois disso, o CSS assume o controle e anima os elementos / caixas.
Como encontrar elementos
Escolhi usar atributos e valores para armazenar informações sobre a animação desejada e como dividir o texto. Gostei de colocar essas opções declarativas
no HTML. O atributo split-by
é usado em JavaScript para encontrar elementos e criar caixas para letras ou palavras. O atributo letter-animation
ou word-animation
é usado no CSS para segmentar elementos filhos e aplicar transformações e animações.
Confira 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>
Como encontrar elementos com JavaScript
Usei a sintaxe do seletor de CSS para presença de atributo e coletei a lista de elementos que querem dividir o texto:
const splitTargets = document.querySelectorAll('[split-by]')
Como encontrar elementos do CSS
Também usei o seletor de presença de atributo em CSS para dar a todas as animações de letras os mesmos estilos básicos. Mais tarde, vamos usar o valor do atributo para adicionar estilos mais específicos e alcançar um efeito.
letter-animation {
@media (--motionOK) {
/* animation styles */
}
}
Dividir texto no lugar
Para cada um dos destinos de divisão encontrados em JavaScript, vamos dividir o texto com base no valor do atributo e mapear cada string para um <span>
. Em seguida, podemos substituir o texto do elemento pelas caixas que criamos:
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
em 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 da seguinte forma em inglês:
- Importe algumas funções utilitárias auxiliares.
- Verifique se o movimento está adequado para esse usuário. Se não estiver, não faça nada.
- Para cada elemento que você quer dividir.
- Divida-os de acordo com a preferência de cada um.
- Substituir texto por elementos.
Dividir animações e transições
A manipulação de divisão de documentos acima acabou de desbloquear uma infinidade de animações e efeitos potenciais com CSS ou JavaScript. Confira alguns links na parte de baixo deste artigo para ajudar você a dividir seu conteúdo.
É hora de mostrar o que você pode fazer com isso! Vou compartilhar quatro animações e transições baseadas em CSS. 🤓
Dividir letras
Como base para os efeitos de letras divididas, achei o seguinte CSS útil. Coloco todas as transições e animações atrás da consulta de mídia de movimento e dou a cada nova letra filha span
uma propriedade de exibição mais um estilo para o que fazer 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 intervalos que são apenas um espaço não sejam recolhidos pelo mecanismo de layout. Agora, vamos para a parte divertida com estado.
Exemplo de letras divididas em transição
Este exemplo usa transições CSS para o efeito de texto dividido. Com as transições, precisamos de estados para que o mecanismo faça a animação entre eles. Escolhi três estados: sem passar o cursor, passar o cursor em uma frase e passar o cursor em uma letra.
Quando o usuário passa o cursor sobre a frase, também conhecida como contêiner, eu reduzo todos os elementos filhos como se o usuário os tivesse afastado. Em seguida, quando o usuário passa o cursor sobre uma letra, ela é trazida para a frente.
@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 de letras divididas
Este exemplo usa uma animação @keyframe
predefinida para animar infinitamente cada
letra e aproveita o índice de propriedade personalizada inline para criar um efeito
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 palavras
O Flexbox funcionou como um tipo de contêiner para mim nesses exemplos, aproveitando bem a unidade ch
como um comprimento de lacuna adequado.
word-animation {
display: inline-flex;
flex-wrap: wrap;
gap: 1ch;
}
Exemplo de palavras divididas na transição
Neste exemplo de transição, uso o passar o cursor novamente. Como o efeito inicialmente oculta o conteúdo até passar o cursor, garanti que a interação e os estilos só fossem aplicados se o dispositivo tivesse a capacidade de 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 palavras divididas animadas
Neste exemplo de animação, uso o CSS @keyframes
novamente para criar uma animação infinita
escalonada em um parágrafo 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;
}
}
Conclusão
Agora que você sabe como eu fiz isso, como você faria? 🙂
Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web. Crie um Codepen ou hospede sua própria demonstração, me envie um tweet com ela e eu vou adicionar à seção de remixes da comunidade abaixo.
Origem
Mais demonstrações e inspiração
Remixes da comunidade
- Componente da Web
<text-hover>
de gnehcwu no CodeSandbox