Search code examples
jqueryhtmlscrollparallaxsticky

Pinning a section when you scroll to showcase features


I am trying to fix Section 2 once it comes to the viewport and on every scroll the next paragraph on the left highlights to the other one and the phone screen on the right slides to next one.

So on every scroll i want to highlight the next text and change the sceen inside the phone like you see on many app landing pages.

Here's a demo

enter image description here


Solution

  • Check this out - here is a summary of what I do here:

    1. I'm absolutely positioning section2 (pin) with respect to a wrapper that I added to it.

    2. On a resize listener I set the pin's height the same as the phone container so that the heights match.

    3. On a scroll listener I calculate if section2 comes into play / slides out of view.

    See the demo below:

    var found = false, last = false;;
    var lockedScrollTop = 0, step = 0, slide = 1;
    
    var wrapper = $('#wrap');
    var pin = $('#pin');
    var box = $('#phone');
    
    $(document).resize(function() {
      pin.outerHeight(box.innerHeight());
    });
    
    $(document).scroll(function() {
      var offsetTop = -wrapper.offset().top + $(window).scrollTop();
      
      // conditions on scroll from top down
      if(offsetTop >= 0 && offsetTop < wrapper.outerHeight() && !last) {
        slide = 2;
      } else if(offsetTop >= 0 && offsetTop >= wrapper.outerHeight()) {
        if(!last) {
          $(window).scrollTop(lockedScrollTop);
          last = true;
          slide = 3;
        } else {
          slide = 4;
        }
      } 
      
      // conditions of scroll from bottom up
      if(offsetTop >= 0 && offsetTop < wrapper.outerHeight() && slide === 4) {
        last = true;
        slide = 3;
      } else if(offsetTop < 0 && last) {
        last = false;
        $(window).scrollTop(lockedScrollTop + wrapper.outerHeight() - 1);
        slide = 2;
      } else if(offsetTop < 0 && !last) {
        slide = 1;
        // reset
        found = false;
        lockedScrollTop = 0;
        step = 0;
      }
      
      // console.log(slide);
      
      if (slide == 2) {
        if(offsetTop < 0)
          offsetTop = 0;
        pin.css({'top': offsetTop + 'px'});
        if (!found) {
          // calculate step
          lockedScrollTop = wrapper.offset().top;
          step = wrapper.outerHeight() / 4;
          found = true;
        }
        // set/unset active text
        var index = Math.floor(($(window).scrollTop() - lockedScrollTop) / step);
        $('#pin .text-container > p').removeClass('active');
        $('#pin .text-container > p').eq(index).addClass('active');
      } else {
        pin.css({'top': 0});
      }
    });
    section {
      display: block;
      width: 100%;
      height: 100vh;
      border-bottom: 1px solid red;
    }
    .phone-container {
      height: 100vh;
      width: 500px;
      background: red;
      display: flex;
      align-items: center;
      justify-content: center;
      float: right;
    }
    .phone {
      width: 200px;
      height: 500px;
      background: #000;
      color: #fff;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    section.long-scroll {
      height: auto;
    }
    p {
      margin-top: 80px;
    }
    p:first-child {
      margin-top: 0px;
    }
    .text-container {
      float: left;
      width: 200px;
    }
    .spacer {
      display: block;
      width: 100%;
    }
    p.active {
      color: pink;
    }
    .clearfix:after {
      visibility: hidden;
      display: block;
      font-size: 0;
      content: " ";
      clear: both;
      height: 0;
    }
    .clearfix {
      display: inline-block;
    }
    /* start commented backslash hack \*/
    
    * html .clearfix {
      height: 1%;
    }
    .clearfix {
      display: block;
    }
    /* close commented backslash hack */
    
    .stuck {
      position: fixed;
      top: 0;
    }
    .fixed {
      position: fixed;
      top: 0;
    }
    .sticky-wrapper {
      height: auto !important;
    }
    .text-container {
      padding-left: 40px;
      padding-top: 40px;
    }
    
    /*NEW STYLES ADDED*/
    #pin {
      position: absolute;
      right: 0;
      top: 0;
    }
    #pin.transition {
      transition: top ease 1s;
    }
    #wrap {
      position: relative;
      border: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <section>
      Scroll-down
    </section>
    <section id="wrap">
      <section class="long-scroll clearfix" id="pin">
        <div class="text-container">
          <p class="active">Text - 1</p>
          <p>Text - 2</p>
          <p>Text - 3</p>
          <p>Text - 4</p>
        </div>
        <div class="phone-container" id="phone">
          <div class="phone">Slide-1</div>
        </div>
      </section>
    </section>
    <section id="unhook"></section>