Pular para o conteúdo principal

Animações de Carregamento: Junho

· Leitura de 20 minutos

Sexta 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 essa animação com SVG, seguindo uma estrutura parecida com as anteriores:

<!-- definições de tamanho --><svg>  <defs>    <!-- caminhos que os círculos percorrem -->    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />  </defs>  <!-- plano de fundo -->  <rect />  <!-- desenhos dos caminhos que os círculos percorrem, para debug -->  <g>    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />  </g>  <!-- primeiro círculo -->  <ellipse>    <!-- animações que fazem o círculo percorrer os caminhos -->    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>  </ellipse>  <!-- segundo círculo -->  <ellipse>    <!-- animações que fazem o círculo percorrer os caminhos -->    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>  </ellipse>  <!-- mais quatro círculos como esse, usados na animação -->  <ellipse>    <!-- animações que fazem o círculo percorrer os caminhos -->    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <!-- começa escondido, aguarda sua animação começar para ficar visível -->    <set />  </ellipse>  <!-- mais quatro círculos como esse, usados como placeholder -->  <ellipse>    <!-- começa visível, aguarda a animação do círculo de animação começar para ficar escondido -->    <set />  </ellipse></svg>

Como funciona

info

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

Tamanho

Mantive o tamanho igual às 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

Os caminhos percorridos têm a forma de semicírculo, por onde seis círculos se movimentam. O primeiro círculo, que começa no primeiro ponto, se movimenta até chegar ao último ponto, e então faz o caminho de volta. Os movimentos seguem no sentido horário.

Primeiro, calculei a posição de cada círculo usando JavaScript.

Resultado



Código

// dimensõesconst width = 200;const height = 200;const xCenter = width / 2;const yCenter = height / 2;// pontos// - 1 antes do primeiro ponto// - 5 entre os pontos// - 1 depois do último pontoconst columns = 7;const spacing = width / columns;const points = [];for (let i = 0; i < 6; i++) {  const x = (i + 1) * spacing;  const y = yCenter;  points.push({ x, y });}/*[  { "x": 28.571428571428573, "y": 100 },  { "x": 57.142857142857146, "y": 100 },  { "x": 85.71428571428572,  "y": 100 },  { "x": 114.28571428571429, "y": 100 },  { "x": 142.85714285714286, "y": 100 },  { "x": 171.42857142857144, "y": 100 }]*/

Em seguida, calculei os semicírculos: os superiores são desenhados da esquerda para a direita, enquanto os inferiores, da direita para a esquerda. Isso facilita a animação, pois os círculos se movem no sentido horário.

O atributo x-axis-rotation tem valor 0, pois não há rotação do eixo x. Por se tratar de semicírculos, o atributo large-arc-flag não tem efeito, deixei com o valor 0. O atributo sweep-flag tem valor 1, para que o arco siga no sentido horário.

Resultado



Código

