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 vários navegadores. Teste a demonstração.
Se você prefere vídeos, confira a versão deste post 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 isso fornece uma base para construção. O objetivo é animar progressivamente. O texto precisa ser legível por padrão, com a animação criada por cima. Os efeitos de movimento de texto dividido podem ser extravagantes e potencialmente perturbadores. Por isso, só vamos manipular o HTML ou aplicar estilos de movimento se o usuário estiver de acordo com o movimento.
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 divisão de texto em JavaScript.
- Orquestre as condicionais e os utilitários no carregamento da página.
- Escrever transições e animações CSS para letras e palavras (a parte legal!).
Confira uma prévia dos resultados condicionais que queremos:
Se um usuário preferir movimento reduzido, deixamos o documento HTML como está e não fazemos animações. Se o movimento estiver bom, vamos dividir em partes. Confira uma prévia do HTML depois que o JavaScript dividiu o texto por letra.
Preparando condicionais de movimento
A consulta de mídia @media
(prefers-reduced-motion: reduce), que está convenientemente disponível, será usada no CSS e no JavaScript neste projeto. Essa consulta de mídia é nossa principal condicional 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 condicional de CSS
Usei o PostCSS para ativar a sintaxe de consultas de mídia de 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);
Preparar a condicional do JS
No 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 da consulta de mídia:
const {matches:motionOK} = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Em seguida, posso testar motionOK e só mudar o documento se o usuário não tiver
solicitado a redução de movimento.
if (motionOK) {
// document split manipulations
}
Posso verificar o mesmo valor usando o PostCSS para ativar a sintaxe @nest do
Nesting Draft 1. Isso permite que eu
armazene 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 do JavaScript, estamos prontos para fazer upgrade do efeito de forma condicional. Isso nos leva à próxima seção, em que analiso o JavaScript para transformar strings em elementos.
Como dividir texto
Letras, palavras, linhas etc. de texto não podem ser animadas 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 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 de divisão de letras
Uma forma divertida de começar é com uma função que recebe uma string e retorna cada letra em uma matriz.
export const byLetter = text =>
[...text].map(span)
A sintaxe de espalhamento do ES6 realmente ajudou a tornar essa tarefa 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)
O método
split()
em strings JavaScript permite especificar quais caracteres serão fatiados.
Passei um espaço vazio, indicando uma divisão entre as palavras.
Função utilitária para criar caixas
O efeito requer caixas para cada letra, e vemos nessas funções que
map()
está sendo chamado com uma função span(). Veja 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 no 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 as animações para criar um visual
escalonado.
Conclusão sobre utilitários
O módulo splitting.js em conclusão:
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 as funções byLetter() e byWord().
Orquestração de divisão
Com os utilitários de divisão prontos para uso, para juntar tudo, você precisa:
- Encontrar quais elementos dividir
- Dividir e substituir o 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 no 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.
Veja 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 do JavaScript
Usei a sintaxe do seletor de CSS para presença de atributo para coletar a lista de elementos que querem que o texto seja dividido:
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 de base. 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 local
Para cada um dos destinos de divisão encontrados no 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 no seguinte inglês:
- Importe algumas funções utilitárias auxiliares.
- Verifique se o movimento está correto para esse usuário. Se não estiver, não faça nada.
- Para cada elemento que precisa ser dividido.
- Divida-os com base em como eles querem ser divididos.
- Substitua o texto por elementos.
Como dividir animações e transições
A manipulação de documentos de divisão acima acabou de desbloquear uma infinidade de animações e efeitos em potencial com CSS ou JavaScript. Há alguns links na parte de baixo deste artigo para ajudar a inspirar seu potencial de divisão.
Hora de mostrar o que você pode fazer com isso! Vou compartilhar quatro animações e transições orientadas por CSS. 🤓
Dividir letras
Como base para os efeitos de letras divididas, achei o CSS a seguir
útil. Coloquei todas as transições e animações por trás da consulta de mídia de movimento e,
em seguida, dei a cada nova letra filha span uma propriedade de exibição e 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 períodos 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 de transição
Este exemplo usa transições CSS para o efeito de texto dividido. Com as transições, precisamos de estados para o mecanismo animar, e escolhi três estados: sem passar o cursor, passar o cursor na frase e passar o cursor em uma letra.
Quando o usuário passa o cursor sobre a frase, ou seja, o contêiner, eu reduzo todos os filhos como se o usuário os tivesse afastado. Depois, quando o usuário passa o mouse sobre uma letra, ela é trazida para 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
de escalonamento.
@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 saudável.
word-animation {
display: inline-flex;
flex-wrap: wrap;
gap: 1ch;
}
Exemplo de palavras de transição divididas
Neste exemplo de transição, uso o hover novamente. Como o efeito inicialmente oculta o conteúdo até que o usuário passe o cursor sobre ele, garanti que a interação e os estilos só fossem aplicados se o dispositivo tivesse essa capacidade.
@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 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, 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, envie um tweet para mim com ela e eu a adicionarei à 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