Pular para o conteúdo principal

Animações de Carregamento: Setembro

· Leitura de 22 minutos

Nona das doze animações.


Animações de Carregamento (série de 13 partes)
  1. Introdução
  2. Janeiro
  3. Fevereiro
  4. Março
  5. Abril
  6. Maio
  7. Junho
  8. Julho
  9. Agosto
  10. Setembro
  11. Outubro
  12. Novembro
  13. Dezembro

Animação

Resultado final:

Introdução

Criei esta animação em SVG, seguindo uma estrutura semelhante às anteriores:

<!-- definições de tamanho --><svg>  <!-- plano de fundo -->  <rect></rect>  <!-- visualização dos caminhos -->  <path></path>  <!-- círculo que se move horizontalmente -->  <ellipse>    <!-- animações que movem o círculo -->    <animateMotion></animateMotion>    <animateMotion></animateMotion>    <!-- animações que esticam o círculo -->    <animate></animate>    <animate></animate>  </ellipse>  <!-- oito círculos que se movem verticalmente -->  <ellipse>    <!-- animação que move o círculo -->    <animateMotion></animateMotion>    <!-- animações que esticam o círculo -->    <animate></animate>    <animate></animate>    <!-- torna o círculo animado visível -->    <set></set>  </ellipse>  <!-- oito círculos temporários -->  <ellipse>    <!-- torna o círculo temporário invisível -->    <set></set>  </ellipse></svg>

Como funciona

info

Omiti alguns elementos e atributos nos blocos a seguir para manter a explicação mais concisa.

Tamanho

Mantive o mesmo tamanho usado nas animações anteriores.

Resultado



Código

<svg  xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 200 200"  width="200"  height="200">  <rect    width="199"    height="199"    x="0.5"    y="0.5"    fill="white"    stroke="lightgray"    strokeWidth="1"    rx="6"  /></svg>

Caminho

A animação utiliza nove caminhos:

  • 1 horizontal: o círculo se move de uma extremidade a outra repetidamente.
  • 8 verticais: quatro superiores e quatro inferiores, onde cada círculo se move desviando do círculo que percorre o caminho horizontal.

Depois do rect (plano de fundo), defini um path para visualizar os trajetos. O elemento path é removido na versão final da animação.

<svg>  <rect />  <path stroke="lightgray" d=""></path></svg>

Caminho horizontal

  • Distância horizontal das bordas: 1/6 da largura, 200 / 6 = 33.3333.
  • Posição vertical: 1/2 da altura: 200 / 2 = 100.

Coordenadas horizontais:

  • Esquerda: x = 0 + 33.3333 = 33.3333, y = 100, onde 0 é a borda esquerda. É uma soma, pois a coordenada fica à direita da borda.
  • Direito: x = 200 - 33.3333 = 166.6666, y = 100, onde 200 é a borda direita. É uma subtração, pois a coordenada fica à esquerda da borda.


<svg>  <rect />  <path    stroke="lightgray"    d="      M 33.3333, 100 L 166.6666, 100    "  ></path></svg>

Caminhos verticais

  • Distância horizontal das bordas: 1/3 da largura, 200 / 3 = 66.6666. Ou seja, os caminhos verticais ficam no terço do centro.
  • Espaçamento entre caminhos (gutter): 66.6666 / 3 = 22.2222.

Para as coordenadas verticais x superiores e inferiores:

  • x = 66.6666 + 0 * 22.2222 = 66.6666
  • x = 66.6666 + 1 * 22.2222 = 88.8888
  • x = 66.6666 + 2 * 22.2222 = 111.1111
  • x = 66.6666 + 3 * 22.2222 = 133.3333


  • Distância vertical: igual ao gutter.
  • Início: 1/2 gutter acima/abaixo do centro vertical.
  • Fim: 1/7 da altura acima/abaixo do centro vertical.

Para as coordenadas verticais y superiores:

  • y = (200 / 2) - (22.2222 / 2) = 88.8888, centro vertical menos 1/2 gutter.
  • y = (200 / 2) - (200 / 7) = 71.4285, centro vertical menos 1/7 da altura.

São subtrações, pois as coordenadas ficam acima do centro vertical.