// dimensõesconst width = 200;const height = 200;const xCenter = width / 2;const yCenter = height / 2;// pontos// - 1 antes do primeiro ponto// - 5 entre os pontos// - 1 depois do último pontoconst columns = 7;const spacing = width / columns;const points = [];for (let i = 0; i < 6; i++) {  const x = (i + 1) * spacing;  const y = yCenter;  points.push({ x, y });}/*[  { "x": 28.571428571428573, "y": 100 },  { "x": 57.142857142857146, "y": 100 },  { "x": 85.71428571428572,  "y": 100 },  { "x": 114.28571428571429, "y": 100 },  { "x": 142.85714285714286, "y": 100 },  { "x": 171.42857142857144, "y": 100 }]*/// arcosconst radius = spacing / 2;const topArcs = [];for (let i = 0; i < points.length - 1; i++) {  const point = points[i];  const nextPoint = points[i + 1];  topArcs.push(    `M ` + // comando `move`      `${point.x}, ${point.y} ` + // x, y      `A ` + // comando `arc`      `${radius}, ${radius} ` + // rx, ry      `0 ` + // x-axis-rotation      `0 ` + // large-arc-flag      `1 ` + // sweep-flag      `${nextPoint.x}, ${nextPoint.y} ` // x, y  );}/*[  "M 0,                  100 A 14.285714285714286, 14.285714285714286 0 0 1 28.571428571428573, 100 ",  "M 28.571428571428573, 100 A 14.285714285714286, 14.285714285714286 0 0 1 57.142857142857146, 100 ",  "M 57.142857142857146, 100 A 14.285714285714286, 14.285714285714286 0 0 1 85.71428571428572,  100 ",  "M 85.71428571428572,  100 A 14.285714285714286, 14.285714285714286 0 0 1 114.28571428571429, 100 ",  "M 114.28571428571429, 100 A 14.285714285714286, 14.285714285714286 0 0 1 142.85714285714286, 100 ",  "M 142.85714285714286, 100 A 14.285714285714286, 14.285714285714286 0 0 1 171.42857142857144, 100 "]*/const bottomArcs = [];for (let i = points.length - 1; i >= 1; i--) {  const point = points[i];  const previousPoint = points[i - 1];  bottomArcs.push(    `M ` + // comando `move`      `${point.x}, ${point.y} ` + // x, y      `A ` + // comando `arc`      `${radius}, ${radius} ` + // rx, ry      `0 ` + // x-axis-rotation      `0 ` + // large-arc-flag      `1 ` + // sweep-flag      `${previousPoint.x}, ${previousPoint.y} ` // x, y  );}/*[  "M 171.42857142857144, 100 A 14.285714285714286, 14.285714285714286 0 0 1 142.85714285714286, 100 ",  "M 142.85714285714286, 100 A 14.285714285714286, 14.285714285714286 0 0 1 114.28571428571429, 100 ",  "M 114.28571428571429, 100 A 14.285714285714286, 14.285714285714286 0 0 1 85.71428571428572,  100 ",  "M 85.71428571428572,  100 A 14.285714285714286, 14.285714285714286 0 0 1 57.142857142857146, 100 ",  "M 57.142857142857146, 100 A 14.285714285714286, 14.285714285714286 0 0 1 28.571428571428573, 100 ",  "M 28.571428571428573, 100 A 14.285714285714286, 14.285714285714286 0 0 1 0,                  100 "]*/

Com os arcos calculados, defini dentro de defs os paths que os círculos percorrem. Eles serão referenciados pelos seus ids.

Depois do rect, que é o plano de fundo, defini dez uses para visualizar os caminhos, visto que o que é definido dentro de defs não é renderizado. Os elementos use são removidos na conclusão da animação.

Resultado



Código

<svg>  <defs>    <path      id="arc-top-0"      d="M 28.571428571428573, 100 A 14.285714285714286, 14.285714285714286 0 0 1 57.142857142857146, 100 "    />    <path      id="arc-top-1"      d="M 57.142857142857146, 100 A 14.285714285714286, 14.285714285714286 0 0 1 85.71428571428572, 100 "    />    <path      id="arc-top-2"      d="M 85.71428571428572, 100 A 14.285714285714286, 14.285714285714286 0 0 1 114.28571428571429, 100 "    />    <path      id="arc-top-3"      d="M 114.28571428571429, 100 A 14.285714285714286, 14.285714285714286 0 0 1 142.85714285714286, 100 "    />    <path      id="arc-top-4"      d="M 142.85714285714286, 100 A 14.285714285714286, 14.285714285714286 0 0 1 171.42857142857144, 100 "    />    <path      id="arc-bottom-0"      d="M 57.142857142857146, 100 A 14.285714285714286, 14.285714285714286 0 0 1 28.571428571428573, 100 "    />    <path      id="arc-bottom-1"      d="M 85.71428571428572, 100 A 14.285714285714286, 14.285714285714286 0 0 1 57.142857142857146, 100 "    />    <path      id="arc-bottom-2"      d="M 114.28571428571429, 100 A 14.285714285714286, 14.285714285714286 0 0 1 85.71428571428572, 100 "    />    <path      id="arc-bottom-3"      d="M 142.85714285714286, 100 A 14.285714285714286, 14.285714285714286 0 0 1 114.28571428571429, 100 "    />    <path      id="arc-bottom-4"      d="M 171.42857142857144, 100 A 14.285714285714286, 14.285714285714286 0 0 1 142.85714285714286, 100 "    />  </defs>  <rect />  <g fill="none" stroke-width="1" stroke="lightgray">    <use href="#arc-top-0" />    <use href="#arc-top-1" />    <use href="#arc-top-2" />    <use href="#arc-top-3" />    <use href="#arc-top-4" />    <use href="#arc-bottom-0" />    <use href="#arc-bottom-1" />    <use href="#arc-bottom-2" />    <use href="#arc-bottom-3" />    <use href="#arc-bottom-4" />  </g></svg>

