Skip to content

Tudo para WordPress, desenvolvimento web — e não só

✨ Animar linhas SVG e caminhos fechados com CSS puro

✨ 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: LENGTH e anime stroke-dashoffset de LENGTH para 0.
  • Para inverter a direção, troque o traço e o intervalo em stroke-dasharray e avance stroke-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 em FULL_LENGTH.
  • Configure linhas duplas através de stroke-dasharray a metade do comprimento e stroke-dashoffset com 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}
6svg 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_LENGTH pelo número real sem unidades de medida. O motor de CSS não entende px ou em dentro de stroke-dasharray.
  • O seletor svg path é para um contorno. Se o SVG tiver vários caminhos, use classes, IDs ou pseudo-classes como path:nth-of-type(3). Ferramentas de depuração: :nth Tester e nthmaster.com.
  • O nome do @keyframes deve 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}
11svg 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}
9svg 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}
11svg 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}
11svg 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}
11svg 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}
9svg 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}
11svg 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}
11svg 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}
11svg .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 @keyframes ou 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 remove alternate se não precisares de inversão. Verifica também conflitos com outras regras CSS para o mesmo elemento, por exemplo, transition na 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.