<svg>  <rect />  <path    stroke="lightgray"    d="      M 33.3333,  100     L 166.6666, 100      M 66.6666,  88.8888 L 66.6666,  71.4285      M 88.8888,  88.8888 L 88.8888,  71.4285      M 111.1111, 88.8888 L 111.1111, 71.4285      M 133.3333, 88.8888 L 133.3333, 71.4285    "  ></path></svg>

Para as coordenadas verticais y inferiores:

  • y = (200 / 2) + (22.2222 / 2) = 111.1111, centro vertical mais 1/2 gutter.
  • y = (200 / 2) + (200 / 7) = 128.5714, centro vertical mais 1/7 da altura.

São somas, pois as coordenadas ficam abaixo do centro vertical.



<svg>  <rect />  <path    stroke="lightgray"    d="      M 33.3333,  100      L 166.6666, 100      M 66.6666,  88.8888  L 66.6666,  71.4285      M 88.8888,  88.8888  L 88.8888,  71.4285      M 111.1111, 88.8888  L 111.1111, 71.4285      M 133.3333, 88.8888  L 133.3333, 71.4285      M 66.6666,  111.1111 L 66.6666,  128.5714      M 88.8888,  111.1111 L 88.8888,  128.5714      M 111.1111, 111.1111 L 111.1111, 128.5714      M 133.3333, 111.1111 L 133.3333, 128.5714    "  ></path></svg>

Resultado



Código

<svg>  <rect />  <path    stroke="lightgray"    d="      M 33.3333,  100      L 166.6666, 100      M 66.6666,  88.8888  L 66.6666,  71.4285      M 88.8888,  88.8888  L 88.8888,  71.4285      M 111.1111, 88.8888  L 111.1111, 71.4285      M 133.3333, 88.8888  L 133.3333, 71.4285      M 66.6666,  111.1111 L 66.6666,  128.5714      M 88.8888,  111.1111 L 88.8888,  128.5714      M 111.1111, 111.1111 L 111.1111, 128.5714      M 133.3333, 111.1111 L 133.3333, 128.5714    "  ></path></svg>

Círculos e suas animações

info

A animação completa dura 1.5s. Nos exemplos intermediários, aumentei para 15s para destacar os movimentos.

Animando o movimento do círculo horizontal

Usei um elemento ellipse, que permite definir o raio nos eixos x e y separadamente. Cada movimento (ida e volta) é controlado por um animateMotion:

  • id: left_to_right e right_to_left
  • begin: cada um inicia quando o outro termina (loop contínuo)
    • #left_to_right, 0ms; right_to_left.end: 0ms faz com que esta seja a primeira animação a executar, e right_to_left.end faz com que animação seja executada quando right_to_left termina
    • #right_to_left, left_to_right.end: a animação é executada quando left_to_right termina
  • path: ambos usam as mesmas coordenadas, um vai da esquerda para a direita, e outro da direita para a esquerda
    • #left_to_right: M33.3333,100 L166.6666,100
    • #right_to_left: M166.6666,100 L33.3333,100

Atributos comuns:

  • dur="7500ms": 7.5s ida + 7.5s volta = 15s (duração total)
  • calcMode="spline": para poder usar curvas de Bézier no atributo keySplines
  • keyTimes, keyPoints e keySplines: dado que funcionam juntos, representei os valores dos atributos em uma tabela:
keyTimeskeyPointskeySplines
000.42, 0, 0.58, 1
110.42, 0, 0.58, 1

Lembrando como funcionam:

  • keyTimes: define em quais momentos específicos ao longo da animação os keyPoints ocorrerão, varia de 0 a 1
  • keyPoints: define onde o círculo deve estar ao longo do caminho, varia de 0 a 1
  • keySplines: define curvas de Bézier para suavizar a progressão entre keyPoints

Ou seja, ambos os animateMotions levam o círculo do início ao fim dos seus respectivos paths, usando uma curva de Bézier que começa lenta, acelera no meio, e desacelera no final.

Apenas o círculo horizontal animado



Código

<svg>  <rect />  <path></path>  <ellipse rx="8" ry="8">    <animateMotion      id="left_to_right"      path="M33.3333,100 L166.6666,100"      dur="750ms"      begin="0ms; right_to_left.end"      calcMode="spline"      keyPoints="0; 1"      keyTimes="0; 1"      keySplines="0.42, 0, 0.58, 1"    ></animateMotion>    <animateMotion      id="right_to_left"      path="M166.6666,100 L33.3333,100"      dur="750ms"      begin="left_to_right.end"      calcMode="spline"      keyPoints="0; 1"      keyTimes="0; 1"      keySplines="0.42, 0, 0.58, 1"    ></animateMotion>  </ellipse></svg>

