Pular para o conteúdo principal

Animações de Carregamento: Fevereiro

· Leitura de 12 minutos

Segunda das doze animações.


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

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

info

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 paths que serão os caminhos que os círculos percorrem. Eles serão referenciados pelos seus ids, 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

info

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 atributo keySplines.

Dado que funcionam juntos, representei os valores dos atribues keyTimes, keyPoints e keySplines em uma tabela:

keyTimeskeyPointskeySplines
00.50, 0.8, 1, 1
0.10.350.8, 0, 1, 1
0.210, 0, 1, 1
0.710.42, 0, 0.58, 1
0.800.42, 0, 0.58, 1
0.950.50, 0, 1, 1
10.5-

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.

Para deixar mais claro o funcionamento da animação para mim mesmo, criei um gráfico usando os valores da tabela:

keyPointskeyTimes101

  • Os eixos x e y representam os valores dos atributos keyTimes e keyPoints, respectivamente.
  • No topo, a linha horizontal cinza escuro representa os segmentos do atributo keySplines, que definem curvas de Bézier:
    1. 0, 0.8, 1, 1: começa rápida e desacelera até o final.
    2. 0.8, 0, 1, 1: começa lenta e acelera até o final.
    3. 0, 0, 1, 1: tem comportamento linear.
    4. 0.42, 0, 0.58, 1: começa lenta, acelera no meio e desacelera no final.
    5. 0.42, 0, 0.58, 1: mesma curva do segmento anterior.
    6. 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 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
8.47.6Estica horizontalmente e encolhe verticalmente, para expressar velocidade ao pegar impulso
88Volta a ser redondo até o impacto com o outro círculo
88Se mantém redondo enquanto está parado
7.68.4Encolhe horizontalmente e estica verticalmente, ao receber o impacto do outro círculo
88Volta a ser redondo até voltar ao meio do caminho
88Se 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>

Leitura recomendada