Como fazer seções de rolagem de encaixe com CSS Scroll-Snap (alternativa fullpage.js)

Publicados: 2022-07-19

Neste tutorial, vamos fazer uma página que se encaixa de uma seção de tela cheia para outra conforme o usuário rola. Esse efeito foi popularizado pela biblioteca fullpage.js . Mas agora, existem opções melhores devido a alterações de licenciamento para fullpage (agora você precisa pagar por isso), novo módulo de snap-snap de CSS e suporte completo ao navegador da API do observador de interseção.

Portanto, se você estiver procurando por uma alternativa fullpage.js, talvez nem precise de uma biblioteca inteira, basta codificá-la!

Na verdade, o efeito de encaixe é 100% CSS. JS é usado apenas para desvanecer elementos quando a tela "encaixa" na visualização.

Usaremos esta "demonstração de snap de rolagem do Dinosaur Park" de Michelle Barker disponível no CodePen para inspiração:

https://codepen.io/michellebarker/pen/PowYKXJ Veja a demonstração do snap de rolagem do Pen Dinosaur Park por Michelle Barker (@michellebarker) no CodePen.

Atenção, você provavelmente desejará ver isso em uma tela maior para experimentar totalmente o efeito "scroll snap".

Usando CSS junto com o Observador de Interseção, faremos cada seção de tela cheia da página de rolagem instantânea e desvanecer o conteúdo quando ele entrar na janela de visualização.

Melhor ainda, são apenas ~30 linhas de código JS! As animações são feitas com CSS; tudo o que precisamos fazer é entender quando as seções e o conteúdo entram em nossa viewport com JS.

1) Construa sua página

Primeiramente, será necessário construir uma página, ou configurar uma já existente para ser compatível com este simples efeito. Há duas coisas a serem observadas aqui:

Estamos usando um elemento <main> , que contém nossas seções. Este elemento principal é 100vh, assim como cada um dos elementos <section> dentro dele.

HTML:

<main> <section class="section section-1"> <div class="section__content" data-content >Conteúdo dentro de</div> </section> <section class="section section-2"> <div class= "section__content" data-content >Conteúdo dentro</div> </section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">Explorar e descobrir</h3 > </header> <div class="section__content" data-content >Conteúdo dentro de</div> </section> </main>

CSS:

/* Comportamento de rolagem */ @media (min-height: 30em) { main { scroll-snap-type: y obrigatório; altura: 100vh; estouro-y: rolar; } } .seção { cor: branco; posição: relativa; scroll-snap-align: centro; exibição: flexível; direção flexível: coluna; min-altura: 100vh; } @media (min-height: 30em) { .section { height: 100vh; } }

2) Configure o Snapping com CSS

imagem-16-11

Aqui podemos ver que existem várias seções, cada uma com uma altura de 100vh.

O elemento <main>, também é 100vh, com overflow-y:scroll.

Para o usuário, o comportamento de rolagem é o mesmo de uma página da Web padrão.

Se você adicionou o CSS mencionado acima, o efeito de encaixe já estará em jogo. Vamos dar uma olhada em como ele funciona.

Primeiro, a consulta " @media (min-height: 30em) " desativa o efeito de ajuste para tamanhos de tela de celular muito curtos em termos de altura. Na seção principal, há uma propriedade que permite o encaixe:

main { .... scroll-snap-type: y obrigatório; .... }

Esta é uma pequena propriedade legal "define quão estritamente os pontos de encaixe são aplicados no contêiner de rolagem, caso haja um" (MDN).

E, em seguida, em nossas seções, temos:

.section { .... scroll-snap-align: center; .... }

Esta propriedade "especifica a posição de encaixe da caixa como um alinhamento de sua área de encaixe (como o assunto do alinhamento) dentro da porta de encaixe de seu contêiner de encaixe (como o contêiner de alinhamento)" (MDN).

Saiba mais sobre o módulo CSS scroll-snap aqui:

E é tão simples quanto isso. Os filhos diretos dentro do contêiner <main> (todas as seções) serão rolados para o centro do contêiner <main>. Como tanto o Main quanto o Section são 100vh, a rolagem se ajustará de seção em seção.

3) Desvanecer o conteúdo usando o Observador de interseção

Até agora, temos um efeito de rolagem de página inteira e, se você não quiser fazer mais nada, pode pular esta seção e aproveitar seu site. No entanto, quando cada seção de página inteira se encaixa na visualização, quero que o conteúdo apareça gradualmente - um efeito único e interessante (também possível com fullpage.js).

Para fazer isso, usaremos o observador de interseção para entender o posicionamento das seções dentro de nossa viewport <main>.

const seções = [...document.querySelectorAll("seção")]); let opções = { rootMargin: "0px", limite: 0,75, }; const callback = (entradas, observador) => { entry.forEach((entry) => { const { target } = entry; if (entry.intersectionRatio >= 0.75) { target.classList.add("is-visible") ; } else { target.classList.remove("is-visible"); } }); }; const observador = new IntersectionObserver(retorno de chamada, opções); section.forEach((section, index) => { const sectionChildren = [...section.querySelector("[data-content]").children]; sectionChildren.forEach((el, index) => { el.style .setProperty("--delay", `${index * 250}ms`); }); observador.observe(seção); });