Animando o movimento dos círculos verticais

Cada círculo precisa de dois elementos ellipse:

  • Animado, inicia invisível, fica visível ao animar (transparent -> black).
  • Estático, inicia visível, some quando a animação do animado começa (black -> transparent).

O controle de visibilidade é feita via set.

Para os círculos animados, usei oito elementos ellipse contendo um elemento animateMotion, que afasta o círculo vertical do círculo horizontal, e um element set, que torna o círculo visível.

Cada animateMotion possui os seguintes atributos:

  • id: upper_1 à upper_4 e lower_1 à lower_4
  • path: usa as coordenadas para os caminhos verticais, definidas anteriormente

Para o atributo begin, após experimentar com tempos de início, usei como desvio quartos de 1/2 da animação horizontal, iniciando a cascata. Sendo 3750 1/2 da animação horizontal:

  • 3750 * (1 / 4) = 937.5
  • 3750 * (2 / 4) = 1875
  • 3750 * (3 / 4) = 2812.5
  • 3750 * (4 / 4) = 3750

Ou seja, quando o círculo horizontal começa a se mover da ponta esquerda para a direita:

  • A animação de upper_1 e lower_1 deve começar 937.5ms depois
  • upper_2 e lower_2, 18755ms depois
  • upper_3 e lower_3, 2812.55ms depois
  • upper_4 e lower_4, 37505ms depois

E, quando o círculo horizontal começa a se mover da ponta direita para a esquerda:

  • A animação de upper_4 e lower_4 deve começar 937.5ms depois
  • upper_3 e lower_3, 18755ms depois
  • upper_2 e lower_2, 2812.55ms depois
  • upper_1 e lower_1, 37505ms depois

Os seguinte atributos são iguais para todos os animateMotions:

  • dur="3000ms": defini que dura 3 segundos, 1/5 da duração total
  • fill="freeze": mantém o círculo na posição final da animação.
  • calcMode="spline": para poder usar curvas de Bézier no atributo keySplines
  • keyTimes, keyPoints e keySplines:
keyTimeskeyPointskeySplines
000.42, 0, 0.58, 1
0.510.42, 0, 0.58, 1
10-

Ou seja, cada animateMotion leva o círculo do início ao fim e de volta ao início do seu path, usando uma curva de Bézier que começa lenta, acelera no meio, e desacelera no final.

Cada set possui os seguintes atributos:

  • begin="<id do pai>.begin": quando o valor deve ser aplicado, ou seja, assim que a animação do pai iniciar

Os seguinte atributos são iguais para todos os sets:

  • attributeName="fill": muda o atributo fill
  • to="black": o valor que será aplicado
  • fill="freeze": mantém o valor aplicado
info

Omiti os atributos que são iguais entre elementos para manter a explicação concisa.

