Creating scroll snapping blocks with GSAP v3 animations

In this article we learn to implement scroll-snap with CSS and add animation with GSAP

4 min read
by Mees Rutten | Wed Jul 01 2020

Setting up some HTML

So firstly I want to say something about using a lot of div elements.
Stop doing that.
Always try to correctly use semantic elements introduced in HTML5.

1<main>
2 <article>
3 <h2>So...</h2>
4 <h3>This is a story all about how</h3>
5 </article>
6 <article>
7 <h2>The web got flipped, turned upside down</h2>
8 </article>
9 <article>
10 <h2>And i'd like to take a minute and sit right there</h2>
11 </article>
12 <article>
13 <h2>Read my blog, kthnxbye</h2>
14 <h3><a href="https://webanimation.blog">webanimation.blog</a></h3>
15 </article>
16</main>

Adding some CSS

To enable the CSS property scroll-snap we need to limit the height of the container to a certain value, in this case: 100vh. Afterwards we add overflow-y: auto; to allow scrolling in the container.

Pro tip: Prefer "auto" as the overflow value over "scroll" to remove unwanted scrollbars on Linux machines.

The article element are the blocks we want to browser to snap to. We add scroll-snap-align: start; to enable snapping to the top of the container.

All the other code is pretty much just making things look better.

1main {
2 scroll-snap-type: y mandatory;
3 position: relative;
4 max-height: 100vh;
5 overflow-y: auto;
6}
7
8article {
9 height: 100vh;
10 width: 100vw;
11
12 display: flex;
13 flex-flow: column nowrap;
14 justify-content: center;
15 align-items: center;
16
17 scroll-snap-align: start;
18}
19
20article:nth-of-type(1) {
21 background-color: rgba(0, 82, 178, 1);
22}
23article:nth-of-type(2) {
24 background-color: rgba(255, 164, 0, 1);
25}
26article:nth-of-type(3) {
27 background-color: rgba(144, 195, 255, 1);
28}
29article:nth-of-type(4) {
30 background-color: rgba(255, 173, 25, 1);
31}
32
33h2,
34h3,
35a {
36 font: 400 3rem/1.5 sans-serif;
37 color: white;
38 transform-origin: center;
39 text-decoration: none;
40}
41
42h3 {
43 font: 400 2em/1.5 sans-serif;
44}

Intersection Observer

The Intersection Observer allows you to, no shit, observe intersections.

1// Elements you want to track
2const blocks = document.querySelectorAll('main article');
3
4const blocksObserver = new IntersectionObserver(
5 entries => {
6 // entries === elements
7 return entries.forEach(event => {
8 // destructures the values we want to check for in the animateVisible function
9 const { target, intersectionRatio, isIntersecting } = event;
10 // Call function when an intersection triggers.
11 animateVisible(target, intersectionRatio, isIntersecting);
12 });
13 },
14 // threshold means the percentage between 0-1 (0 = 0, 1 = 100) when we want the intersection callback to trigger.
15 // This Intersection Observer triggers when the element is in view for at least half of it's height.
16 { threshold: 0.5 }
17);
18
19for (const block of blocks) {
20 blocksObserver.observe(block);
21}

Animating in view with GSAP

Get GSAP here!

1// Set all children of the article to invisible (autoAlpha is a mix between CSS properties: opacity and visibility) and downward for 1rem
2// Note: * should be avoided because it can cause performance issues, but for this example it was fine.
3gsap.set('main article *', { autoAlpha: 0, y: '1rem' });
4
5// Is triggered in the Intersection Bbserver
6const animateVisible = (block, ratio, isIntersecting) => {
7 // When the element is in view:
8 if (ratio > 0 && isIntersecting) {
9 // Animate all the children of this element
10 gsap.to(block.querySelectorAll('*'), {
11 // duration is one second
12 duration: 1,
13 // animate back to visible
14 autoAlpha: 1,
15 // animate back to top
16 y: '0',
17 // stagger will make all animations fire 0.3 seconds after each other.
18 stagger: 0.3,
19 // Ease In Out: accelerates --> decelerates
20 ease: 'power3.inOut',
21 });
22 } else {
23 // When the element is not in view anymore we set it to invisible and downward for 1rem (16px usually)
24 gsap.set(block.querySelectorAll('*'), {
25 autoAlpha: 0,
26 y: '1rem',
27 });
28 }
29};

Final demo


Thanks for reading!

I hope someone somewhere learned something via this post! If you did, please consider sharing the article.

by @

All rights reserved