Conteúdo deste artigo
- Estrutura HTML
- Posicionando a faixa de miniaturas
- Criando a animação do Motion Path
- Construindo a linha do tempo
- Conclusão
Este experimento foi inspirado em um estudo de movimento compartilhado por Rron Berisha . Ao recriar a ideia para a web, fiquei interessado em como o plugin MotionPath do GSAP poderia ser usado para criar transições fluidas entre diferentes estados da galeria usando apenas alguns pontos de controle.
Neste tutorial, construiremos uma galeria de imagens de prova de conceito simples, onde uma pilha de miniaturas sobrepostas se expande em uma faixa de imagem vertical que revela uma imagem em destaque. A galeria em si não se destina a ser totalmente funcional. Em vez disso, o objetivo é explorar como uma pequena quantidade de lógica de trajetória de movimento pode criar uma transição surpreendentemente orgânica.
O efeito depende do plugin MotionPath do GSAP, permitindo que cada miniatura siga uma trajetória curva em vez de se mover diretamente de uma posição para outra. Ao definir apenas alguns pontos por imagem, podemos criar um movimento que parece muito mais dinâmico do que uma interpolação de posição padrão.
Estrutura HTML
O HTML é intencionalmente mínimo. Temos um botão abrir, um contêiner de imagens em destaque e uma coleção de elementos em miniatura.
Cada miniatura contém uma imagem e um rótulo numérico. Inicialmente, todas as miniaturas ficam umas sobre as outras, formando uma pilha compacta. Assim que a interação é acionada, elas se espalham em uma faixa vertical posicionada ao lado da imagem em destaque.
<button class="open__btn">+ Open</button>
<div class="big__featured__container">
<button class="close__btn">Close</button>
</div>
<div class="thumbnail">
<p class="number">01</p>
<div class="thumbnail__image">
<img src="./public/img-1.webp" alt="" />
</div>
</div>
Posicionando a faixa de miniaturas
Antes de animar qualquer coisa, precisamos determinar onde as miniaturas devem terminar. Medimos uma miniatura, calculamos suas dimensões em escala e determinamos a posição final da faixa vertical.
Como as miniaturas diminuem durante a animação, precisamos compensar a largura perdida ao calcular a posição X final.
const scaledWidth = this.thumbnailRect.width * this.scale;
this.targetX =
this.vw -
this.thumbnailRect.right +
(this.thumbnailRect.width - scaledWidth) / 2;
Em seguida, calculamos uma posição centralizada verticalmente para a faixa.
this.targetY =
(
this.vh -
this.thumbnailRect.height * this.scale +
this.thumbnailRect.top -
this.gap * 2
) / 2;
Isso nos dá a posição de âncora a partir da qual a localização final de cada miniatura será calculada.
Criando a animação do Motion Path
A parte mais interessante do efeito acontece dentro da configuração do MotionPath. Cada miniatura recebe seu próprio destino dentro da faixa, mas o movimento em si é definido por apenas dois pontos.
Primeiro, calculamos a posição Y final para cada miniatura. O índice da imagem determina sua localização dentro da faixa.
const targetYItem =
this.targetY -
index * (this.thumbnailRect.height * this.scale + this.gap);
Assim que soubermos o destino final, podemos definir o próprio caminho do movimento.
return {
path: [
{
x: this.targetX * 0.95,
y: -targetYItem * 0.095,
scale: (1 - this.scale) * 0.25 + this.scale,
},
{
x: this.targetX,
y: -targetYItem,
scale: this.scale,
},
],
curviness: 0.45,
};
O segundo ponto representa o destino final da miniatura na faixa. O primeiro ponto atua como um waypoint temporário, criando uma ultrapassagem sutil antes que a imagem se estabeleça no lugar.
O interessante é que cada miniatura compartilha a mesma definição de caminho, mas cada uma recebe uma posição Y final diferente. Por causa disso, o GSAP gera curvas ligeiramente diferentes para cada imagem. O waypoint intermediário é calculado a partir da posição final de cada miniatura. Como resultado, as miniaturas que terminam mais abaixo na faixa são temporariamente mais deslocadas antes de se fixarem no lugar, produzindo arcos de movimento maiores.
O resultado parece muito mais orgânico do que uma interpolação de posição padrão, embora a configuração em si permaneça extremamente simples.
Construindo a linha do tempo
Uma vez definido o caminho, podemos combinar tudo em uma única linha do tempo GSAP.
O botão abrir desaparece enquanto as miniaturas viajam em direção às suas novas posições.
this.tl = gsap.timeline({
defaults: {
duration: 1.2,
ease: 'expo.inOut',
easeReverse: true,
},
paused: true,
});
this.tl.to(this.btnOpen, {
opacity: 0,
duration: 0.5,
});
this.tl.to(
this.thumbnails,
{
motionPath: (index) => {
// ...
},
stagger: {
from: 'start',
each: 0.02,
},
},
'<'
);
Ao mesmo tempo, os rótulos numéricos são animados e exibidos.
this.tl.fromTo(
this.numbers,
{
opacity: 0,
yPercent: 50,
},
{
opacity: 1,
yPercent: 0,
stagger: 0.045,
},
'<'
);
Finalmente, a imagem em destaque desaparece e aparece na visualização.
this.tl.to(
this.featuredContainer,
{
scale: 1,
opacity: 1,
},
'<'
);
Conclusão
O que torna esse efeito interessante é o quão pouco é realmente necessário para criá-lo. Alguns pontos do caminho do movimento e algumas medições simples são suficientes para transformar uma pilha estática de imagens em algo que parece muito mais vivo.
A demonstração em si é intencionalmente simples, mas a ideia subjacente pode ser levada muito mais longe experimentando diferentes formas de caminho, direções de empilhamento, escalas e configurações escalonadas.
Não se esqueça de conferir as outras variações de demonstração incluídas no projeto. Embora esta demonstração comece com uma pilha de miniaturas sobrepostas e se expanda em uma faixa vertical, alguns dos outros exemplos começam com faixas de imagens horizontais e exploram configurações de MotionPath completamente diferentes.
Espero que este tutorial lhe dê algumas ideias para seus próprios experimentos GSAP MotionPath. Obrigado por ler 😃