<svg>  <rect />  <path></path>  <!-- início: círculo horizontal -->  <ellipse>    <animateMotion id="left_to_right"></animateMotion>    <animateMotion id="right_to_left"></animateMotion>  </ellipse>  <!-- fim: círculo horizontal -->  <!-- início: círculo vertical superior 1 -->  <ellipse fill="transparent">    <animateMotion      id="upper_1"      path="M66.6666,88.8888 L66.6666,71.4285"      begin="left_to_right.begin+937.5ms; right_to_left.begin+3750ms"    ></animateMotion>    <set to="black" begin="upper_1.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 1 -->  <!-- início: círculo vertical inferior 1 -->  <ellipse fill="transparent">    <animateMotion      id="lower_1"      path="M66.6666,111.1111 L66.6666,128.5714"      begin="left_to_right.begin+937.5ms; right_to_left.begin+3750ms"    ></animateMotion>    <set to="black" begin="lower_1.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 1 -->  <!-- início: círculo vertical superior 2 -->  <ellipse fill="transparent">    <animateMotion      id="upper_2"      path="M88.8888,88.8888 L88.8888,71.4285"      begin="left_to_right.begin+1875ms; right_to_left.begin+2812.5ms"    ></animateMotion>    <set to="black" begin="upper_2.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 2 -->  <!-- início: círculo vertical inferior 2 -->  <ellipse fill="transparent">    <animateMotion      id="lower_2"      path="M88.8888,111.1111 L88.8888,128.5714"      begin="left_to_right.begin+1875ms; right_to_left.begin+2812.5ms"    ></animateMotion>    <set to="black" begin="lower_2.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 2 -->  <!-- início: círculo vertical superior 3 -->  <ellipse fill="transparent">    <animateMotion      id="upper_3"      path="M111.1111,88.8888 L111.1111,71.4285"      begin="left_to_right.begin+2812.5ms; right_to_left.begin+1875ms"    ></animateMotion>    <set to="black" begin="upper_3.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 3 -->  <!-- início: círculo vertical inferior 3 -->  <ellipse fill="transparent">    <animateMotion      id="lower_4"      path="M111.1111,111.1111 L111.1111,128.5714"      begin="left_to_right.begin+2812.5ms; right_to_left.begin+1875ms"    ></animateMotion>    <set to="black" begin="lower_4.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 3 -->  <!-- início: círculo vertical superior 4 -->  <ellipse fill="transparent">    <animateMotion      id="upper_4"      path="M133.3333,88.8888 L133.3333,71.4285"      begin="left_to_right.begin+3750ms; right_to_left.begin+937.5ms"    ></animateMotion>    <set to="black" begin="upper_4.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 4 -->  <!-- início: círculo vertical inferior 4 -->  <ellipse fill="transparent">    <animateMotion      id="lower_4"      path="M133.3333,111.1111 L133.3333,128.5714"      begin="left_to_right.begin+3750ms; right_to_left.begin+937.5ms"    ></animateMotion>    <set to="black" begin="lower_4.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 4 --></svg>

Para os círculos estáticos, usei oito elementos ellipse contendo um elemento set, que possuem os seguintes atributos:

  • begin="<id do círculo animado>.begin": quando o valor deve ser aplicado, ou seja, assim que a animação do círculo animado iniciar

Os seguinte atributos são iguais para todos os sets:

  • attributeName="fill": muda o atributo fill
  • to="transparent": o valor que será aplicado
  • fill="freeze": mantém o valor aplicado
info

Omiti os atributos que são iguais entre elementos para manter a explicação concisa.

<svg>  <rect />  <path></path>  <!-- início: círculo horizontal -->  <ellipse>    <animateMotion id="left_to_right"></animateMotion>    <animateMotion id="right_to_left"></animateMotion>  </ellipse>  <!-- fim: círculo horizontal -->  <!-- início: círculo vertical superior 1 -->  <ellipse fill="transparent">    <animateMotion      id="upper_1"      path="M66.6666,88.8888 L66.6666,71.4285"      begin="left_to_right.begin+937.5ms; right_to_left.begin+3750ms"    ></animateMotion>    <set to="black" begin="upper_1.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="upper_1.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 1 -->  <!-- início: círculo vertical inferior 1 -->  <ellipse fill="transparent">    <animateMotion      id="lower_1"      path="M66.6666,111.1111 L66.6666,128.5714"      begin="left_to_right.begin+937.5ms; right_to_left.begin+3750ms"    ></animateMotion>    <set to="black" begin="lower_1.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="lower_1.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 1 -->  <!-- início: círculo vertical superior 2 -->  <ellipse fill="transparent">    <animateMotion      id="upper_2"      path="M88.8888,88.8888 L88.8888,71.4285"      begin="left_to_right.begin+1875ms; right_to_left.begin+2812.5ms"    ></animateMotion>    <set to="black" begin="upper_2.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="upper_2.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 2 -->  <!-- início: círculo vertical inferior 2 -->  <ellipse fill="transparent">    <animateMotion      id="lower_2"      path="M88.8888,111.1111 L88.8888,128.5714"      begin="left_to_right.begin+1875ms; right_to_left.begin+2812.5ms"    ></animateMotion>    <set to="black" begin="lower_2.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="lower_2.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 2 -->  <!-- início: círculo vertical superior 3 -->  <ellipse fill="transparent">    <animateMotion      id="upper_3"      path="M111.1111,88.8888 L111.1111,71.4285"      begin="left_to_right.begin+2812.5ms; right_to_left.begin+1875ms"    ></animateMotion>    <set to="black" begin="upper_3.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="upper_3.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 3 -->  <!-- início: círculo vertical inferior 3 -->  <ellipse fill="transparent">    <animateMotion      id="lower_4"      path="M111.1111,111.1111 L111.1111,128.5714"      begin="left_to_right.begin+2812.5ms; right_to_left.begin+1875ms"    ></animateMotion>    <set to="black" begin="lower_4.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="lower_4.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 3 -->  <!-- início: círculo vertical superior 4 -->  <ellipse fill="transparent">    <animateMotion      id="upper_4"      path="M133.3333,88.8888 L133.3333,71.4285"      begin="left_to_right.begin+3750ms; right_to_left.begin+937.5ms"    ></animateMotion>    <set to="black" begin="upper_4.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="upper_4.begin"></set>  </ellipse>  <!-- fim: círculo vertical superior 4 -->  <!-- início: círculo vertical inferior 4 -->  <ellipse fill="transparent">    <animateMotion      id="lower_4"      path="M133.3333,111.1111 L133.3333,128.5714"      begin="left_to_right.begin+3750ms; right_to_left.begin+937.5ms"    ></animateMotion>    <set to="black" begin="lower_4.begin"></set>  </ellipse>  <ellipse fill="black">    <set to="transparent" begin="lower_4.begin"></set>  </ellipse>  <!-- fim: círculo vertical inferior 4 --></svg>