Há duas coisas sendo feitas aqui. Primeiro, ele identifica quando uma seção está na viewport e, conforme ela entra, adiciona uma classe CSS .is-visible . Ao sair da viewport, ele remove essa classe.

Além disso, à medida que esta seção entra na viewport, ela consecutivamente (uma após a outra) define a propriedade de atraso para 250ms em cada um dos elementos filhos. Isso cria um efeito de fade escalonado.

Ele só faz isso para filhos dentro de elementos que têm o atributo de conteúdo de dados. Portanto, você precisa adicionar isso aos wrappers ao redor do conteúdo de suas páginas se quiser que o efeito de fade.

Agora, para fazer isso funcionar, precisamos configurar a animação fade-in para elementos que tenham a classe .is-visible adicionada.

@media (min-height: 30em) { .section__content > * { opacity: 0; transform: translate3d(0, 4rem, 0); transição: opacidade 800ms var(--delay), transformar 800ms cúbico-bezier(0,13, 0,07, 0,26, 0,99) var(--delay); } } .is-visible .section__content > * { opacidade: 1; transform: translate3d(0, 1rem, 0); } .is-visible .section__img { opacidade: 0,75; }

Se nos referirmos ao HTML da demonstração inicial, você verá que há uma div envolvendo o conteúdo com a classe de .section__content e o atributo data-content.

<main> <section class="section section-1"> <div class="section__content" data-content >Conteúdo dentro de</div> </section> <section class="section section-2"> <div class= "section__content" data-content >Conteúdo dentro</div> </section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">Explorar e descobrir</h3 > </header> <div class="section__content" data-content >Conteúdo dentro de</div> </section> </main>

Isso une nosso JS e CSS. A opacidade é 0 para conteúdo até que a classe .is-visible seja adicionada pelo observador de interseção ao entrar na viewport. Quando ele sai, ele volta para uma opacidade de 0, portanto, independentemente da sua direção de rolagem, sempre haverá um feed em vigor.

Finalmente, o delay que é aplicado consecutivamente a cada elemento filho escalona o efeito fade.

Observe a terceira seção. O cabeçalho da seção não está dentro de uma seção de conteúdo de dados, então não irá aparecer (já estará lá).

Todo o código

Aqui está a demonstração:

isotrópico-2022-07-15-at-15-00-58

Aqui está todo o código que usamos em nossa demonstração:

HTML

<main> <section class="section section-1"> <div class="section__content" data-content> <img class="section__img section__img--left" src="https://s3-us-west-2 .amazonaws.com/s.cdpn.io/85648/trex.svg" alt="T-rex" /> <h2>Conteúdo dentro</h2> <p>blá blá blá</p> </div> < /section> <section class="section section-2"> <div class="section__content" data-content> <h2>Conteúdo dentro</h2> <p>blá blá blá</p> </div> </ section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">Explorar e descobrir</h3> </header> <div class="section__content" data-content > <h2>Conteúdo dentro</h2> <p>blá blá blá</p> </div> </section> </main>

CSS

@media (min-height: 30em) { main { scroll-snap-type: y obrigatório; altura: 100vh; estouro-y: rolar; } } .seção { cor: branco; posição: relativa; scroll-snap-align: centro; exibição: flexível; direção flexível: coluna; min-altura: 100vh; } @media (min-height: 30em) { .section { height: 100vh; } } @media (min-height: 30em) { .section__content > * { opacity: 0; transform: translate3d(0, 4rem, 0); transição: opacidade 800ms var(--delay), transformar 800ms cúbico-bezier(0,13, 0,07, 0,26, 0,99) var(--delay); } } .is-visible .section__content > * { opacidade: 1; transform: translate3d(0, 1rem, 0); } .is-visible .section__img { opacidade: 0,75; }

JS

const seções = [...document.querySelectorAll("seção")]); let opções = { rootMargin: "0px", limite: 0,75, }; const callback = (entradas, observador) => { entry.forEach((entry) => { const { target } = entry; if (entry.intersectionRatio >= 0.75) { target.classList.add("is-visible") ; } else { target.classList.remove("is-visible"); } }); }; const observador = new IntersectionObserver(retorno de chamada, opções); section.forEach((section, index) => { const sectionChildren = [...section.querySelector("[data-content]").children]; sectionChildren.forEach((el, index) => { el.style .setProperty("--delay", `${index * 250}ms`); }); observador.observe(seção); });

Conclusão

Esta é a alternativa fullpage.js perfeita que não para apenas no encaixe entre as seções de tela cheia. Também usamos a API do observador de interseção para entender quando uma seção entra na página e, em seguida, aplicamos dinamicamente uma animação escalonada. Você pode usar este exemplo básico para construir e criar alguns efeitos muito legais com o mínimo de JS e CSS de baunilha.