Crie uma apresentação de slides assimétrica em JavaScript com CSS Grid e GSAP

Sem mais delongas, aqui está o que vamos criar:

Observe que a apresentação de slides não é otimizada para dispositivos móveis. O layout assimétrico/quebrado funciona melhor em telas grandes, então certifique-se de visualizar a demonstração em um dispositivo grande. Para dispositivos móveis, você pode escolher ter uma apresentação de slides padrão com uma única imagem e uma sobreposição com algum texto acima dela.

Este não é um tutorial introdutório. Ao longo dos anos, publiquei vários tutoriais onde apresento o GSAP em detalhes e discuto como animar o clip-path propriedade. Se você não estiver familiarizado o suficiente com essas coisas ou precisar de uma atualização, recomendo que você confira os tutoriais abaixo antes de prosseguir.

1. Comece com a marcação HTML

Dentro de um contêiner, colocaremos:

  • Uma lista de slides
  • Um elemento que exibirá o índice do slide ativo.
  • As setas de navegação

Vamos supor que cada slide descreverá uma casa/apartamento para alugar e incluirá um título, algumas imagens e um botão de call-to-action.

A posição destes elementos será diferente e dependerá de três layout-* aulas (layout-a, layout-b, layout-c).

Por padrão, o primeiro slide ficará visível graças ao is-active aula.

Aqui está a marcação necessária em geral:

1
 class="slides-wrapper">
2
   class="slides">
3
     class="slide layout-a is-active">...
4
     class="slide layout-b">...
5
     class="slide layout-c">...
6
  
7
   class="counter">1 / 3
8
   class="arrows-wrapper">
9
     class="arrow arrow-prev" aria-label="Navigate to the previous house">...
10
     class="arrow arrow-next" aria-label="Navigate to the next house">...
11
  
12

E mais especificamente, a marcação dentro de cada slide ficará assim:

1
 class="img-wrapper img1-wrapper">
2
   width="" height="" src="IMG_URL" alt=""> 
3

4
 class="img-wrapper img2-wrapper">
5
   width="" height="" src="IMG_URL" alt="">
6

7
 class="img-wrapper img3-wrapper">
8
   width="" height="" src="IMG_URL" alt="">
9

10
 class="img-wrapper img4-wrapper">
11
   width="" height="" src="IMG_URL" alt="">
12

13
 class="title text">...
14
 href="" class="btn-book text">...

2. Adicione o CSS

Como de costume, vamos nos concentrar nos estilos principais — deixaremos os introdutórios por enquanto.

Primeiro, usaremos CSS Grid para empilhar todos os slides; a qualquer momento, apenas aquele com o is-active a classe aparecerá.

1
.slides-wrapper .slides {
2
  display: grid;
3
}
4

5
.slides-wrapper .slide {
6
  grid-area: 1/1;
7
  opacity: 0;
8
  visibility: hidden;
9
}
10

11
.slides-wrapper .slide.is-active {
12
  opacity: 1;
13
  visibility: visible;
14
}

Cada slide será um contêiner de grade com 20 colunas e linhas. Além disso, cada linha terá um 5vh altura.

A grade de slidesA grade de slidesA grade de slides
1
.slides-wrapper .slide {
2
  display: grid;
3
  grid-template-rows: repeat(20, 5vh);
4
  grid-template-columns: repeat(20, 1fr);
5
}

Em seguida, cada item do slide ficará em um local diferente com base em seu grid-row e grid-column valores. Além disso, alguns itens dos slides compartilharão os mesmos grid-row-end e grid-column-end valores. Todos eles são arbitrários, e você pode alterá-los como quiser.

1
.slides-wrapper .layout-a .img1-wrapper {
2
  grid-row: 1 / -1;
3
  grid-column: 1 / span 7;
4
}
5

6
.slides-wrapper .layout-a .img2-wrapper {
7
  grid-row: 6 / span 5;
8
  grid-column: 16 / -1;
9
}
10

11
.slides-wrapper .layout-a .img3-wrapper {
12
  grid-row: 8 / span 9;
13
  grid-column: 10 / span 5;
14
}
15
.slides-wrapper .layout-a .img4-wrapper {
16
  grid-row: 15 / -1;
17
  grid-column: 17 / -1;
18
}
19
.slides-wrapper .layout-a .title {
20
  grid-row-start: 7;
21
  grid-column-start: 1;
22
}
23

24
.slides-wrapper .layout-a .btn-book {
25
  grid-row: 3;
26
  grid-column-start: 11;
27
}

As imagens caberão perfeitamente dentro de sua célula graças ao object-fit: cover propriedade CSS super útil.

1
.slides-wrapper img {
2
  width: 100%;
3
  height: 100%;
4
  object-fit: cover;
5
}

Por fim, os itens relacionados à navegação serão posicionados de forma absoluta e ficarão nas bordas esquerda e direita da apresentação de slides.

1
.slides-wrapper .counter,
2
.slides-wrapper .arrows-wrapper {
3
  position: absolute;
4
  top: 20px;
5
}
6

7
.slides-wrapper .counter {
8
  left: 20px;
9
}
10

11
.slides-wrapper .arrows-wrapper {
12
  right: 20px;
13
}

3. Adicione o JavaScript

Neste ponto, estamos prontos para adicionar interatividade à nossa apresentação de slides.

