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.
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 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
E mais especificamente, a marcação dentro de cada slide ficará assim:
1 |
|
2 |
width="" height="" src="IMG_URL" alt="">
|
3 |
|
4 |
|
5 |
width="" height="" src="IMG_URL" alt="">
|
6 |
|
7 |
|
8 |
width="" height="" src="IMG_URL" alt="">
|
9 |
|
10 |
|
11 |
width="" height="" src="IMG_URL" alt="">
|
12 |
|
13 |
|
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.
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:
- Pegue o slide ativo e seus itens.
- 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.
- 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.
- 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!