Pular para o conteúdo principal

Animações de Carregamento: Maio

· Leitura de 13 minutos

Quinta das doze animações.


Animações de Carregamento (série de 13 partes)
  1. Introdução
  2. Janeiro
  3. Fevereiro
  4. Março
  5. Abril
  6. Maio
  7. Junho
  8. Julho
  9. Agosto
  10. Setembro
  11. Outubro
  12. Novembro
  13. Dezembro

Animação

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

info

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

Tamanho

Mantive o tamanho igual às animações anteriores.

Resultado



Código

<svg  xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 200 200"  width="200"  height="200">  <rect    width="199"    height="199"    x="0.5"    y="0.5"    fill="white"    stroke="lightgray"    strokeWidth="1"    rx="6"  /></svg>

Caminho

Dentro de defs, defini os paths que os círculos percorrem. Eles serão referenciados pelos seus respectivos ids.

  • #left-arc e #right-arc: representam o movimento de pêndulo dos círculos 1 e 5
  • #left-line e #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

info

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 atributo begin das 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 atributo keySplines.
  • 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:

keyTimeskeyPointskeySplines
000.1, 0.7, 1, 1
0.510.7, 0.1, 1, 1
10-

Lembrando como funcionam:

  • keyTimes: define em quais momentos específicos durante a duração da animação os keyPoints ocorrerão, varia de 0 a 1.
  • keyPoints: define onde o círculo deve estar ao longo do caminho, varia de 0 a 1.
  • keySplines: define curvas de Bézier para suavizar a progressão entre keyPoints.

Ou seja, 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 atributo begin das 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:

keyTimeskeyPoints
00
0.51
10

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 atributo begin das 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:

keyTimeskeyPoints
00
0.51
10

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 atributo begin das 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 atributo keySplines.
  • 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:

keyTimeskeyPointskeySplines
000.1, 0.7, 1, 1
0.510.7, 0.1, 1, 1
10-

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 os values serão assumidos pelo atributo attributeName, varia de 0 a 1.

Para o atributo values:

attributeName="rx"attributeName="ry"Descrição
88Começa redondo
7.68.4Estica verticalmente e encolhe horizontalmente, para expressar o impacto recebido
88Volta a ser redondo ao chegar ao topo
8.47.6Estica horizontalmente e encolhe verticalmente, para expressar velocidade ao descer
88Volta 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 1
  • begin="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>

Leitura recomendada