I’m trying to mimic the an effect where cards unstack on scroll. For visuals, please click here to view a GIF of the effect.
The website the above GIF is from is this website.
Now, I’ve tried to mimic the above using GSAP / ScrollTrigger. However, my effect differs in the following aspects:
- The cards stack on top of each other in my demo, but reveal themselves in the demo I’m trying to mimic (see image of the design I’m trying to achieve below). I’ve tried
z-indexing
but this didn’t do the trick. - The cards pin to the top of the page, whereas I’m looking for it to be centered.
- It doesn’t unpin when you’ve passed
.cardStacking
(carries on until the end of the page)
Demo (view on 1200px +)
$(function() {
const cards = gsap.utils.toArray(".stackCard");
cards.forEach((card, index) => {
const tween = gsap.to(card, {
scrollTrigger: {
trigger: card,
start: () => `top bottom-=100`,
end: () => `top top+=40`,
scrub: true,
markers: true,
invalidateOnRefresh: true
},
ease: "none",
scale: () => 1 - (cards.length - index) * 0.025
});
ScrollTrigger.create({
trigger: card,
start: "top top",
pin: true,
pinSpacing: false,
markers: true,
id: 'pin',
end: 'max',
//end: '.cardStacking',
invalidateOnRefresh: true,
});
});
});
:root {
--navy: #0E185F;
--white: #FFFFFF;
}
.background--navy {
background-color: var(--navy);
}
.color--white {
color: var(--white);
}
.spacer {
height: 2000px;
}
.cardStacking {
padding: 120px 0 141px 0;
/*********/
}
.cardStacking__intro {
margin-bottom: 100px;
}
.cardStacking .stackCard {
border-radius: 40px;
background: linear-gradient(90deg, #c7defe 0%, #e7e7f2 100%);
margin-bottom: 50px;
padding: 106px 135px 126px 77px;
/* CONTENT */
}
.cardStacking .stackCard:first-child {
box-shadow: 0px 10px 30px 0px rgba(0, 0, 0, 0.16);
}
.cardStacking .stackCard__content-header {
margin-bottom: 10px;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js"></script>
<div class="spacer"></div>
<section class="cardStacking background--navy">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-7">
<div class="cardStacking__intro text-center">
<h2 class="cardStacking__intro-header color--white">LOREM IPSUM DOLOR SIT AMET CONSETETUR SADIPSCING</h2>
<div class="cardStacking__intro-copy color--white">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.</div>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-12 col-md-10">
<div class="cardStacking__cards">
<!------------>
<!-- CARD 1 -->
<!------------>
<div class="stackCard">
<div class="stackCard__content">
<span class="stackCard__content-header d-block">Header</span>
<div class="stackCard__content-copy">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.</div>
</div>
</div>
<!------------>
<!-- CARD 2 -->
<!------------>
<div class="stackCard">
<div class="stackCard__content">
<span class="stackCard__content-header d-block">Header 2</span>
<div class="stackCard__content-copy">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.</div>
</div>
</div>
<!------------>
<!-- CARD 3 -->
<!------------>
<div class="stackCard">
<div class="stackCard__content">
<span class="stackCard__content-header d-block">Header 3</span>
<div class="stackCard__content-copy">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="spacer"></div>
Edit:
I have managed to somewhat resolve items 1 and 3 in my above list.
However, the functionality for item 1 isn’t quite there yet. I do not know why the cards are not stacked upon each other and are not centered.
See updated demo here:
$(function() {
const container = document.querySelector(".cardStacking__cards");
const card = document.querySelector(".stackCard");
const cards = document.querySelectorAll(".stackCard");
const height = 500;
const timeline = gsap.timeline({
scrollTrigger: {
trigger: container,
pin: true,
markers: true,
scrub: 1,
start: "bottom-=10% center",
end: "bottom top"
}
});
timeline.from(card, {
y: (index) => height * (cards.length - (index + 1)),
duration: (index) => 0.6 / (index + 1),
ease: "none",
stagger: (index) => 0.3 * (index),
});
});
:root {
--navy: #0E185F;
--white: #FFFFFF;
}
.background--navy {
background-color: var(--navy);
}
.color--white {
color: var(--white);
}
.spacer {
height: 2000px;
}
.cardStacking {
padding: 120px 0 141px 0;
/*********/
}
.cardStacking__intro {
margin-bottom: 100px;
}
.cardStacking .stackCard {
border-radius: 40px;
background: linear-gradient(90deg, #c7defe 0%, #e7e7f2 100%);
margin-bottom: 50px;
padding: 106px 135px 126px 77px;
/* CONTENT */
}
.cardStacking .stackCard:first-child {
box-shadow: 0px 10px 30px 0px rgba(0, 0, 0, 0.16);
}
.cardStacking .stackCard__content-header {
margin-bottom: 10px;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js"></script>
<div class="spacer"></div>
<section class="cardStacking background--navy">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-7">
<div class="cardStacking__intro text-center">
<h2 class="cardStacking__intro-header color--white">LOREM IPSUM DOLOR SIT AMET CONSETETUR SADIPSCING</h2>
<div class="cardStacking__intro-copy color--white">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.</div>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-12 col-md-10">
<div class="cardStacking__cards">
<!------------>
<!-- CARD 1 -->
<!------------>
<div class="stackCard" style="z-index: 0;">
<div class="stackCard__content">
<span class="stackCard__content-header d-block">Header</span>
<div class="stackCard__content-copy">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.</div>
</div>
</div>
<!------------>
<!-- CARD 2 -->
<!------------>
<div class="stackCard" style="z-index: -1;">
<div class="stackCard__content">
<span class="stackCard__content-header d-block">Header 2</span>
<div class="stackCard__content-copy">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.</div>
</div>
</div>
<!------------>
<!-- CARD 3 -->
<!------------>
<div class="stackCard" style="z-index: -2;">
<div class="stackCard__content">
<span class="stackCard__content-header d-block">Header 3</span>
<div class="stackCard__content-copy">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus.</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="spacer"></div>
2
Answers
In order to reproduce this effect you need to create 2 scroll effects.
First the stage with the stacked cards is in reality as high as if the cards were not stacked. Then when scrolling over that stage, we pin the whole content of the stage to the top of the screen:
In addition to that, we preserve the original position of each card when we initialize the stack. By doing so we can now calculate when we would reach each card and start repositioning the card when scrolling further down.
The animation does not work properly in this snippet. I am not 100 percent sure why. It works in this jsfiddle. And I tested it locally in every major browser.
There is obviously room for improvement. The animation is a little bit abrupt in the beginning, and it is obviously not responsive due to missing media queries, but I think the concept is clear enough to work with it.
why use GASP when you can just use css
credit to : Jquery translate elements based on scroll