Cada vez que clicarmos em uma seta de navegação, executaremos as seguintes ações:

  1. Pegue o slide ativo e seus itens.
  2. Certifique-se de que todos os elementos animados do nosso slideshow se tornem imediatamente visíveis usando o método set() do GSAP. Fazemos isso para cancelar quaisquer estilos inline anteriores que são aplicados durante o ciclo.
  3. Verifique qual botão foi clicado. Se for o próximo botão, definiremos o próximo slide ativo como aquele que imediatamente segue o slide ativo atual. Se não houver tal slide, o próximo slide se tornará o primeiro. Da mesma forma, se o botão anterior for clicado, definiremos o próximo slide como aquele que imediatamente precede o slide ativo atual. Se não houver tal slide, o próximo slide se tornará o último.
  4. Com todo esse conhecimento em mãos, chamaremos nosso tl() função onde animamos todos os itens do slideshow.

Aqui está o código JavaScript necessário:

1
...
2

3
btnArrows.forEach(function (btn) {
4
  btn.addEventListener("click", function (e) {
5
    // 1

6
    const activeSlide = slidesWrapper.querySelector(".slide.is-active");
7
    const activeSlideImgs = activeSlide.querySelectorAll("img");
8
    const activeSlideText = activeSlide.querySelectorAll(".text");
9
    let nextSlide = null;
10

11
    // 2

12
    gsap.set(slideImgs, { clipPath: "inset(0 0 0 0)" });
13
    gsap.set(slideTexts, { opacity: 1 });
14

15
    // 3

16
    if (e.currentTarget === btnArrowNext) {
17
      nextSlide = activeSlide.nextElementSibling
18
        ? activeSlide.nextElementSibling
19
        : firstSlide;
20
    } else {
21
      nextSlide = activeSlide.previousElementSibling
22
        ? activeSlide.previousElementSibling
23
        : lastSlide;
24
    }
25
    // 4

26
    tl(nextSlide, activeSlide, activeSlideImgs, activeSlideText);
27
  });
28
});

Claro, se quisermos estar mais seguros, podemos esperar para executar este código quando todos os ativos da página forem carregados por meio do load evento.

Dentro do tl() função criaremos uma linha do tempo GSAP que ocultará todos os elementos do slide ativo no momento simultaneamente. Mais importante, suas imagens desaparecerão ao animar seus clip-path propriedade. O interessante aqui é que o movimento da animação virá de uma seleção aleatória de clip-path.

Assim que essa linha do tempo terminar, registraremos outra linha do tempo que mostrará os elementos do novo slide ativo novamente de uma vez. Dessa vez, porém, as imagens associadas aparecerão com uma animação de slide oposta. Por exemplo, se as imagens anteriores forem recortadas da esquerda para a direita, estas aparecerão da direita para a esquerda.

Aqui está a assinatura desta função:

1
function tl(
2
  nextActiveEl,
3
  currentActiveSlide,
4
  currentActiveSlideImgs,
5
  currentSlideActiveText
6
) {
7
  const tl = gsap.timeline({ onComplete });
8

9
  const randomClipPathOption = Math.floor(
10
    Math.random() * clipPathOptions.length
11
  );
12

13
  tl.to(currentActiveSlideImgs, {
14
    clipPath: clipPathOptions[randomClipPathOption]
15
  }).to(
16
    currentSlideActiveText,
17
    {
18
      opacity: 0,
19
      duration: 0.15
20
    },
21
    "-=0.5"
22
  );
23

24
  function onComplete() {
25
    currentActiveSlide.classList.remove(ACTIVE_CLASS);
26
    nextActiveEl.classList.add(ACTIVE_CLASS);
27
    counterSpan.textContent = slidesArray.indexOf(nextActiveEl) + 1;
28
    
29
    const nextSlideImgs = nextActiveEl.querySelectorAll("img");
30
    const nextSlideText = nextActiveEl.querySelectorAll(".text");
31
    const tl = gsap.timeline();
32

33
    tl.from(nextSlideImgs, {
34
      clipPath: clipPathOptions[randomClipPathOption]
35
    }).from(
36
      nextSlideText,
37
      {
38
        opacity: 0,
39
        duration: 0.15
40
      },
41
      "-=0.5"
42
    );
43
  }
44
}

Adicionar suporte de teclado

Apenas para melhorar a funcionalidade do nosso slideshow, adicionaremos suporte para navegação por teclado. Dito isso, cada vez que o esquerda () ou certo () teclas de seta forem pressionados, acionaremos um clique para as setas de navegação anterior e seguinte, respectivamente.

Aqui está o código relevante:

1
document.addEventListener("keyup", (e) => {
2
  console.log(e.key);
3
  if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
4
    // left arrow

5
    if (e.key === "ArrowLeft") {
6
      btnArrowPrev.click();
7
    } else {
8
      // right arrow

9
      btnArrowNext.click();
10
    }
11
  }
12
});

Conclusão

Pronto! Durante este tutorial, fomos realmente criativos e aprendemos a construir uma apresentação de slides GSAP animada cujos slides consistem em diferentes layouts assimétricos exclusivos.

Espero que você tenha gostado da demonstração resultante e a use como inspiração para criar suas próprias apresentações de slides em JavaScript com grade quebrada. 🙏.

Como sempre, muito obrigado pela leitura!

Deixe uma resposta