Círculos horizontal e verticais animados



Código

<svg>  <rect></rect>  <path></path>  <!-- início: círculo horizontal -->  <ellipse>    <animateMotion id="left_to_right"></animateMotion>    <animateMotion id="right_to_left"></animateMotion>  </ellipse>  <!-- fim: círculo horizontal -->  <!-- início: círculo vertical superior 1 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_1"      path="M66.6666,88.8888 L66.6666,71.4285"      dur="3000ms"      begin="left_to_right.begin+937.5ms; right_to_left.begin+3750ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="upper_1.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="66.6666" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_1.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 1 -->  <!-- início: círculo vertical inferior 1 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_1"      path="M66.6666,111.1111 L66.6666,128.5714"      dur="3000ms"      begin="left_to_right.begin+937.5ms; right_to_left.begin+3750ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="lower_1.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="66.6666" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_1.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 1 -->  <!-- início: círculo vertical superior 2 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_2"      path="M88.8888,88.8888 L88.8888,71.4285"      dur="3000ms"      begin="left_to_right.begin+1875ms; right_to_left.begin+2812.5ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="upper_2.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="88.8888" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_2.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 2 -->  <!-- início: círculo vertical inferior 2 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_2"      path="M88.8888,111.1111 L88.8888,128.5714"      dur="3000ms"      begin="left_to_right.begin+1875ms; right_to_left.begin+2812.5ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="lower_2.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="88.8888" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_2.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 2 -->  <!-- início: círculo vertical superior 3 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_3"      path="M111.1111,88.8888 L111.1111,71.4285"      dur="3000ms"      begin="left_to_right.begin+2812.5ms; right_to_left.begin+1875ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="upper_3.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="111.1111" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_3.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 3 -->  <!-- início: círculo vertical inferior 3 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_3"      path="M111.1111,111.1111 L111.1111,128.5714"      dur="3000ms"      begin="left_to_right.begin+2812.5ms; right_to_left.begin+1875ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="lower_3.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="111.1111" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_3.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 3 -->  <!-- início: círculo vertical superior 4 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_4"      path="M133.3333,88.8888 L133.3333,71.4285"      dur="3000ms"      begin="left_to_right.begin+3750ms; right_to_left.begin+937.5ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="upper_4.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="133.3333" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_4.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 4 -->  <!-- início: círculo vertical inferior 4 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_4"      path="M133.3333,111.1111 L133.3333,128.5714"      dur="3000ms"      begin="left_to_right.begin+3750ms; right_to_left.begin+937.5ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <set      attributeName="fill"      to="black"      begin="lower_4.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="133.3333" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_4.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 4 --></svg>

Animando rx e ry dos círculos horizontal e verticais

