Sexta das doze animações.
Animações de Carregamento (série de 13 partes)
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
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
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 decircle_0_arc_top_0acircle_0_arc_top_4, ecircle_0_arc_bottom_4acircle_0_arc_bottom_0begin: cada animação começa quando a anterior termina, repetindo indefinidamente#circle_0_arc_top_0,0; circle_0_arc_bottom_0.end:0faz com que esta seja a primeira animação a executar, começando o movimento para a direita, ecircle_0_arc_bottom_0.endfaz com que animação seja executada quando a animação do arco oposto termina#circle_0_arc_top_1a#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_3a#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 exemplosfill="freeze": mantém o círculo na posição final da animação.calcMode="spline": para poder usar curvas de Bézier no atributokeySplineskeyTimes,keyPointsekeySplines: dado que funcionam juntos, representei os valores dos atributos em uma tabela:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.25, 0.1, 0.25, 1 |
| 1 | 1 | - |
Lembrando como funcionam:
keyTimes: define em quais momentos específicos ao longo da animação oskeyPointsocorrerão, varia de0a1keyPoints: define onde o círculo deve estar ao longo do caminho, varia de0a1keySplines: define curvas de Bézier para suavizar a progressão entrekeyPoints
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 comcircle_1_...em vez decircle_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 comcircle_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>