Utilizing GSAP for crafting a dynamic horizontal scrolling section

I am currently working on creating a unique section that transforms into a horizontal scroller once the items (in this case, images) are visible, and then reverts to a vertical scroller once all the items have been scrolled through.

My inspiration and adaptation for this approach came from the following demonstrations:

The visual presentation I aim to achieve is displaying two images initially and then enabling scrolling to reveal the rest. Refer to the mockup below:

https://i.sstatic.net/zZjVq.png

This is my current approach:

$(function() {

  let scroll_tl = gsap.timeline({
    scrollTrigger: {
      trigger: '.horizontalScroller__images',
      start: "top center",
   
      scrub: true,
      end: "+=300",
      
    }
  });

  var images = document.querySelectorAll('.horizontalScroller__item');

  scroll_tl.to('.horizontalScroller__intro', {
    duration: 1,
    ease: "slow"
  });

  scroll_tl.to(images, {
    xPercent: -85 * (images.length - 1),
    scrollTrigger: {
      trigger: ".horizontalScroller__images",
      start: "center center",
      pin: true,
    
      markers: true,
      scrub: 1,
      snap: 1 / (images.length - 1),
    
    }
  });

});
.spacer{
  height: 300vh;
  background: lightblue;
}

.horizontalScroller {
  padding: 114px 0 80px 0;
  height: 100vh;
  position: relative;
  background-color: #5d209f;
}
.horizontalScroller__intro {
  margin-bottom: 25px;
  color: #FFFFFF;
}
.horizontalScroller__header {
  margin-bottom: 17px;
}
.horizontalScroller__images {
  display: flex;
  justify-content: center;
  align-items: center;
}
.horizontalScroller__scroll {
  position: relative;
  overflow: hidden;
  padding: 60px 0;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/ScrollTrigger.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bddfd2d2c9cec9cfdccdfd88938d938f">[email protected]</a>/dist/css/bootstrap.min.css" rel="stylesheet">

<div class="spacer"> Scroll down </div>

<section class="horizontalScroller">

  <div class="container">
    <div class="row">
      <div class="col-12 col-md-8 col-xl-6">
        <div class="horizontalScroller__intro">
          <h2 class="horizontalScroller__header">This is the header</h2>
          <p class="horizontalScroller__standfirst mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
      </div>
    </div>
  </div>

  <div class="horizontalScroller__scroll">

    <div class="horizontalScroller__images" id="horizontal-scroll">
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
    </div>

  </div>

</section>

While testing the above demo, I noticed that the images initially move downward (which is not the intended behavior) before scrolling to the left (instead of right for horizontal scroll).

Additionally, in an attempt to display the two cards at the beginning, setting a width to the .horizontalScroller__item caused further issues.

If you have any insights on why my code is behaving in this manner, I'd appreciate your input.

Answer №1

To create a scrolling effect, utilize the outermost parent container named horizontalScroller, which should cover the entire height of the viewport:

$(function() {
  let images = gsap.utils.toArray(".horizontalScroller__item");

  gsap.to(images, {
    xPercent: -100 * (images.length - 1),
    ease: "none",
    scrollTrigger: {
      trigger: ".horizontalScroller",
      pin: true,
      scrub: 1,
      snap: 1 / (images.length - 1),
      end: () => "+=" + document.querySelector(".horizontalScroller__images").offsetWidth
    }
  });

});
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.spacer {
  height: 300vh;
  background: lightblue;
}

.horizontalScroller {
  padding: 2rem 0 1rem 0;
  height: 100vh;
  position: relative;
  background-color: #5d209f;
}

.horizontalScroller__intro {
  margin-bottom: 1.5rem;
  color: #FFFFFF;
}

.horizontalScroller__header {
  margin-bottom: 1rem;
}

.horizontalScroller__scroll {
  height: 70%;
  position: relative;
  overflow: hidden;
  padding: 2rem 0;
}

.horizontalScroller__images {
  height: 100%;
  display: flex;
  align-items: center;
}

.horizontalScroller__item {
  width: 50vw;
  height: 100%;
  display: flex;
  justify-content: center;
  flex: 0 0 auto;
}

.horizontalScroller__image {
  width: 70%;
  object-fit: fill;
  margin: 0 auto;
}

.footer {
  height: 80vh;
  width: 100%;
  background-color: yellow;
  font-size: 3rem;
}

.copyrights {
  height: 20vh;
  width: 100%;
  background-color: lightsalmon;
  font-size: 3rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/ScrollTrigger.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6f0d00001b1c1b1d0e1f2f5a415f415d">[email protected]</a>/dist/css/bootstrap.min.css" rel="stylesheet">

<div class="spacer"> Scroll down </div>

<section class="horizontalScroller">

  <div class="container">
    <div class="row">
      <div class="col-12 col-md-8 col-xl-6">
        <div class="horizontalScroller__intro">
          <h2 class="horizontalScroller__header">This is the header</h2>
          <p class="horizontalScroller__standfirst mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
      </div>
    </div>
  </div>

  <div class="horizontalScroller__scroll">

    <div class="horizontalScroller__images" id="horizontal-scroll">
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
      <div class="horizontalScroller__item">
        <img class="horizontalScroller__image" src="https://picsum.photos/200/300" alt="image">
      </div>
    </div>
  </div>
</section>
<div class="footer">Footer</div>
<div class="copyrights">Copyrights</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can you explain the significance of xs, md, and lg within the CSS Flexbox framework?

Currently in the process of developing an application using React and looking to apply some styles to my components. I stumbled upon which delves into a fluid grid system. A snippet from the example is shown below: <Row> <Col xs={12} sm={3} m ...

Struggling to generate an HTML table using JSON data

Struggling with generating an HTML table from JSON data, I'm inexperienced and having trouble with the logic. The JSON data I have needs to populate a complex dynamic HTML table. The design of the table is intricate, making it challenging for me to i ...

The image on the landing page is not properly scaling to fit the size requirements

Currently utilizing Bootstrap Studio, I incorporated the code background-size:cover. While it works well in desktop mode, it unfortunately isn't very mobile-friendly. Below are screenshots for reference: Mobile View Example Desktop View Example ...

Tips for setting up a bookmark in bootstrapvue

Hello everyone, I am currently working with bootstrapvue and I need help figuring out how to add a favorite icon. The documentation only provides icons for rating systems. I have a list of reports and I want users to be able to mark their favorites, simil ...

Choose the number that is nearest to the options given in the list

I am faced with a challenge involving a list of numbers and an input form where users can enter any number, which I want to automatically convert to the closest number from my list. My list includes random numbers such as 1, 5, 10, 12, 19, 23, 100, 400, 9 ...

What is the best way to create a dynamic JavaScript counter, like one that counts the world's population

Can someone guide me on creating a real-time JavaScript count-up feature that doesn't reset when the page reloads? Any tips or examples similar to would be much appreciated. Thank you! ...

Learn how to move to a new line when inputting data into a CSV file with JavaScript

My challenge involves taking an array of objects, for example: array=[hello, how, are, you], extracted from the document.innerHTML. I aim to write these objects to a CSV file using puppeteer and JavaScript, each on a new line. Although when using the sta ...

Leveraging JQuery and JCarousel for Smart Wrapping: Harnessing the Power of WrapInner and nth Selectors to Wrap Items at

My goal is to utilize JCarousel for presenting UL elements with LI containing a total of eight images arranged in two rows of four, resembling this structure: <li> <div> [image][image][image][image] [image][image][image][image] </div& ...

Having trouble with conditional statements in jQuery when handling ajax responses?

I am currently working with the following code: $.ajax({ url: 'upload.php', //Server script to process data type: 'POST', xhr: function() { // Custom XMLHttpRequest var myXhr = $.ajaxSettings.xhr(); if(myX ...

Creating an AI adversary for a simple Tic Tac Toe game board: a step-by-step guide

Being new to programming, I recently completed a basic Tic Tac Toe gameboard successfully. However, as I progress to the next phase of my assignment which involves implementing an AI opponent, I encountered several challenges. As a novice in programming, ...

An error has occurred in the callback function for the watcher "function () { return this._data.$$state }": "Error: [vuex] It is forbidden to modify the vuex store state outside of a mutation"

Here is a screenshot of the error I encountered in the console This is the method that I am using The issue seems to be happening in mounted I have also included MapState in the Computed section While my code is currently functional, I am puzzled by th ...

iOS Chrome: Enabling Cookies with "Always Allow"

While the Safari browser on OSX has a setting under Privacy & Security -> Block Cookies -> Always Allow, enabling the storage of entries in the browser's local storage even when accessing pages from third party sites like those running in an ifr ...

Application with menu design, similar to the layout of google.com on mobile devices

Have you seen the Android smartphone with Chrome from GOOGLE.COM? Did you notice the pop-up MENU that looks similar to an application? This menu has a "SPRING" effect, and I'm curious how Google achieved this result. I tried using the property "-web ...

Is there a way to launch QTP from JavaScript without relying on ActiveXObject?

Is there a way to call QTP from JavaScript without relying on ActiveXObject? I would appreciate any guidance on how to accomplish this task. Thanks in advance, Ramya. ...

Reveal each element individually upon clicking the button

I am trying to display 5 div elements one by one when clicking a button, but the current code is not working. I am open to alternative solutions for achieving this. Additionally, I want to change the display property from none to flex in my div element. ...

The process of combining a CssStyleCollection with a System.Web.UI.WebControls.Style

I am working with a list item and trying to apply styles using System.Web.UI.WebControls.Style. However, I noticed that there isn't a MergeStyle option similar to what's available for controls. Instead, there is an Attributes.CssStyle property of ...

Can the hexadecimal code for a particular color name be identified?

Similar Question: Javascript function to convert color names to hex codes Is there a way to determine the hexadecimal value of a named color that is currently being used by the browser? For example, I would like to achieve something similar ...

Contrast between Braces and Quotation Marks in Variable Usage

I've been experimenting with adding an initial empty value to a variable, but I'm confused about the distinctions between the following: var question = ''; var question = {}; Can someone explain the difference between using curly bra ...

Socket.io allows us to broadcast messages to all users connected to the server using the "io

I've set up a MEAN app using npm socket.io with expressjs and btford.socket-io on the client side. angular.module('myApp',['btford.socket-io']) .factory('socket',function(socketFactory){ return socketFactory() ...

Tips for testing the onEnter and resolve functions of a ui-router state

I need help testing an Angular UI Bootstrap modal that is triggered from the onEnter function in the state below: .state("profile.index.edit.services", { url: "/edit/services/:serviceCode", parent:"profile.index", onEnter: ['$ ...