Quinta das doze animações.
Animações de Carregamento (série de 13 partes)
Animação
A animação é um Pêndulo de Newton, 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 /> </defs> <!-- plano de fundo --> <rect /> <!-- desenhos dos caminhos que os círculos percorrem, para debug --> <g> <use /> <use /> <use /> <use /> </g> <!-- círculos com diferentes animações --> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <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
Dentro de defs, defini os paths que os círculos percorrem. Eles serão referenciados pelos seus respectivos ids.
#left-arce#right-arc: representam o movimento de pêndulo dos círculos 1 e 5#left-linee#right-line: representam um pequeno movimento de impacto dos círculos 2 e 4
Depois do rect, que é o plano de fundo, defini elementos 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-arc" d="M68,115 Q38,115 38,85" /> <path id="right-arc" d="M132,115 Q162,115 162,85" /> <path id="left-line" d="M84,115 L83.2,115" /> <path id="right-line" d="M116,115 116.8,115" /> </defs> <rect /> <g fill="none" stroke="lightgray" stroke-width="1"> <use href="#left-arc" /> <use href="#right-arc" /> <use href="#left-line" /> <use href="#right-line" /> </g></svg>Círculos e suas animações
A duração final da animação é de 0.7 segundos. Nesta seção as animações têm duração de 10 segundos para ficarem mais evidentes.
Animações que percorrem o caminho
Inicialmente, criei as animações dos círculos 1 e 2. Minha intenção aqui era espelhar essas mesmas animações nos círculos 4 e 5. O círculo 3 não possui animação.
Animando o círculo 1
Usei um elemento ellipse, pois ele permite definir o raio nos eixos x e y separadamente. O elemento mpath referencia o path#left-arc, enquanto o elemento animateMotion define a animação.
No elemento animateMotion, defini os atributos da seguinte forma:
id="circle_left_arc": ID usado no atributobegindas animações.begin="0; circle_right_arc.end": a animação começa imediatamente e também quando a animação do círculo 5 termina.dur="10000ms": a duração é de 10 segundos para os exemplos.calcMode="spline": para poder usar curvas de Bézier no atributokeySplines.rotate="auto": faz com que o círculo siga a rotação do caminho, mantendo a orientação correta.fill="freeze": mantém o círculo na posição final da animação.
Dado que funcionam juntos, representei os valores dos atribues keyTimes, keyPoints e keySplines em uma tabela:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.1, 0.7, 1, 1 |
| 0.5 | 1 | 0.7, 0.1, 1, 1 |
| 1 | 0 | - |
Lembrando como funcionam:
keyTimes: define em quais momentos específicos durante a duração da animação oskeyPointsocorrerão, varia de0a1.keyPoints: define onde o círculo deve estar ao longo do caminho, varia de0a1.keySplines: define curvas de Bézier para suavizar a progressão entrekeyPoints.
Ou seja, na primeira metade da animação, o círculo vai do extremo direito ao esquerdo seguindo a curva de Bézier, começando com um movimento mais rápido e terminando mais lento. Na segunda metade da animação, o círculo vai do extremo esquerdo ao direito seguindo a curva de Bézier, começando com um movimento mais lento e terminando mais rápido.
Apenas o círculo 1 animado
Código
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_arc" begin="0; circle_right_arc.end" dur="10000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#left-arc" /> </animateMotion> </ellipse></svg>Animando o círculo 2
Usei um elemento ellipse. O elemento mpath referencia o path#left-line, enquanto o elemento animateMotion define a animação.
No elemento animateMotion, defini os atributos da seguinte forma:
id="circle_left_line": ID usado no atributobegindas animações.begin="0; circle_right_arc.end": a animação começa imediatamente e também quando a animação do círculo 5 termina.dur="2000ms": a duração é de 2 segundos para os exemplos.rotate="auto": faz com que o círculo siga a rotação do caminho, mantendo a orientação correta.fill="freeze": mantém o círculo na posição final da animação.
Dado que funcionam juntos, representei os valores dos atribues keyTimes e keyPoints em uma tabela:
keyTimes | keyPoints |
|---|---|
| 0 | 0 |
| 0.5 | 1 |
| 1 | 0 |
Ou seja, na primeira metade da animação, o círculo vai do extremo direito ao esquerdo seguindo de forma linear. Na segunda metade da animação, o círculo vai do extremo esquerdo ao direito linearmente.
Aproveitei para adicionar o círculo 3, que não possui animação.
Círculos 1, 2 e 3 animados
Código
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse /> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_line" begin="0; circle_right_arc.end" dur="2000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#left-line" /> </animateMotion> </ellipse> <ellipse rx="8" ry="8" cx="100" cy="115" /></svg>Animando o círculo 4
Para animar o círculo 4 foram necessários dois elementos ellipse:
- Um que possui a animação de fato, que inicia oculto e passa a ser exibido quando a animação do círculo 1 termina
- Outro que é exibido apenas no início e é oculto quando a animação do círculo 1 termina
Usei esta abordagem para facilitar o posicionamento e animação do círculo 4, 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 1 termina.
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.
O elemento mpath referencia o path#right-line, enquanto o elemento animateMotion define a animação.
No elemento animateMotion, defini os atributos da seguinte forma:
id="circle_right_line": ID usado no atributobegindas animações.begin="circle_left_arc.end": a animação começa quando a animação do círculo 1 termina.dur="2000ms": a duração é de 2 segundos para os exemplos.rotate="auto": faz com que o círculo siga a rotação do caminho, mantendo a orientação correta.fill="freeze": mantém o círculo na posição final da animação.
Dado que funcionam juntos, representei os valores dos atribues keyTimes e keyPoints em uma tabela:
keyTimes | keyPoints |
|---|---|
| 0 | 0 |
| 0.5 | 1 |
| 1 | 0 |
Ou seja, na primeira metade da animação, o círculo vai do extremo esquerdo ao direito seguindo de forma linear. Na segunda metade da animação, o círculo vai do extremo direito ao esquedo linearmente.
Círculos 1, 2, 3 e 4 animados
Código
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse /> <ellipse /> <ellipse /> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_line" begin="circle_left_arc.end" dur="2000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#right-line" /> </animateMotion> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="116" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse></svg>Animando o círculo 5
A mesma abordagem do círculo 4 foi usada para o círculo 5, que também possui dois elementos ellipse.
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 1 termina.
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.
O elemento mpath referencia o path#right-arc, enquanto o elemento animateMotion define a animação.
No elemento animateMotion, defini os atributos da seguinte forma:
id="circle_right_arc": ID usado no atributobegindas animações.begin="circle_left_arc.end": a animação começa imediatamente e também quando a animação do círculo 1 termina.dur="10000ms": a duração é de 10 segundos para os exemplos.calcMode="spline": para poder usar curvas de Bézier no atributokeySplines.rotate="auto": faz com que o círculo siga a rotação do caminho, mantendo a orientação correta.fill="freeze": mantém o círculo na posição final da animação.
Dado que funcionam juntos, representei os valores dos atribues keyTimes, keyPoints e keySplines em uma tabela:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.1, 0.7, 1, 1 |
| 0.5 | 1 | 0.7, 0.1, 1, 1 |
| 1 | 0 | - |
Ou seja, na primeira metade da animação, o círculo vai do extremo esquerdo ao direito seguindo a curva de Bézier, começando com um movimento mais rápido e terminando mais lento. Na segunda metade da animação, o círculo vai do extremo direito ao esquerdo seguindo a curva de Bézier, começando com um movimento mais lento e terminando mais rápido.
Todos os círculos animados
Código
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_arc" begin="circle_left_arc.end" dur="10000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#right-arc" /> </animateMotion> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="132" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse></svg>Animações que esticam
Em seguida, fiz as animações que distorcem os círculos de alguma forma.
Animando rx e ry dos círculos 1 e 5
Para transmitir a ideia de impacto e velocidade, criei duas animações que, em conjunto, esticam os círculos horizontal e verticalmente.
Os elementos animate possuem atributos com o mesmo valor:
dur="10000ms": a duração é de 10 segundos para os exemplos.keyTimes="0; 0.25; 0.5; 0.75; 1": define em quais momentos específicos durante a duração da animação osvaluesserão assumidos pelo atributoattributeName, varia de0a1.
Para o atributo values:
attributeName="rx" | attributeName="ry" | Descrição |
|---|---|---|
| 8 | 8 | Começa redondo |
| 7.6 | 8.4 | Estica verticalmente e encolhe horizontalmente, para expressar o impacto recebido |
| 8 | 8 | Volta a ser redondo ao chegar ao topo |
| 8.4 | 7.6 | Estica horizontalmente e encolhe verticalmente, para expressar velocidade ao descer |
| 8 | 8 | Volta a ser redondo ao voltar para baixo |
Para o atributo begin, as animações dos elementos animate começam quando a animação do animateMotion vizinho começa:
begin="circle_left_arc.begin": para o círculo 1begin="circle_right_arc.begin": para o círculo 5
Todos os círculos animados
Código
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse> <animateMotion /> <animate attributeName="rx" begin="circle_left_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_left_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> </ellipse> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse> <animateMotion /> <animate attributeName="rx" begin="circle_right_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_right_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> <set /> </ellipse> <ellipse /></svg>Conclusão
Nessa animação pude utilizar novamente os syncbase values no atributo begin, o que permitiu coordenar o tempo das animações de maneira reativa, fazendo com que os elementos de cada lado respondessem ao término das animações uns dos outros.
Além disso, explorei pela primeira vez o uso do elemento set para alterar atributos também com base em syncbase values. Isso abriu novas possibilidades para manipular propriedades de forma pontual, sem a necessidade de definir animações completas quando uma simples mudança de estado é suficiente.
Pretendo continuar explorando essas ferramentas em animações futuras.
Resultado
Código
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200"> <defs> <path id="left-arc" d="M68,115 Q38,115 38,85" /> <path id="right-arc" d="M132,115 Q162,115 162,85" /> <path id="left-line" d="M84,115 L83.2,115" /> <path id="right-line" d="M116,115 116.8,115" /> </defs> <rect width="199" height="199" x="0.5" y="0.5" fill="white" stroke="lightgray" stroke-width="1" rx="6" /> <!-- começo: círculo 1 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_arc" begin="0; circle_right_arc.end" dur="700ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#left-arc" /> </animateMotion> <animate attributeName="rx" begin="circle_left_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_left_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> </ellipse> <!-- fim: círculo 1 --> <!-- começo: círculo 2 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_line" begin="0; circle_right_arc.end" dur="150ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#left-line" /> </animateMotion> </ellipse> <!-- fim: círculo 2 --> <!-- começo: círculo 3 --> <ellipse rx="8" ry="8" cx="100" cy="115" /> <!-- fim: círculo 3 --> <!-- começo: círculo 4 --> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_line" begin="circle_left_arc.end" dur="150ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#right-line" /> </animateMotion> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="116" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <!-- fim: círculo 4 --> <!-- começo: círculo 5 --> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_arc" begin="circle_left_arc.end" dur="700ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#right-arc" /> </animateMotion> <animate attributeName="rx" begin="circle_right_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_right_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="132" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <!-- fim: círculo 5 --></svg>