Para transmitir velocidade, adicionei animate em rx e ry, esticando e comprimindo os círculos. Os elementos animate possuem os seguintes atributos:

  • dur: a duração é a mesma da respectiva animação de movimento
    • Círculo horizontal: 7500ms
    • Círculos verticais: 3000ms
  • begin:
    • Círculo horizontal: left_to_right.begin; right_to_left.begin, quando uma das animações de movimento se inicia
    • Círculos verticais: upper_1.begin à upper_4.begin e lower_1.begin à lower_4.begin, quando suas respectivas animações de movimento se iniciam

Os seguinte atributos são iguais para todos os animate:

  • calcMode="spline": para poder usar curvas de Bézier no atributo keySplines
  • attributeName, values, keyTimes ekeySplines são representados em conjunto na tabela:

Para o atributo values:

keyTimesattributeName="rx"attributeName="ry"keySplines
0880.42, 0, 0.58, 1
0.58.47.60.42, 0, 0.58, 1
188-

Ou seja, o círculo começa redondo, estica verticalmente e encolhe horizontalmente, e volta a ser redondo. A transição entre cada valor de rx e ry usa uma curva de Bézier que começa lenta, acelera no meio, e desacelera no final. No círculo horizontal, a animação tem o efeito de aceleração. Nos círculos verticais, tem efeito de parada repentina para voltar ao ponto de origem.

Circulos horizontal e verticais animados



Código

info

Omiti os atributos que são iguais entre elementos para manter a explicação concisa.

<svg>  <rect></rect>  <path></path>  <!-- início: círculo horizontal -->  <ellipse>    <animateMotion id="left_to_right"></animateMotion>    <animateMotion id="right_to_left"></animateMotion>    <animate      attributeName="rx"      dur="7500ms"      begin="left_to_right.begin; right_to_left.begin"    ></animate>    <animate      attributeName="ry"      dur="7500ms"      begin="left_to_right.begin; right_to_left.begin"    ></animate>  </ellipse>  <!-- fim: círculo horizontal -->  <!-- início: círculo vertical superior 1 -->  <ellipse>    <animateMotion id="upper_1"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="upper_1.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="upper_1.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical superior 1 -->  <!-- início: círculo vertical inferior 1 -->  <ellipse>    <animateMotion id="lower_1"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="lower_1.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="lower_1.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical inferior 1 -->  <!-- início: círculo vertical superior 2 -->  <ellipse>    <animateMotion id="upper_2"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="upper_2.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="upper_2.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical superior 2 -->  <!-- início: círculo vertical inferior 2 -->  <ellipse>    <animateMotion id="lower_2"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="lower_2.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="lower_2.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical inferior 2 -->  <!-- início: círculo vertical superior 3 -->  <ellipse>    <animateMotion id="upper_3"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="upper_3.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="upper_3.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical superior 3 -->  <!-- início: círculo vertical inferior 3 -->  <ellipse>    <animateMotion id="lower_3"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="lower_3.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="lower_3.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical inferior 3 -->  <!-- início: círculo vertical superior 4 -->  <ellipse>    <animateMotion id="upper_4"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="upper_4.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="upper_4.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical superior 4 -->  <!-- início: círculo vertical inferior 4 -->  <ellipse>    <animateMotion id="lower_4"></animateMotion>    <animate attributeName="rx" dur="3000ms" begin="lower_4.begin"></animate>    <animate attributeName="ry" dur="3000ms" begin="lower_4.begin"></animate>    <set></set>  </ellipse>  <ellipse>    <set></set>  </ellipse>  <!-- fim: círculo vertical inferior 4 --></svg>

Conclusão

Esta animação foi um exercício para combinar diferentes técnicas do SVG: definir trajetórias com path, sincronizar movimentos com animateMotion, controlar visibilidade com set, e dar mais vida aos elementos ao animar rx e ry. O resultado é uma composição simples, mas que transmite dinamismo, interação e ritmo apenas com recursos nativos do SVG, sem precisar de CSS ou JavaScript. Com isso, concluí a nona de doze animações desta série.

Resultado



Código

