Gallery designs are ubiquitous across websites, especially in creative portfolios and agency presentations. In this tutorial, I'm going to guide you through creating a dynamic, endlessly scrolling image gallery. Each image in this assembly will move at varying speeds, generating a captivating floating illusion. When a viewer selects an item, it expands from its grid placement to a detailed view.

This guide is segmented into three distinct parts:

  • Slider (Scroller): An infinite scrolling mechanic controlled by mouse and touch interactions, enhanced with individual parallax effects.
  • Reveal: Images smoothly fade in as they transition into the viewport to prevent any jarring appearances.
  • Transition: A fluid morphing effect that enlarges a selected item into the meticulous detail view.

Throughout the tutorial, pure GSAP and standard JavaScript will be our tools of choice. Key GSAP features include the Observer for handling inputs, Flip for the transition morphing, and SplitText for revealing text effects.

1. Templating

HTML Structure

Let’s kick things off by structuring our gallery as follows:

<div class="gallery">
  <figure class="gallery__slide">
    <div class="gallery__img-wrapper">
      <img class="gallery__img" src="..." alt="Cliffs overlooking a deep blue bay" />
    </div>
    <figcaption>Above the Cove</figcaption>
  </figure>
  <!-- Additional slides go here -->
</div>

The detail view will reside in an overlay, initially concealed and revealed upon item selection:

<div class="content">
  <div class="content-wrapper">
    <figure class="content__preview-img"><img alt="" /></figure>
    <div class="content__group-list">
      <button class="content__back" type="button">Back (Esc)</button>
      <div class="content__group" data-index="0">
        <div class="content__title">Above the Cove</div>
        <div class="content__description">...</div>
      </div>
      <!-- Additional groups for each slide -->
    </div>
  </div>
</div>

Each content group corresponds to a gallery slide, aligned via the data-index attribute. When a thumbnail is clicked, it becomes the highlighted item; the transition morphs between content without altering markup, focusing solely on visibility and the image source for the preview. The key element, <figure class="content__preview-img">, is the destination for every thumbnail’s transition.

Styling

Next, we'll address the CSS. We'll disable the native scroll functionality to facilitate our design and ensure each item is appropriately scattered:

html, body {
  overflow: hidden; /* Disable native scroll */
  overscroll-behavior: none;
  touch-action: none;
}

.gallery {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12vh; 
  padding-top: 14vh; 
}

.gallery__slide {
  transform: translateX(var(--stagger, 0vw));
}

.gallery__slide:nth-child(1) { --stagger: -24vw; --img-w: 190px; }
.gallery__slide:nth-child(2) { --stagger:  14vw; --img-w: 260px; }
/* Continue for additional slides */

.gallery__img-wrapper {
  width: var(--img-w, 200px);
  aspect-ratio: 0.8;
  overflow: hidden;
}

Setting the custom properties for gallery__slide, including --stagger to position items off-center and --img-w for width, will allow us to control visual spacing through CSS. This inclusion ensures that on resizing, we can reset parameters dynamically through JavaScript without losing the original transforms.