Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallax elements on scroll

Fiddle

I have a horizontal page with multiple sections on it. On section 2 I have three images. When I scroll section 2 into view, I want the images to move 50px in the opposite scroll direction.

I have two problems which I can't figure out due to the layout of this page (horizontal instead of vertical):

  1. how to detect when I reach section 2

  2. how to move the images by ~50px in the opposite direction of the scroll and make it as smooth as possible

I use this code to figure out the direction of the scroll

var $scrollWrapper = $('.scroll_wrapper');
var $scrollBtn = $('#scrollBtn');
var $scrollOuterWrapper = $('.scroll_outer-wrapper');

$scrollWrapper.scrollTop(0)
$('#scrollBtn').on('click', function() {
  $scrollWrapper.scrollTop($scrollWrapper.scrollTop() + 100)
});

var lastScrollTop = 0;

$scrollOuterWrapper.on('scroll', function() {
  var st = $(this).scrollTop();
  var endOfWrapper = $(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight;
  if (st > lastScrollTop){
    // down scroll
    console.log('downscroll');

    // parallax elements - move to front
    // ??
    $moveElement = $('.move-on-scroll');

    $moveElement.each(function() {
      var firstTop = $(this).offset().top;
      var wrapperScrollTop = $scrollOuterWrapper.scrollTop();
      var shiftDistance = (firstTop - wrapperScrollTop)*0.02;

      $(this).css("transform","translateX("+shiftDistance+"px)");
    });


  } else {
    // upscroll
     console.log('upscroll');

     // parallax elements - move to back
     // ??
  }
  lastScrollTop = st;
});

Here's also a snippet:

var $scrollWrapper = $('.scroll_wrapper');
var $scrollBtn = $('#scrollBtn');
var $scrollOuterWrapper = $('.scroll_outer-wrapper');

$scrollWrapper.scrollTop(0)
$('#scrollBtn').on('click', function() {
  $scrollWrapper.scrollTop($scrollWrapper.scrollTop() + 100)
});

var lastScrollTop = 0;

$scrollOuterWrapper.on('scroll', function() {
  var st = $(this).scrollTop();
  var endOfWrapper = $(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight;
  if (st > lastScrollTop){
    // down scroll
    console.log('downscroll');
    
    // parallax elements - move to front
    // ??
    $moveElement = $('.move-on-scroll');
    
    $moveElement.each(function() {
      var firstTop = $(this).offset().top;
      var wrapperScrollTop = $scrollOuterWrapper.scrollTop();
      var shiftDistance = (firstTop - wrapperScrollTop)*0.2;

      $(this).css("transform","translateX("+shiftDistance+"px)");
    });


  } else {
    // upscroll
     console.log('upscroll');
     
     // parallax elements - move to back
     // ??
  }
  lastScrollTop = st;
});
.scroll_outer-wrapper {
  width: 100vh;
  height: 100vw;
  transform: rotate(-90deg) translateX(-100vh);
  transform-origin: top left;
  overflow-y: scroll;
  overflow-x: hidden;
  position: absolute;
}
.scroll_wrapper {
  display: flex;
  flex-direction: row;
  width: 400vw;
  transform: rotate(90deg) translateY(-100vh);
  transform-origin: top left;
  transition: transform .5s ease;
}
.scroll_section {
  width: 100vw;
  height: 100vh;
}

.scroll_section.one{background: black; color: white;}
.scroll_section.two{background: white; color: black;}
.scroll_section.three{background: black; color: white;}
.scroll_section.four{background: pink; color: black;}

#scrollBtn {
    position: absolute;
    bottom: 20px;
    right: 20px;
    background-color: darkblue;
    color: white;
    border: none;
    width: 80px;
    height: 80px;
    border-radius: 50%;
    text-transform: uppercase;
    font-size: 12px;
    line-height: 20px;
    cursor: pointer;
}

.move-on-scroll {
  width: 150px;
  height: 150px;
  border: 2px solid red;
  margin: 0 20px;
}

.move-on-scroll img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.two_inner {
  display: flex;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="scroll_outer-wrapper">
  <div class="scroll_wrapper">
    <section class="scroll_section one">
      <h2>section 1</h2>
    </section>
    <section class="scroll_section two">
      <h2>section 2</h2>
      <div class="scroll_section two two_inner">
        <div class="move-on-scroll">
          <img src="https://images.unsplash.com/photo-1590336751349-f65720fee481?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80" >
        </div>
        <div class="move-on-scroll">
          <img src="https://images.unsplash.com/photo-1590336751349-f65720fee481?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80" >
        </div>
        <div class="move-on-scroll">
          <img src="https://images.unsplash.com/photo-1590336751349-f65720fee481?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80" >
        </div>
      </div>
    </section>
    <section class="scroll_section three">  
      <h2>section 3</h2>
    </section>
    <section class="scroll_section four">
      <h2>section 4</h2>
    </section>
  </div>
</div>

<button id="scrollBtn">Click to Scroll</button>
like image 769
Aerra Avatar asked Dec 01 '25 10:12

Aerra


1 Answers

While writing the answer, start thinking. I'm not quite sure, what is the goal. Maybe if you show some images of a result or explain the scenario - i could do more accurate. For now - made this. First line of image starts moving with the second screen. The second line of images start moving from the beginning. View the code in Full page mode.

UPDATED

Simply added transition to CSS for the element, which is transforming with transform via jquery.

.move-on-scroll {transition: all .5s cubic-bezier(.25,.99,.52,.9);}

If you what to correct the ease transition-timing-function - you can create your own cubic-bezier here https://cubic-bezier.com/

var $scrollWrapper = $('.scroll_wrapper');
var $scrollBtn = $('#scrollBtn');
var $scrollOuterWrapper = $('.scroll_outer-wrapper');

// Have no idea, what it shoud do
/*$scrollWrapper.scrollTop(0);
$('#scrollBtn').on('click', function() {
  $scrollWrapper.scrollTop($scrollWrapper.scrollTop() + 100)
});*/

$scrollOuterWrapper.on('scroll', function() {

  var st = $(this).scrollTop();
  var sOneWidth = $('.scroll_section.one').width();

  $moveElement = $('.move-on-scroll');
  $moveElement.each(function() {
    var firstTop = $(this).offset().left;
    var shiftDistance = -st * 0.3;
    // detects, when you reash section 2
    if (st >= sOneWidth) {
      //do something
    }
    $(this).css("transform", "translateX(" + shiftDistance + "px)");
  });

});
* {
  box-sizing: border-box;
}

html {
  height: 100%;
}

body {
  min-height: 100%;
  margin: 0;
  padding: 0;
}

.scroll_outer-wrapper {
  width: 100vh;
  height: 100vw;
  transform: rotate(-90deg) translateX(-100vh);
  transform-origin: top left;
  overflow-y: scroll;
  overflow-x: hidden;
  position: absolute;
}

.scroll_outer-wrapper {
  scrollbar-width: thin;
}

.scroll_outer-wrapper::-webkit-scrollbar {
  width: 6px;
  background-color: #fff;
}

.scroll_outer-wrapper::-webkit-scrollbar-track {
  background-color: #F5F5F5;
  border-radius: 10px;
}

.scroll_outer-wrapper::-webkit-scrollbar-thumb {
  background: #ffa000;
}

.scroll_wrapper {
  display: flex;
  flex-direction: row;
  width: 400vw;
  transform: rotate(90deg) translateY(-100vh);
  transform-origin: top left;
  transition: transform .5s ease;
}

.scroll_section {
  width: 100vw;
  height: 100vh;
}

.scroll_section.one {
  background: black;
  color: white;
}

.scroll_section.two {
  background: white;
  color: black;
}

.scroll_section.three {
  background: black;
  color: white;
}

.scroll_section.four {
  background: pink;
  color: black;
}

#scrollBtn {
  position: absolute;
  bottom: 20px;
  right: 20px;
  background-color: darkblue;
  color: white;
  border: none;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  text-transform: uppercase;
  font-size: 12px;
  line-height: 20px;
  cursor: pointer;
}

.move-on-scroll {
  width: 150px;
  height: 150px;
  border: 2px solid red;
  margin: 0 20px;
  transition: all .5s cubic-bezier(.25, .99, .52, .9);
}

.move-on-scroll img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.two_inner {
  display: flex;
  justify-content: flex-end;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="scroll_outer-wrapper">
  <div class="scroll_wrapper">
    <section class="scroll_section one">
      <h2>section 1</h2>
    </section>
    <section class="scroll_section two">
      <h2>section 2</h2>
      <div class="two_inner">
        <div class="move-on-scroll">
          <img src="https://images.unsplash.com/photo-1590336751349-f65720fee481?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80">
        </div>
        <div class="move-on-scroll">
          <img src="https://images.unsplash.com/photo-1590336751349-f65720fee481?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80">
        </div>
        <div class="move-on-scroll">
          <img src="https://images.unsplash.com/photo-1590336751349-f65720fee481?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80">
        </div>
      </div>
    </section>
    <section class="scroll_section three">
      <h2>section 3</h2>
    </section>
    <section class="scroll_section four">
      <h2>section 4</h2>
    </section>
  </div>
</div>

<button id="scrollBtn">Click to Scroll</button>
like image 114
focus.style Avatar answered Dec 04 '25 00:12

focus.style



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!