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 8 partes)
  1. Introdução
  2. Janeiro
  3. Fevereiro
  4. Março
  5. Abril
  6. Maio
  7. Junho
  8. Julho

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