Comment créer des sections de défilement d'accrochage avec CSS Scroll-Snap (alternative à fullpage.js)

Publié: 2022-07-19

Dans ce didacticiel, nous allons créer une page qui passe d'une section plein écran à une autre au fur et à mesure que l'utilisateur fait défiler. Cet effet a été popularisé par la bibliothèque fullpage.js . Mais maintenant, il existe de meilleures options en raison des changements de licence en pleine page (vous devez maintenant payer pour cela), du nouveau module CSS scroll-snap et de la prise en charge complète du navigateur de l'API d'observateur d'intersection.

Donc, si vous cherchez une alternative à fullpage.js, vous n'aurez peut-être même pas besoin d'une bibliothèque entière, vous pouvez simplement la coder vous-même !

En fait, l'effet d'accrochage est 100% CSS. JS est simplement utilisé pour faire apparaître des éléments lorsque l'écran "s'enclenche" dans la vue.

Nous utiliserons cette "Dinosaur Park scroll snap demo" de Michelle Barker disponible sur CodePen pour nous inspirer :

https://codepen.io/michellebarker/pen/PowYKXJ Voir la démo du Pen Dinosaur Park par Michelle Barker (@michellebarker) sur CodePen.

Attention, vous souhaiterez probablement afficher cela sur un écran plus grand pour profiter pleinement de l'effet "scroll snap".

En utilisant CSS couplé à Intersection Observer, nous allons faire défiler chaque section plein écran de la page et faire apparaître le contenu en fondu lorsqu'il entre dans la fenêtre d'affichage.

Encore mieux, c'est seulement ~30 lignes de code JS ! Les animations sont faites avec CSS; tout ce que nous devons faire est de comprendre quand les sections et le contenu entrent dans notre fenêtre avec JS.

1) Créez votre page

Tout d'abord, il faudra créer une page, ou en configurer une existante pour qu'elle soit compatible avec ce simple effet. Il y a deux choses à noter ici :

Nous utilisons un élément <main> , qui contient nos sections. Cet élément principal est 100vh, tout comme chacun des éléments <section> qu'il contient.

HTML :

<main> <section class="section section-1"> <div class="section__content" data-content >Contenu à l'intérieur</div> </section> <section class="section section-2"> <div class= "section__content" data-content >Contenu à l'intérieur</div> </section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">Explorer et découvrir</h3 > </header> <div class="section__content" data-content >Contenu à l'intérieur</div> </section> </main>

CSS :

/* Comportement de défilement */ @media (min-height : 30em) { main { scroll-snap-type : y obligatoire ; hauteur : 100vh ; débordement-y : défilement ; } } .section { couleur : blanc ; position : relative ; scroll-snap-align : centre ; affichage : flexible ; flex-direction : colonne ; hauteur min : 100vh ; } @media (min-height : 30em) { .section { height : 100vh ; } }

2) Configurer l'accrochage avec CSS

image-16-11

Ici, nous pouvons voir qu'il y a plusieurs sections, chacune avec une hauteur de 100vh.

L'élément <main> vaut également 100vh, avec overflow-y:scroll.

Pour l'utilisateur, le comportement de défilement est le même qu'une page Web par défaut.

Si vous avez ajouté le CSS mentionné ci-dessus, l'effet d'accrochage sera déjà en jeu. Voyons comment cela fonctionne.

Tout d'abord, la requête " @media (min-height: 30em) " désactive l'effet d'accrochage pour les tailles d'écran mobiles trop courtes en termes de hauteur. Sous la section principale, il y a une propriété qui active la capture :

main { .... scroll-snap-type: y obligatoire; .... }

Il s'agit d'une petite propriété sympa "définissant la rigueur avec laquelle les points d'accrochage sont appliqués sur le conteneur de défilement s'il y en a un" (MDN).

Et puis sous nos rubriques, nous avons :

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

Cette propriété "spécifie la position d'accrochage de la boîte comme un alignement de sa zone d'accrochage (en tant que sujet d'alignement) dans le port d'accrochage de son conteneur d'accrochage (en tant que conteneur d'alignement)" (MDN).

En savoir plus sur le module CSS scroll-snap ici :

Et c'est aussi simple que ça. Les enfants directs dans le conteneur <main> (toutes les sections) défileront au centre du conteneur <main>. Étant donné que le principal et la section sont à 100vh, le défilement s'enclenchera d'une section à l'autre.

3) Fondu du contenu à l'aide d'Intersection Observer

À présent, nous avons un effet de défilement de page entière, et si vous ne voulez rien faire d'autre, vous pouvez ignorer cette section et profiter de votre site Web. Cependant, lorsque chaque section de page complète s'affiche, je souhaite faire apparaître le contenu à l'intérieur - un effet unique et sympa (également possible avec fullpage.js).

Pour ce faire, nous utiliserons l'observateur d'intersection pour comprendre le positionnement des sections dans notre fenêtre <main>.