<svg  xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 200 200"  width="200"  height="200">  <rect    width="199"    height="199"    rx="6"    x="0.5"    y="0.5"    fill="white"    stroke-width="1"    stroke="lightgray"  ></rect>  <!-- início: círculo horizontal -->  <ellipse rx="8" ry="8">    <animateMotion      id="left_to_right"      path="M33.3333,100 L166.6666,100"      dur="750ms"      begin="0ms; right_to_left.end"      calcMode="spline"      keyPoints="0; 1"      keyTimes="0; 1"      keySplines="0.42, 0, 0.58, 1"    ></animateMotion>    <animateMotion      id="right_to_left"      path="M166.6666,100 L33.3333,100"      dur="750ms"      begin="left_to_right.end"      calcMode="spline"      keyPoints="0; 1"      keyTimes="0; 1"      keySplines="0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="750ms"      begin="left_to_right.begin; right_to_left.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="750ms"      begin="left_to_right.begin; right_to_left.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>  </ellipse>  <!-- fim: círculo horizontal -->  <!-- início: círculo vertical superior 1 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_1"      path="M66.6666,88.8888 L66.6666,71.4285"      dur="300ms"      begin="left_to_right.begin+93.75ms; right_to_left.begin+375ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="upper_1.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="upper_1.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="upper_1.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="66.6666" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_1.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 1 -->  <!-- início: círculo vertical inferior 1 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_1"      path="M66.6666,111.1111 L66.6666,128.5714"      dur="300ms"      begin="left_to_right.begin+93.75ms; right_to_left.begin+375ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="lower_1.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="lower_1.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="lower_1.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="66.6666" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_1.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 1 -->  <!-- início: círculo vertical superior 2 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_2"      path="M88.8888,88.8888 L88.8888,71.4285"      dur="300ms"      begin="left_to_right.begin+187.5ms; right_to_left.begin+281.25ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="upper_2.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="upper_2.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="upper_2.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="88.8888" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_2.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 2 -->  <!-- início: círculo vertical inferior 2 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_2"      path="M88.8888,111.1111 L88.8888,128.5714"      dur="300ms"      begin="left_to_right.begin+187.5ms; right_to_left.begin+281.25ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="lower_2.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="lower_2.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="lower_2.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="88.8888" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_2.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 2 -->  <!-- início: círculo vertical superior 3 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_3"      path="M111.1111,88.8888 L111.1111,71.4285"      dur="300ms"      begin="left_to_right.begin+281.25ms; right_to_left.begin+187.5ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="upper_3.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="upper_3.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="upper_3.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="111.1111" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_3.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 3 -->  <!-- início: círculo vertical inferior 3 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_3"      path="M111.1111,111.1111 L111.1111,128.5714"      dur="300ms"      begin="left_to_right.begin+281.25ms; right_to_left.begin+187.5ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="lower_3.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="lower_3.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="lower_3.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="111.1111" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_3.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 3 -->  <!-- início: círculo vertical superior 4 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="upper_4"      path="M133.3333,88.8888 L133.3333,71.4285"      dur="300ms"      begin="left_to_right.begin+375ms; right_to_left.begin+93.75ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="upper_4.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="upper_4.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="upper_4.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="133.3333" cy="88.8888">    <set      attributeName="fill"      to="transparent"      begin="upper_4.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical superior 4 -->  <!-- início: círculo vertical inferior 4 -->  <ellipse fill="transparent" rx="8" ry="8">    <animateMotion      id="lower_4"      path="M133.3333,111.1111 L133.3333,128.5714"      dur="300ms"      begin="left_to_right.begin+375ms; right_to_left.begin+93.75ms"      fill="freeze"      calcMode="spline"      keyPoints="0; 1; 0"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animateMotion>    <animate      attributeName="rx"      dur="300ms"      begin="lower_4.begin"      calcMode="spline"      values="8; 8.4; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <animate      attributeName="ry"      dur="300ms"      begin="lower_4.begin"      calcMode="spline"      values="8; 7.6; 8"      keyTimes="0; 0.5; 1"      keySplines="0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1"    ></animate>    <set      attributeName="fill"      to="black"      begin="lower_4.begin"      fill="freeze"    ></set>  </ellipse>  <ellipse fill="black" rx="8" ry="8" cx="133.3333" cy="111.1111">    <set      attributeName="fill"      to="transparent"      begin="lower_4.begin"      fill="freeze"    ></set>  </ellipse>  <!-- fim: círculo vertical inferior 4 --></svg>

Leitura recomendada