Círculos e suas animações

info

A duração final da animação é de 0.5 segundos. Nesta seção as animações têm duração de 2 segundos para ficarem mais evidentes.

Animando o primeiro círculo

Começando com o primeiro círculo, usei um elemento ellipse.

Defini dez elementos animateMotion, cinco para os arcos superiores e cinco para os inferiores, cada um contendo um elemento mpath que referencia os paths definidos anteriormente.

Cada animateMotion possui os seguintes atributos:

  • id: vai de circle_0_arc_top_0 a circle_0_arc_top_4, e circle_0_arc_bottom_4 a circle_0_arc_bottom_0
  • begin: cada animação começa quando a anterior termina, repetindo indefinidamente
    • #circle_0_arc_top_0, 0; circle_0_arc_bottom_0.end: 0 faz com que esta seja a primeira animação a executar, começando o movimento para a direita, e circle_0_arc_bottom_0.end faz com que animação seja executada quando a animação do arco oposto termina
    • #circle_0_arc_top_1 a #circle_0_arc_top_4: começam suas animações quando a animação anterior termina, fazendo o círculo chegar ao extremo direito
    • #circle_0_arc_bottom_4, circle_0_arc_top_4.end: começa o movimento para a esquerda quando a animação do arco oposto termina
    • #circle_0_arc_bottom_3 a #circle_0_arc_bottom_0: começam suas animações quando a animação anterior termina, fazendo o círculo chegar ao extremo esquerdo

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

  • dur="2000ms": a animação dura 2 segundos nos exemplos
  • 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: dado que funcionam juntos, representei os valores dos atributos em uma tabela:
keyTimeskeyPointskeySplines
000.25, 0.1, 0.25, 1
11-

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, o círculo começa no ponto 0, vai até o ponto 1 e, ao longo do caminho, segue uma curva de Bézier definida por keySplines.

Apenas o primeiro círculo animado



Código

<svg>  <defs>    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />  </defs>  <rect />  <g>    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />  </g>  <!-- começo: círculo 0 -->  <ellipse rx="8" ry="8" fill="black">    <animateMotion      id="circle_0_arc_top_0"      begin="0; circle_0_arc_bottom_0.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-0" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_1"      begin="circle_0_arc_top_0.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-1" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_2"      begin="circle_0_arc_top_1.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-2" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_3"      begin="circle_0_arc_top_2.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-3" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_4"      begin="circle_0_arc_top_3.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-4" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_4"      begin="circle_0_arc_top_4.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-4" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_3"      begin="circle_0_arc_bottom_4.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-3" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_2"      begin="circle_0_arc_bottom_3.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-2" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_1"      begin="circle_0_arc_bottom_2.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-1" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_0"      begin="circle_0_arc_bottom_1.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-0" />    </animateMotion>  </ellipse>  <!-- fim: círculo 0 --></svg>

Animando o segundo círculo

Assim como com o primeiro círculo, usei um elemento ellipse, desta vez contendo dois elementos animateMotion. Cada animateMotion contém um elemento mpath.

