Segunda das doze animações.
Animação
Resultado final:
Introdução
Criei essa animação com SVG, seguindo uma estrutura bem parecida com a da animação de janeiro:
<!-- definições de tamanho --><svg> <defs> <!-- caminhos que os círculos percorrem --> <path /> <path /> </defs> <!-- plano de fundo --> <rect /> <!-- desenho dos caminhos que os círculos percorrem, para debug --> <use /> <use /> <!-- círculo da esquerda --> <ellipse> <!-- animação que faz o círculo percorrer o caminho --> <animateMotion> <mpath /> </animateMotion> <!-- animação que estica o círculo horizontalmente --> <animate /> <!-- animação que estica o círculo verticalmente --> <animate /> </ellipse> <!-- círculo da direita --> <ellipse> <!-- animação que faz o círculo percorrer o caminho --> <animateMotion> <mpath /> </animateMotion> <!-- animação que estica o círculo horizontalmente --> <animate /> <!-- animação que estica o círculo verticalmente --> <animate /> </ellipse></svg>
Como funciona
Omiti alguns elementos e atributos nos blocos a seguir para manter a explicação concisa.
Tamanho
Mantive igual a animação de janeiro.
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
Dentro de defs
, defini dois path
s que serão os caminhos que os círculos percorrem. Eles serão referenciados pelos seus id
s, left-motion-path
e right-motion-path
. Os caminhos são duas linhas definidas usando o comando L
. Enquanto a linha esquerda é desenhada da esquerda para a direita, a linha direita é desenhada da direita para a esquerda, isso foi importante para espelhar as animações.
Vale ressaltar o fato de que elementos circle
e ellipse
são posicionados de acordo com seus centros. Logo, o path
esquerdo vai até x = 92
, que é o centro do rect
, x = 100
, menos o raio do círculo, 8
. Já o path
direito vai até x = 108
, centro do rect
mais o raio do círculo. Dessa forma, os círculos não se sobrepõem, apenas se tocam.
Depois do rect
, que é o plano de fundo, defini dois use
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="left-motion-path" d="M30,100 L92,100" /> <path id="right-motion-path" d="M170,100 L108,100" /> </defs> <rect /> <use href="#left-motion-path" fill="none" stroke="lightgray" stroke-width="1" /> <use href="#right-motion-path" fill="none" stroke="lightgray" stroke-width="1" /></svg>
Círculos e suas animações
A duração final da animação é de dois segundos. Nesta seção as animações têm duração de 10 segundos para ficarem mais evidentes.
Animações que percorrem os caminhos
Inicialmente, criei as animações que definem o ciclo de ida e volta dos círculos. Minha intenção aqui era criar uma animação para o círculo esquerdo que também pudesse ser usada no círculo direito, apenas espelhando e mudando o tempo de início da animação. Dessa forma, foi importante definir uma animação em que metade do tempo um círculo ficasse parado, e na outra metade, em movimento.
Animando o círculo esquerdo
Començando pelo círculo esquerdo, usei um elemento ellipse
, pois ele permite definir o raio nos eixos x
e y
separadamente. O elemento mpath
referencia o path#left-motion-path
, enquanto o elemento animateMotion
define a animação.
No elemento animateMotion
, defini os atributos da seguinte forma:
repeatCount="indefinite"
: a animação repete indefinidamente.dur="10000ms"
: a duração é de 10 segundos para os exemplos.calcMode="spline"
: para poder usar curvas de Bézier no atributokeySplines
.
Dado que funcionam juntos, representei os valores dos atribues keyTimes
, keyPoints
e keySplines
em uma tabela:
keyTimes | keyPoints | keySplines |
---|---|---|
0 | 0.5 | 0, 0.8, 1, 1 |
0.1 | 0.35 | 0.8, 0, 1, 1 |
0.2 | 1 | 0, 0, 1, 1 |
0.7 | 1 | 0.42, 0, 0.58, 1 |
0.8 | 0 | 0.42, 0, 0.58, 1 |
0.95 | 0.5 | 0, 0, 1, 1 |
1 | 0.5 | - |
Lembrando como funcionam:
keyTimes
: define em quais momentos específicos durante a duração da animação oskeyPoints
ocorrerão, varia de0
a1
.keyPoints
: define onde o círculo deve estar ao longo do caminho, varia de0
a1
.keySplines
: define curvas de Bézier para suavizar a progressão entrekeyPoints
.
Para deixar mais claro o funcionamento da animação para mim mesmo, criei um gráfico usando os valores da tabela:
- Os eixos
x
ey
representam os valores dos atributoskeyTimes
ekeyPoints
, respectivamente. - No topo, a linha horizontal cinza escuro representa os segmentos do atributo
keySplines
, que definem curvas de Bézier:0, 0.8, 1, 1
: começa rápida e desacelera até o final.0.8, 0, 1, 1
: começa lenta e acelera até o final.0, 0, 1, 1
: tem comportamento linear.0.42, 0, 0.58, 1
: começa lenta, acelera no meio e desacelera no final.0.42, 0, 0.58, 1
: mesma curva do segmento anterior.0, 0, 1, 1
: mesma curva do terceiro segmento.
- À direita, a linha vertical cinza claro e o círculo preto representam o movimento final.
Apenas o círculo esquerdo animado
Código
<svg> <defs> <path /> <path /> </defs> <rect /> <use /> <use /> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" keyPoints="0.5; 0.35; 1; 1; 0; 0.5; 0.5" keySplines="0, 0.8, 1, 1; 0.8, 0, 1, 1; 0, 0, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" > <mpath href="#left-motion-path" /> </animateMotion> </ellipse></svg>
Animando o círculo direito
Assim como no círculo esquerdo, usei um elemento ellipse
, dessa vez com o elemento mpath
referenciando o path#right-motion-path
.
Os atributos do elemento animateMotion
são os mesmos usados no círculo esquerdo, com a diferença que a animação é adiantada em metade do tempo de ciclo:
begin="-5000ms"
: define quando a animação deve começar.
A animação está espelhada pois o path
usado foi desenhado no sentido contrário do círculo esquerdo, da direita para a esquerda.
Apenas o círculo direito animado
Código
<svg> <defs> <path /> <path /> </defs> <rect /> <use /> <use /> <ellipse> <animateMotion> <mpath /> </animateMotion> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" begin="-5000ms" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" keyPoints="0.5; 0.35; 1; 1; 0; 0.5; 0.5" keySplines="0, 0.8, 1, 1; 0.8, 0, 1, 1; 0, 0, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" > <mpath href="#right-motion-path" /> </animateMotion> </ellipse></svg>
Resultado
Código
<svg> <defs> <path /> <path /> </defs> <rect /> <use /> <use /> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" keyPoints="0.5; 0.35; 1; 1; 0; 0.5; 0.5" keySplines="0, 0.8, 1, 1; 0.8, 0, 1, 1; 0, 0, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" > <mpath href="#left-motion-path" /> </animateMotion> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" begin="-5000ms" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" keyPoints="0.5; 0.35; 1; 1; 0; 0.5; 0.5" keySplines="0, 0.8, 1, 1; 0.8, 0, 1, 1; 0, 0, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" > <mpath href="#right-motion-path" /> </animateMotion> </ellipse></svg>
Animações que esticam
Em seguida, fiz as animações que distorcem o círculo de alguma forma.
Animando rx
e ry
do círculo esquerdo
Para transmitir a ideia de velocidade e impacto, criei duas animações que, em conjunto, esticam os círculos horizontal e verticalmente.
Os elementos animate
possuem atributos com o mesmo valor:
repeatCount="indefinite"
: a animação repete indefinidamente.dur="10000ms"
: a duração é de 10 segundos para os exemplos.keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1"
: define em quais momentos específicos durante a duração da animação osvalues
serão assumidos pelo atributoattributeName
, varia de0
a1
.
Para o atributo values
:
attributeName="rx" | attributeName="ry" | Descrição |
---|---|---|
8 | 8 | Começa redondo |
8.4 | 7.6 | Estica horizontalmente e encolhe verticalmente, para expressar velocidade ao pegar impulso |
8 | 8 | Volta a ser redondo até o impacto com o outro círculo |
8 | 8 | Se mantém redondo enquanto está parado |
7.6 | 8.4 | Encolhe horizontalmente e estica verticalmente, ao receber o impacto do outro círculo |
8 | 8 | Volta a ser redondo até voltar ao meio do caminho |
8 | 8 | Se mantém redondo enquanto está parado |
Apenas o círculo esquerdo animado
Código
<svg> <defs> <path /> <path /> </defs> <rect /> <use /> <use /> <ellipse> <animateMotion> <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 8.4; 8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 7.6; 8; 8; 8.4; 8; 8" /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> </ellipse></svg>
Animando rx
e ry
do círculo direito
Os atributos são os mesmos usados no círculo esquerdo, com a diferença que a animação é adiantada em metade do tempo de ciclo:
begin="-5000ms"
: define quando a animação deve começar.
Apenas o círculo direito animado
Código
<svg> <defs> <path /> <path /> </defs> <rect /> <use /> <use /> <ellipse> <animateMotion> <mpath /> </animateMotion> <animate /> <animate /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" begin="-5000ms" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 8.4; 8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" begin="-5000ms" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 7.6; 8; 8; 8.4; 8; 8" /> </ellipse></svg>
Resultado
Código
<svg> <defs> <path /> <path /> </defs> <rect /> <use /> <use /> <ellipse> <animateMotion> <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 8.4; 8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 7.6; 8; 8; 8.4; 8; 8" /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" begin="-5000ms" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 8.4; 8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" begin="-5000ms" dur="10000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 7.6; 8; 8; 8.4; 8; 8" /> </ellipse></svg>
Conclusão
Assim como a animação de janeiro, a de fevereiro também foi divertida de criar, pois é a primeira que envolve a interação entre círculos — algo que se tornará comum nas próximas postagens da série. Dessa vez, não utilizei elementos ou atributos novos, mas pude aplicar tudo o que aprendi na animação de janeiro.
O maior desafio foi criar um ciclo de animação perfeito entre os círculos. Criar o gráfico que ilustra como os atributos keyTimes
, keyPoints
e keySplines
funcionam em conjunto foi fundamental para a experimentação de diferentes valores, algo que certamente utilizarei nas próximas animações.
Resultado
Código
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200"> <defs> <path id="left-motion-path" d="M30,100 L92,100" /> <path id="right-motion-path" d="M170,100 L108,100" /> </defs> <rect width="199" height="199" x="0.5" y="0.5" fill="white" stroke="lightgray" stroke-width="1" rx="6" /> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="2000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" keyPoints="0.5; 0.35; 1; 1; 0; 0.5; 0.5" keySplines="0, 0.8, 1, 1; 0.8, 0, 1, 1; 0, 0, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" > <mpath href="#left-motion-path" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="2000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 8.4; 8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="2000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 7.6; 8; 8; 8.4; 8; 8" /> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" begin="-1000ms" dur="2000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" keyPoints="0.5; 0.35; 1; 1; 0; 0.5; 0.5" keySplines="0, 0.8, 1, 1; 0.8, 0, 1, 1; 0, 0, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" > <mpath href="#right-motion-path" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" begin="-1000ms" dur="2000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 8.4; 8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" begin="-1000ms" dur="2000ms" keyTimes="0; 0.1; 0.2; 0.7; 0.8; 0.95; 1" values="8; 7.6; 8; 8; 8.4; 8; 8" /> </ellipse></svg>