Site icon Puneet Sharma – Freelance Web Developer

Pinned snapping without reverse snapping issue:

Pinned snapping without reverse snapping issue:

Pinned snapping without reverse snapping issue:

There is an issue with the snap function of the scroll trigger such that – if the user does not scroll enough as a threshold, the section has a bouncy effect and gets auto-snapped to the original position, To make forward snapping always work follow the following demo and code below.

DEMO

See the Pen GSAP Code To Move Up slides on scroll by Puneet Sharma (@webdevpuneet) on CodePen.

HTML

<div class="container">
  <div class="section section1">
    <h1>Section 1</h1>
  </div>
  <div class="section section2">
    <div class="slide slide1 active">
      <h1>Pinned Slide 1</h1>
    </div>
    <div class="slide slide2">
      <h1>Pinned Slide 2</h1>
    </div>
    <div class="slide slide3">
      <h1>Pinned Slide 3</h1>
    </div>
    <div class="slide slide4">
      <h1>Pinned Slide 4</h1>
    </div>
  </div>
  <div class="section section3">
    <h1>Section 3</h1>
  </div>
  <div class="section section4">
    <h1>Section 4</h1>
  </div>
</div>

CSS

body,
html {
  margin: 0;
  padding: 0;
}
body * {
  box-sizing: border-box;
}
.section2 {
  height: 100vh;
  overflow: hidden;
}
.section2 .slide {
  height: 100vh;
  width: 100%;
  position: absolute;
  background: blue;
}
.section2 .slide.active {
  z-index: 1;
}
.section1,
.section3,
.section4 {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.section1 {
  background: green;
}
.section2 {
  background: white;
}
.section3 {
  background: green;
}
.section4 {
  background: green;
}

JS

gsap.registerPlugin(ScrollTrigger);

const slides = document.querySelectorAll(".section2 .slide");
const section2 = document.querySelector(".section2");
const slideCount = slides.length;
let currentIndex = 0;
let isAnimating = false;

// Function to update active slide
const updateActiveSlide = (index) => {
  slides.forEach((slide, i) => slide.classList.toggle("active", i === index));
};

// Function to transition to a specific slide
const goToSlide = (index) => {
  if (isAnimating || index < 0 || index >= slideCount) return;

  isAnimating = true;
  currentIndex = index;

  gsap.to(slides, {
    duration: 0.5,
    ease: "power2.inOut",
    onComplete: () => (isAnimating = false)
  });

  updateActiveSlide(index);
};

// ScrollTrigger to pin .section2 and enable slide transitions
let snap;
ScrollTrigger.create({
  trigger: section2,
  start: "top top",
  end: () => `+=${window.innerHeight * slideCount}`,
  pin: true,
  scrub: false,
  snap: {
    snapTo: (progress, self) => snap(progress, self.direction),
    duration: { min: 0.3, max: 0.6 },
    ease: "power2.inOut"
  },
  onUpdate: (self) => {
    if (!isAnimating) {
      let newIndex = Math.round(self.progress * (slideCount - 1));
      if (newIndex !== currentIndex) {
        goToSlide(newIndex);
      }
    }
  }
});

// Create a directional snapping function
ScrollTrigger.addEventListener("refresh", () => {
  let sectionPositions = Array.from(
    { length: slideCount },
    (_, i) => i / (slideCount - 1)
  );
  snap = ScrollTrigger.snapDirectional(sectionPositions);
});

Exit mobile version