Cada animateMotion possui os seguintes atributos:

  • id: assim como no primeiro círculo, porém com circle_1_... em vez de circle_0_...
  • begin:
    • #circle_1_arc_bottom_0, circle_0_arc_top_0.begin: inicia quando o primeiro círculo passa pelo arco superior
    • #circle_1_arc_top_0, circle_0_arc_bottom_0.begin: inicia quando o primeiro círculo passa pelo arco inferior
  • Demais atributos: iguais aos do primeiro círculo

Primeiro par de círculos animado



Código

<svg>  <defs>    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />  </defs>  <rect />  <g>    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />  </g>  <!-- começo: círculo 0 -->  <ellipse>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>  </ellipse>  <!-- fim: círculo 0 -->  <!-- começo: círculo 1 -->  <ellipse rx="8" ry="8" fill="black">    <animateMotion      id="circle_1_arc_bottom_0"      begin="circle_0_arc_top_0.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-0" />    </animateMotion>    <animateMotion      id="circle_1_arc_top_0"      begin="circle_0_arc_bottom_0.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-0" />    </animateMotion>  </ellipse>  <!-- fim: círculo 1 --></svg>

Animando demais círculos

Para animar os demais círculos, foram necessários dois elementos ellipse por círculo:

  • Um que possui a animação de fato, que inicia oculto e passa a ser exibido quando sua animação inicia
  • Outro que é exibido apenas no início e é oculto quando a animação do círculo de animação se inicia

Usei esta abordagem para facilitar o posicionamento e animação, visto que, ao definir uma posição x,y de um elemento, a origem do elemento animateMotion muda para este ponto (o ponto 0,0 se torna x,y, não mais o topo esquerdo do svg).

Para o elemento ellipse que começa visível e se torna invisível, começa com fill black e, usando um elemento set, muda para transparent quando a animação do círculo de animação se inicia.

Para o elemento ellipse que começa invisível e se torna visível, começa com fill transparent e muda para black usando um elemento set. Começa invisível pois assume a posição 0,0 enquanto a animação não inicia.

Nos elementos animateMotion, defini os atributos da seguinte forma:

  • id: assim como nos círculos anteriores, porém com circle_2_... até circle_5_...
  • begin: cada animação é iniciada quando o primeiro círculo passa pelo arco oposto
  • Demais atributos: iguais aos do primeiro círculo

Definindo o círculo 2 como exemplo, destacando os atributos mais importantes dos elementos ellipse, animateMotion e set:

<!-- começo: círculo 2 --><ellipse  rx="8"  ry="8"  fill="transparent">  <animateMotion    id="circle_2_arc_bottom_1"    begin="circle_0_arc_top_1.begin"    dur="500ms"    keyTimes="0; 1"    keyPoints="0; 1"    calcMode="spline"    keySplines="0.25, 0.1, 0.25, 1"    fill="freeze"  >    <mpath href="#arc-bottom-1" />  </animateMotion>  <animateMotion    id="circle_2_arc_top_1"    begin="circle_0_arc_bottom_1.begin"    dur="500ms"    keyTimes="0; 1"    keyPoints="0; 1"    calcMode="spline"    keySplines="0.25, 0.1, 0.25, 1"    fill="freeze"  >    <mpath href="#arc-top-1" />  </animateMotion>  <set    attributeName="fill"    to="black"    begin="circle_2_arc_bottom_1.begin"    fill="freeze"  /></ellipse><ellipse  rx="8"  ry="8"  cx="85.71428571428572"  cy="100"  fill="black">  <set    attributeName="fill"    to="transparent"    begin="circle_2_arc_bottom_1.begin"    fill="freeze"  /></ellipse><!-- fim: círculo 2 -->

Demais círculos animados



Código

