Aerra
Aerra

Reputation: 84

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>

Upvotes: 3

Views: 1555

Answers (1)

focus.style
focus.style

Reputation: 6750

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>

Upvotes: 1

Related Questions