const sections = [...document.querySelectorAll("section")] ; let options = { rootMargin : "0px", seuil : 0,75, } ; const callback = (entrées, observateur) => { entrées.forEach((entrée) => { const { cible } = entrée ; si (entrée.intersectionRatio >= 0,75) { cible.classList.add("is-visible") ; } else { target.classList.remove("is-visible"); } }); } ; const observer = new IntersectionObserver (rappel, options); sections.forEach((section, index) => { const sectionChildren = [...section.querySelector("[data-content]").children]; sectionChildren.forEach((el, index) => { el.style .setProperty("--delay", `${index * 250}ms`); }); observer.observe(section); });

Il y a deux choses qui sont faites ici. Tout d'abord, il identifie le moment où une section se trouve dans la fenêtre d'affichage et, à son entrée, ajoute une classe CSS .is-visible . Lorsqu'il quitte la fenêtre d'affichage, il supprime cette classe.

De plus, lorsque cette section entre dans la fenêtre d'affichage, elle définit consécutivement (l'une après l'autre) la propriété de délai de 250 ms sur chacun des éléments enfants. Cela donne un effet de fondu échelonné.

Il ne le fait qu'aux enfants dans les éléments qui ont l'attribut de contenu de données. Vous devez donc l'ajouter aux enveloppes autour du contenu de vos pages si vous voulez que le fondu s'applique.

Maintenant, pour que cela fonctionne, nous devons configurer l'animation de fondu enchaîné pour les éléments auxquels la classe .is-visible a été ajoutée.

@media (min-height : 30em) { .section__content > * { opacity : 0 ; transformer : translate3d(0, 4rem, 0); transition : opacité 800 ms var(--delay), transformation 800 ms cube-bezier(0,13, 0,07, 0,26, 0,99) var(--delay) ; } } .is-visible .section__content > * { opacité : 1 ; transformer : translate3d(0, 1rem, 0); } .is-visible .section__img { opacité : 0,75 ; }

Si nous nous référons au HTML depuis le début de la démo, vous verrez qu'il y a un div entourant le contenu avec la classe .section__content et l'attribut data-content.

<main> <section class="section section-1"> <div class="section__content" data-content >Contenu à l'intérieur</div> </section> <section class="section section-2"> <div class= "section__content" data-content >Contenu à l'intérieur</div> </section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">Explorer et découvrir</h3 > </header> <div class="section__content" data-content >Contenu à l'intérieur</div> </section> </main>

Cela relie notre JS et CSS ensemble. L'opacité est de 0 pour le contenu jusqu'à ce que la classe .is-visible soit ajoutée par l'observateur d'intersection lorsqu'elle entre dans la fenêtre. Quand il part, il revient à une opacité de 0, donc quel que soit votre sens de défilement, il y aura toujours un flux en vigueur.

Enfin, le delay qui est appliqué consécutivement à chaque élément enfant échelonne l'effet de fondu en cours.

Notez la troisième section. L'en-tête de section n'est pas dans une section de contenu de données, il ne s'estompera donc pas (sera déjà là).

Tout le code

Voici la démo :

isotrope-2022-07-15-au-15-00-58

Voici tout le code que nous avons utilisé dans notre démo :

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>Contenu à l'intérieur</h2> <p>bla bla bla</p> </div> < /section> <section class="section section-2"> <div class="section__content" data-content> <h2>Contenu à l'intérieur</h2> <p>bla bla bla</p> </div> </ section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">Explorer et découvrir</h3> </header> <div class="section__content" data-content > <h2>Contenu à l'intérieur</h2> <p>bla bla bla</p> </div> </section> </main>

CSS

@media (min-height : 30em) { main { scroll-snap-type : y obligatoire ; hauteur : 100vh ; débordement-y : défilement ; } } .section { couleur : blanc ; position : relative ; scroll-snap-align : centre ; affichage : flexible ; flex-direction : colonne ; hauteur min : 100vh ; } @media (min-height : 30em) { .section { height : 100vh ; } } @media (min-height : 30em) { .section__content > * { opacity : 0 ; transformer : translate3d(0, 4rem, 0); transition : opacité 800 ms var(--delay), transformation 800 ms cube-bezier(0,13, 0,07, 0,26, 0,99) var(--delay) ; } } .is-visible .section__content > * { opacité : 1 ; transformer : translate3d(0, 1rem, 0); } .is-visible .section__img { opacité : 0,75 ; }

JS

const sections = [...document.querySelectorAll("section")] ; let options = { rootMargin : "0px", seuil : 0,75, } ; const callback = (entrées, observateur) => { entrées.forEach((entrée) => { const { cible } = entrée ; si (entrée.intersectionRatio >= 0,75) { cible.classList.add("is-visible") ; } else { target.classList.remove("is-visible"); } }); } ; const observer = new IntersectionObserver(callback, options); sections.forEach((section, index) => { const sectionChildren = [...section.querySelector("[data-content]").children]; sectionChildren.forEach((el, index) => { el.style .setProperty("--delay", `${index * 250}ms`); }); observer.observe(section); });

Conclusion

C'est l'alternative parfaite à fullpage.js qui ne s'arrête pas à la capture entre les sections plein écran. Nous avons également utilisé l'API intersection observer pour comprendre quand une section entre dans la page, puis appliquer dynamiquement une animation décalée. Vous pouvez utiliser cet exemple de base pour construire et créer des effets vraiment sympas avec un minimum de JS et CSS vanille.