<svg>  <defs>    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />    <path />  </defs>  <rect />  <g>    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />    <use />  </g>  <!-- começo: círculo 0 -->  <ellipse>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>  </ellipse>  <!-- fim: círculo 0 -->  <!-- começo: círculo 1 -->  <ellipse>    <animateMotion>      <mpath />    </animateMotion>    <animateMotion>      <mpath />    </animateMotion>  </ellipse>  <!-- fim: círculo 1 -->  <!-- começo: círculo 2 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_2_arc_bottom_1"      begin="circle_0_arc_top_1.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-1" />    </animateMotion>    <animateMotion      id="circle_2_arc_top_1"      begin="circle_0_arc_bottom_1.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-1" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_2_arc_bottom_1.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="85.71428571428572" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_2_arc_bottom_1.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 2 -->  <!-- começo: círculo 3 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_3_arc_bottom_2"      begin="circle_0_arc_top_2.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-2" />    </animateMotion>    <animateMotion      id="circle_3_arc_top_2"      begin="circle_0_arc_bottom_2.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-2" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_3_arc_bottom_2.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="114.28571428571429" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_3_arc_bottom_2.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 3 -->  <!-- começo: círculo 4 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_4_arc_bottom_3"      begin="circle_0_arc_top_3.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-3" />    </animateMotion>    <animateMotion      id="circle_4_arc_top_3"      begin="circle_0_arc_bottom_3.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-3" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_4_arc_bottom_3.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="142.85714285714286" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_4_arc_bottom_3.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 4 -->  <!-- começo: círculo 5 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_5_arc_bottom_4"      begin="circle_0_arc_top_4.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-4" />    </animateMotion>    <animateMotion      id="circle_5_arc_top_4"      begin="circle_0_arc_bottom_4.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-4" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_5_arc_bottom_4.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="171.42857142857144" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_5_arc_bottom_4.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 5 --></svg>

Conclusão

Essa foi mais uma das animações que venho explorando com SVG. A ideia foi reutilizar a estrutura das anteriores, ajustando os caminhos e o tempo de cada movimento.

Ao calcular os arcos com JavaScript e usar animateMotion com mpath, consegui organizar os deslocamentos de forma previsível. Os atributos begin, keyTimes, keyPoints e keySplines ajudaram a controlar a sequência e a fluidez da animação.

No geral, o foco foi entender melhor como esses elementos se combinam para criar movimentos sincronizados. A animação completa dura meio segundo, mas os testes com dois segundos facilitam a visualização do que está acontecendo.

Continuo experimentando novas formas de explorar o SVG declarativo.

Resultado



Código

