Freddy
Freddy

Reputation: 867

Implementing cards stacking animation on scroll with Gsap and ScrollTrigger

I have a module where 3 cards stack on top of each other and then, on scroll, the cards will unstack one by one. See GIF of desired effect here.

I have tried to emulate the above effect using GSAP and ScrollTrigger, however, my animation is yielding undesirable results.

Here is my code so fare, to be viewed on +1200px screens:

$(function() {
  
  gsap.registerPlugin(ScrollTrigger);

  const container = document.querySelector(".cardStacking__cards");
  const card = document.querySelector(".stackCard");
  const cards = document.querySelectorAll(".stackCard");
  const height = 510;

  const timeline = gsap.timeline({
    scrollTrigger: {
      trigger: ".cardStacking__cards",
      pin: true,
      markers: true,
      scrub: 1,
      start: "bottom-=10% center",
      end: "bottom top"
    }
  });

  timeline.from(".stackCard", {
    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>

Issue(s)

  1. From the demo you can see that, as soon as the "scroller start" indicator scrolls to and past the "start" indicator, the cards disappear. Upon inspecting the code, for some reason, when the indicators pass, cardStacking__cards has a top value of top: -1334.52px, which I'm unsure why.

  2. The section does not pin when the cards are in view. I'm trying to pin the section until all cards are revealed, and then unpin.

Upvotes: 4

Views: 3139

Answers (2)

Taras
Taras

Reputation: 1145

Please take a look at working example https://jsfiddle.net/23dz0evy/4/. By modifying const bottom = 40 bottom margin between panels can be changed, works with infinite number of panels.

Upvotes: 1

Youssouf Oumar
Youssouf Oumar

Reputation: 46281

You can do it as below. I created a simplified version of the code you pasted to showcase the feature. Here is a codepen of it as well.

gsap.registerPlugin(ScrollTrigger);
const tl = gsap.timeline({
  scrollTrigger: {
    trigger: ".cards",
    pin: true,
    start: "top 10%",
    end: "bottom+=1000 bottom",
    scrub: true
  }
});

tl.to(".first", {
  y: "-90vh"
}).to(
  ".second",
  {
    y: "-85vh"
  },
  "-=0.15"
);
body {
  text-align: center;
}
.cards {
  position: relative;
  height: 85vh;
}
.card {
  border-radius: 2rem;
  position: absolute;
  inset: 0;
}

.first {
  background: lightblue;
  bottom: 10vh;
}
.second {
  background: lightgreen;
  bottom: 5vh;
}
.third {
  background: lightgray;
}
<script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js"></script>
<script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script>
<div>
  <h1>Scroll to see magic</h1>
  <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. </p>
</div>
<div class="cards">
  <div class="card third"></div>
  <div class="card second"></div>
  <div class="card first"></div>
</div>
<div>
  <h1>Scroll to see magic</h1>
  <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. </p>
</div>

Upvotes: 3

Related Questions