
✨ Animar linhas SVG e caminhos fechados com CSS puro
A animação de traçado SVG é uma daquelas técnicas que transformam um ícone estático numa ilustração viva. Provavelmente já viu este efeito: o contorno de um logótipo desenha-se suavemente, como se uma caneta invisível percorresse a forma do início ao fim. Parece caro, mas faz-se apenas com CSS.
O problema é que a maioria dos tutoriais fica pelo caso mais simples: um caminho aberto, uma única linha, animação do início para o fim. Mas os gráficos reais envolvem contornos fechados, formas interrompidas e direções de desenho que o designer não controlou. Um caminho SVG pode começar em qualquer ponto, e precisamos que desenhe no sentido horário a partir de um ponto de partida esteticamente agradável.
A seguir está o conjunto completo de técnicas: da animação básica às linhas duplas e ao controlo total sobre os pontos de início e fim. Tudo em CSS puro, sem bibliotecas nem medições em tempo de execução. Estes métodos foram testados durante o desenvolvimento do plugin SVG Divider e funcionam em qualquer browser moderno.
💡 Visão geral rápida:
- Encontre o comprimento do caminho SVG através de
getTotalLength()ou do Illustrator e escreva o número no CSS. - Defina
stroke-dasharray: LENGTHe animestroke-dashoffsetdeLENGTHpara0. - Para inverter a direção, troque o traço e o intervalo em
stroke-dasharraye avancestroke-dashoffset. - Desloque o ponto de partida adicionando metade (ou qualquer fração) do comprimento a
stroke-dashoffset. - Lance a animação «de dentro para fora»: comece em
HALF_LENGTH, termine emFULL_LENGTH. - Configure linhas duplas através de
stroke-dasharraya metade do comprimento estroke-dashoffsetcom um quarto de deslocamento.
Fundamento técnico: traços, intervalos e comprimento do caminho
No centro de qualquer animação SVG estão duas propriedades CSS: stroke-dasharray e stroke-dashoffset. A primeira define um padrão «traço-intervalo» ao longo do contorno, a segunda desloca esse padrão. Se fizer o traço igual ao comprimento total do caminho e depois animar o deslocamento, a linha «desenha-se» diante dos seus olhos.
O passo fundamental antes de qualquer animação é encontrar o comprimento total do caminho. Métodos:
- JavaScript:
document.querySelector('path').getTotalLength(), uma chamada na consola, o número é colocado diretamente no CSS. Sem tempo de execução. - Illustrator: o painel «Informações do documento» mostra o comprimento do caminho selecionado.
- Script personalizado: se tiver dezenas ou centenas de caminhos, faz sentido escrever uma travessia automática e marcação de comprimentos.
Nuance importante: getTotalLength() e o Illustrator podem dar números ligeiramente diferentes para o mesmo caminho. A razão é a diferente precisão de cálculo. A discrepância costuma estar dentro de 1 a 2 unidades, não afeta o aspeto visual.
Verifique a geometria do caminho quanto a erros. A direção do desenho e o ponto de partida dependem de como o designer construiu a curva no editor, isso é normal. A seguir aprenderemos a controlar ambos sem editar o próprio SVG.
Animação básica: desenhar uma linha do início ao fim
Caso mais simples: um caminho aberto com um comprimento de, digamos, 100 unidades. Defina o traço para o comprimento total e anime o deslocamento de 100 para 0:
1 @keyframes reveal { 2 to { 3 stroke-dashoffset: 0; 4 } 5 } 6 svg path { 7 stroke-dashoffset: 100; 8 stroke-dasharray: 100; 9 animation: reveal 1s ease-in-out 0s forwards; 10 }
O que está a acontecer aqui: stroke-dasharray: 100 define 100 unidades de linha sólida e 100 unidades de espaço vazio (o segundo valor assume o valor do primeiro por defeito). stroke-dashoffset: 100 desloca o padrão de modo a que a parte visível fique além dos limites do caminho, a linha fica invisível. A animação reduz o deslocamento para zero e o traço vai surgindo no lugar.
Algumas notas práticas:
- Substitua
FULL_LENGTHpelo número real sem unidades de medida. O motor de CSS não entendepxouemdentro destroke-dasharray. - O seletor
svg pathé para um contorno. Se o SVG tiver vários caminhos, use classes, IDs ou pseudo-classes comopath:nth-of-type(3). Ferramentas de depuração::nth Testere nthmaster.com. - O nome do
@keyframesdeve ser único para cada variante de animação. Caminhos diferentes exigem frequentemente as suas próprias keyframes. - Evite valores negativos em
stroke-dashoffset: o Safari pode apresentar cintilação ou mostrar artefactos com deslocamentos negativos.
Controlar a direção: inverter o desenho
Por defeito, a animação vai do início ao fim do caminho, onde quer que o designer tenha desenhado a curva. Mas muitas vezes precisamos do oposto: um contorno fechado a desenhar no sentido horário em vez de anti-horário.
A solução é inverter o padrão do traço: a linha começa invisível (0 de traço, 100 de intervalo) e torna-se visível (100 de traço, 0 de intervalo), enquanto o deslocamento a puxa para trás:
1 @keyframes reversed { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 0; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 100; 9 } 10 } 11 svg path { 12 animation: reversed 1s ease-in-out 0s forwards; 13 }
A mecânica funciona através da natureza cíclica do padrão de traço: a linha sólida materializa-se no fim do caminho, enquanto a sua cópia vai para a esquerda além do início e permanece invisível. O mesmo acontece em contornos fechados: a cópia do traço aparece «antes» do início e não interfere com a perceção.
Há uma abordagem alternativa, mais curta e sem manipulação de arrays:
1 @keyframes alternative-reversed { 2 from { 3 stroke-dashoffset: 100; 4 } 5 to { 6 stroke-dashoffset: 200; 7 } 8 } 9 svg path { 10 stroke-dasharray: 100; 11 animation: alternative-reversed 1s ease-in-out 0s forwards; 12 }
Aqui stroke-dasharray é fixo no comprimento total, enquanto o deslocamento vai de 100 a 200. Mesmo resultado, a linha desenha-se no sentido inverso. Use a variante que lhe parecer mais intuitiva.
Alterar o ponto de partida
Um caminho fechado é como uma linha de comboio circular: o comboio pode partir de qualquer estação. A tarefa é deslocar o início da animação para um ponto esteticamente agradável sem mexer na geometria em si.
Começar do lado oposto
Para contornos simétricos, um deslocamento de metade do comprimento é suficiente:
1 @keyframes start-from-other-side { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 50; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 50; 9 } 10 } 11 svg path { 12 animation: start-from-other-side 1s ease-in-out 0s forwards; 13 }
A direção do desenho mantém-se a original, mas visualmente a animação começa na extremidade oposta.
Inverter a direção e ponto de partida arbitrário
Para inverter simultaneamente a direção e deslocar o ponto de partida, adicione o comprimento total ao deslocamento final:
1 @keyframes other-side-and-reverse { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 50; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 150; 9 } 10 } 11 svg path { 12 animation: other-side-and-reverse 1s ease-in-out 0s forwards; 13 }
Para um ponto completamente arbitrário, substitua por qualquer valor em vez de 50. Tentativa e erro (mais ou menos 5 a 10 unidades) encontram a posição ideal. Num projeto, o ponto de partida num ícone de bits do Twitch foi deslocado 17 píxeis, um número feio, mas visualmente perfeito.
Animação «de dentro para fora»
A linha cresce a partir do meio em ambas as direções simultaneamente, uma técnica clássica para logótipos e infografias:
1 @keyframes inside-out { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 50; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 100; 9 } 10 } 11 svg path { 12 animation: inside-out 1s ease-in-out 0s forwards; 13 }
O deslocamento começa em metade do comprimento (centro do caminho) e vai até ao comprimento total, puxando o traço consigo. Para formas assimétricas, o ponto de partida pode ser deslocado adicionalmente:
1 @keyframes custom-inside-out { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 84; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 134; 9 } 10 }
Aqui 84 = METADE_DO_COMPRIMENTO + 34. A constante 34 é escolhida manualmente para uma forma específica. Não há receita universal, experimente, veja, ajuste.
Animação «de fora para dentro»: caneta dupla
A abordagem oposta: a linha desenha-se a partir de ambas as extremidades simultaneamente, convergindo para o meio. Em contornos abertos, isto dá um efeito de «duas canetas»:
1 @keyframes outside-in { 2 from { 3 stroke-dasharray: 0 100; 4 } 5 to { 6 stroke-dasharray: 50 0; 7 } 8 } 9 svg path { 10 animation: outside-in 1s ease-in-out 0s forwards; 11 }
O traço atinge apenas metade do comprimento, o intervalo colapsa duas vezes mais rápido do que na animação básica. stroke-dashoffset não é necessário aqui: o padrão de «intervalo 0» no estado final faz, por si só, com que ambas as metades do traço convinjam.
Controlo total: pontos de início e fim
Quando uma direção e um ponto de partida não são suficientes, pode controlar ambos os limites do desenho. Duas cabeças de linha em movimento convergem para um destino comum:
1 @keyframes custom-start-end { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 30; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 80; 9 } 10 } 11 svg path { 12 animation: custom-start-end 1s ease-in-out 0s forwards; 13 }
Os valores 30 e 80 são arbitrários; o deslocamento from menor do que o deslocamento to é a única regra rígida. Se os pontos estiverem próximos, um segmento voa rápido, o outro rasteja lentamente, isso é normal e até bonito.
Não tenha medo de ultrapassar o comprimento total no valor to. stroke-dashoffset: 130 com comprimento 100 é válido, apenas desloca o ciclo do traço para a segunda volta. O principal é evitar valores negativos.
Linhas duplas e efeitos especiais
O modo mais flexível: traços de metade do comprimento com deslocamento de um quarto. Cada caminho recebe duas cabeças de linha que se movem independentemente:
1 @keyframes double-lines { 2 from { 3 stroke-dasharray: 0 50; 4 stroke-dashoffset: 25; 5 } 6 to { 7 stroke-dasharray: 50 0; 8 stroke-dashoffset: 50; 9 } 10 } 11 svg path { 12 animation: double-lines 1s ease-in-out 0s forwards; 13 }
Para formas complexas, o array de traços pode consistir em múltiplos componentes. A soma de todos os valores do array deve igualar o comprimento total. Exemplo real para um sinal de infinito:
1 @keyframes infinity-double-lines { 2 from { 3 stroke-dasharray: 0 53 0 47; 4 stroke-dashoffset: 37; 5 } 6 to { 7 stroke-dasharray: 53 0 47 0; 8 stroke-dashoffset: 60; 9 } 10 } 11 svg .infinity { 12 animation: infinity-double-lines 8s ease-in-out 0s alternate infinite; 13 stroke: #8D69D8; 14 }
Quatro componentes do array (53 + 47 = 100 = comprimento total) trocam de lugar entre from e to. A animação repete-se em ciclo (infinite) e inverte (alternate), criando um contorno meditativo e respiratório que nunca se repete de forma idêntica.
Vídeo: animação SVG na prática
Para ver todas as técnicas descritas ao vivo e perceber as nuances de configuração, veja este tutorial em inglês:
O autor mostra a criação passo a passo de uma ilustração SVG animada, desde a exportação a partir do Figma até aos toques finais de CSS. Os timecodes na descrição permitem saltar diretamente para a secção pretendida.
⁉️🤔 Perguntas frequentes
*Porquê saber o comprimento do traçado antecipadamente se existe getTotalLength()?*
O
getTotalLength()é JavaScript, e o nosso objetivo é CSS puro sem dependências de runtime. Mede o comprimento UMA VEZ (na consola ou no Illustrator), inseres o número no CSS e a animação funciona sem uma única linha de JS.
É possível animar vários traçados com uma só animação?
Sim, se tiverem o mesmo comprimento e o comportamento desejado for igual. Na prática, os traçados raramente coincidem; é mais simples dar a cada um o seu próprio
@keyframesou usar variáveis CSS com valores de comprimento diferentes.
Porque é que o Safari pisca com stroke-dashoffset negativo?
É um bug de renderização conhecido do WebKit: valores de offset negativos são por vezes interpretados como reinicialização instantânea para 0. A solução é manter sempre
stroke-dashoffset≥ 0; se precisares de um deslocamento, adiciona o comprimento total em vez de usares valores negativos.
O que fazer se a animação «soluçar» no início ou no fim?
Adiciona
forwardsàanimation(preserva o estado final) e removealternatese não precisares de inversão. Verifica também conflitos com outras regras CSS para o mesmo elemento, por exemplo,transitionna mesma propriedade.
Isto funciona com stroke-dasharray em contornos fechados com geometria complexa?
Sim, a mecânica é a mesma para quaisquer traçados SVG, linhas retas, curvas de Bézier, polígonos fechados. A única diferença é que num contorno fechado não há um «início» e um «fim» visuais, pelo que controlar o ponto de partida é especialmente importante para a estética.
Vale a pena escrever animação manualmente quando existem bibliotecas?
Resposta curta: depende da escala. Para um ícone isolado numa landing page, o controlo manual dá resultados perfeitos sem dependências extra. Quinze minutos a ajustar um offset e o contorno desenha-se exatamente como pretendeste, e não como o criador da biblioteca decidiu.
Quando criámos os SVG dividers para o Elementor, o controlo manual compensou três vezes mais. O plugin usa dezenas de divisores animados, cada um com traçados afinados individualmente. Nenhuma biblioteca daria tal controlo sobre a estética.
Além das técnicas deste artigo, podes sobrepor:
- Controlo do estado da animação via JavaScript (iniciar/parar/reiniciar);
- Lançamento diferido via
Intersection Observer, o contorno desenha-se apenas quando entra no viewport; - Cadeias de animação via
animation-delay; - Funções de easing personalizadas (
cubic-bezier()); - Trabalho adicional de opacidade nos
@keyframes; - Animação elástica de linhas tracejadas («marching ants»).
Usa os esquemas deste artigo como base, substitui pelos teus valores de comprimento e experimenta com cubic-bezier(). Uma função de easing bem escolhida altera a perceção da animação mais do que deslocar o ponto de partida em alguns pixels.