<svg  xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 200 200"  width="200"  height="200">  <defs>    <path      id="arc-top-0"      d="M 28.571428571428573, 100 A 14.285714285714286, 14.285714285714286 0 0 1 57.142857142857146, 100 "    />    <path      id="arc-top-1"      d="M 57.142857142857146, 100 A 14.285714285714286, 14.285714285714286 0 0 1 85.71428571428572, 100 "    />    <path      id="arc-top-2"      d="M 85.71428571428572, 100 A 14.285714285714286, 14.285714285714286 0 0 1 114.28571428571429, 100 "    />    <path      id="arc-top-3"      d="M 114.28571428571429, 100 A 14.285714285714286, 14.285714285714286 0 0 1 142.85714285714286, 100 "    />    <path      id="arc-top-4"      d="M 142.85714285714286, 100 A 14.285714285714286, 14.285714285714286 0 0 1 171.42857142857144, 100 "    />    <path      id="arc-bottom-0"      d="M 57.142857142857146, 100 A 14.285714285714286, 14.285714285714286 0 0 1 28.571428571428573, 100 "    />    <path      id="arc-bottom-1"      d="M 85.71428571428572, 100 A 14.285714285714286, 14.285714285714286 0 0 1 57.142857142857146, 100 "    />    <path      id="arc-bottom-2"      d="M 114.28571428571429, 100 A 14.285714285714286, 14.285714285714286 0 0 1 85.71428571428572, 100 "    />    <path      id="arc-bottom-3"      d="M 142.85714285714286, 100 A 14.285714285714286, 14.285714285714286 0 0 1 114.28571428571429, 100 "    />    <path      id="arc-bottom-4"      d="M 171.42857142857144, 100 A 14.285714285714286, 14.285714285714286 0 0 1 142.85714285714286, 100 "    />  </defs>  <rect    width="199"    height="199"    x="0.5"    y="0.5"    fill="white"    stroke="lightgray"    stroke-width="1"    rx="6"  />  <g fill="none" stroke-width="1" stroke="lightgray">    <use href="#arc-top-0" />    <use href="#arc-top-1" />    <use href="#arc-top-2" />    <use href="#arc-top-3" />    <use href="#arc-top-4" />    <use href="#arc-bottom-0" />    <use href="#arc-bottom-1" />    <use href="#arc-bottom-2" />    <use href="#arc-bottom-3" />    <use href="#arc-bottom-4" />  </g>  <!-- começo: círculo 0 -->  <ellipse rx="8" ry="8" fill="black">    <animateMotion      id="circle_0_arc_top_0"      begin="0; circle_0_arc_bottom_0.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-0" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_1"      begin="circle_0_arc_top_0.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-1" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_2"      begin="circle_0_arc_top_1.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-2" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_3"      begin="circle_0_arc_top_2.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-3" />    </animateMotion>    <animateMotion      id="circle_0_arc_top_4"      begin="circle_0_arc_top_3.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-4" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_4"      begin="circle_0_arc_top_4.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-4" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_3"      begin="circle_0_arc_bottom_4.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-3" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_2"      begin="circle_0_arc_bottom_3.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-2" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_1"      begin="circle_0_arc_bottom_2.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-1" />    </animateMotion>    <animateMotion      id="circle_0_arc_bottom_0"      begin="circle_0_arc_bottom_1.end"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-0" />    </animateMotion>  </ellipse>  <!-- fim: círculo 0 -->  <!-- começo: círculo 1 -->  <ellipse rx="8" ry="8" fill="black">    <animateMotion      id="circle_1_arc_bottom_0"      begin="circle_0_arc_top_0.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-0" />    </animateMotion>    <animateMotion      id="circle_1_arc_top_0"      begin="circle_0_arc_bottom_0.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-0" />    </animateMotion>  </ellipse>  <!-- fim: círculo 1 -->  <!-- começo: círculo 2 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_2_arc_bottom_1"      begin="circle_0_arc_top_1.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-1" />    </animateMotion>    <animateMotion      id="circle_2_arc_top_1"      begin="circle_0_arc_bottom_1.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-1" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_2_arc_bottom_1.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="85.71428571428572" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_2_arc_bottom_1.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 2 -->  <!-- começo: círculo 3 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_3_arc_bottom_2"      begin="circle_0_arc_top_2.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-2" />    </animateMotion>    <animateMotion      id="circle_3_arc_top_2"      begin="circle_0_arc_bottom_2.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-2" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_3_arc_bottom_2.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="114.28571428571429" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_3_arc_bottom_2.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 3 -->  <!-- começo: círculo 4 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_4_arc_bottom_3"      begin="circle_0_arc_top_3.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-3" />    </animateMotion>    <animateMotion      id="circle_4_arc_top_3"      begin="circle_0_arc_bottom_3.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-3" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_4_arc_bottom_3.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="142.85714285714286" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_4_arc_bottom_3.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 4 -->  <!-- começo: círculo 5 -->  <ellipse rx="8" ry="8" fill="transparent">    <animateMotion      id="circle_5_arc_bottom_4"      begin="circle_0_arc_top_4.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-bottom-4" />    </animateMotion>    <animateMotion      id="circle_5_arc_top_4"      begin="circle_0_arc_bottom_4.begin"      dur="500ms"      keyTimes="0; 1"      keyPoints="0; 1"      calcMode="spline"      keySplines="0.25, 0.1, 0.25, 1"      fill="freeze"    >      <mpath href="#arc-top-4" />    </animateMotion>    <set      attributeName="fill"      to="black"      begin="circle_5_arc_bottom_4.begin"      fill="freeze"    />  </ellipse>  <ellipse rx="8" ry="8" cx="171.42857142857144" cy="100" fill="black">    <set      attributeName="fill"      to="transparent"      begin="circle_5_arc_bottom_4.begin"      fill="freeze"    />  </ellipse>  <!-- fim: círculo 5 --></svg>

Leitura recomendada