Nona das doze animações.
Animações de Carregamento (série de 13 partes)
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
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, onde0é a borda esquerda. É uma soma, pois a coordenada fica à direita da borda. - Direito:
x = 200 - 33.3333 = 166.6666,y = 100, onde200é 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.6666x = 66.6666 + 1 * 22.2222 = 88.8888x = 66.6666 + 2 * 22.2222 = 111.1111x = 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
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_righteright_to_leftbegin: cada um inicia quando o outro termina (loop contínuo)#left_to_right,0ms; right_to_left.end:0msfaz com que esta seja a primeira animação a executar, eright_to_left.endfaz com que animação seja executada quandoright_to_lefttermina#right_to_left,left_to_right.end: a animação é executada quandoleft_to_righttermina
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 atributokeySplineskeyTimes,keyPointsekeySplines: dado que funcionam juntos, representei os valores dos atributos em uma tabela:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.42, 0, 0.58, 1 |
| 1 | 1 | 0.42, 0, 0.58, 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, 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_4elower_1àlower_4path: 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.53750 * (2 / 4) = 18753750 * (3 / 4) = 2812.53750 * (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_1elower_1deve começar937.5msdepois upper_2elower_2,18755msdepoisupper_3elower_3,2812.55msdepoisupper_4elower_4,37505msdepois
E, quando o círculo horizontal começa a se mover da ponta direita para a esquerda:
- A animação de
upper_4elower_4deve começar937.5msdepois upper_3elower_3,18755msdepoisupper_2elower_2,2812.55msdepoisupper_1elower_1,37505msdepois
Os seguinte atributos são iguais para todos os animateMotions:
dur="3000ms": defini que dura 3 segundos, 1/5 da duração totalfill="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:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.42, 0, 0.58, 1 |
| 0.5 | 1 | 0.42, 0, 0.58, 1 |
| 1 | 0 | - |
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 atributofillto="black": o valor que será aplicadofill="freeze": mantém o valor aplicado
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 atributofillto="transparent": o valor que será aplicadofill="freeze": mantém o valor aplicado
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
- Círculo horizontal:
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.beginelower_1.beginàlower_4.begin, quando suas respectivas animações de movimento se iniciam
- Círculo horizontal:
Os seguinte atributos são iguais para todos os animate:
calcMode="spline": para poder usar curvas de Bézier no atributokeySplinesattributeName,values,keyTimesekeySplinessão representados em conjunto na tabela:
Para o atributo values:
keyTimes | attributeName="rx" | attributeName="ry" | keySplines |
|---|---|---|---|
| 0 | 8 | 8 | 0.42, 0, 0.58, 1 |
| 0.5 | 8.4 | 7.6 | 0.42, 0, 0.58, 1 |
| 1 | 8 | 8 | - |
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
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>