Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallax scrolling – decrease height of container to match transform gap

I have an arrangement of elements on the page – positions controlled via a CMS which gives each element a width, top position, left position, z-index and 'speed'.

The speed is used to create a parallax effect using JS. It takes it's 'speed' and calculates this by the window.pageYOffset – if the speed is less than 0 then it divides the window.pageYOffset by the speed and if it's more than 0 then it multiplies the window.pageYOffset by the speed. On scroll it then adjusts the Y translation to give this 'parallax' effect.

This is generally all fine but of course as you alter the Y positions of the elements on scroll you are left with a 'gap' at the bottom (where the elements would be if the scroll speed matched the user scroll speed).

To rectify this I thought I would get the most bottom element, and get it's getBoundingClientRect().bottom position and on scroll reduce the height of the container to match the bottom of this element so as you scroll down or up the container would contract/expand to match and thus removing the gap.

However, this doesn't seem to work. The math/logic is either wrong or I'm missing the entire thing.

Below is my code and I have set up a JSFiddle to help visualise this.

https://jsfiddle.net/6up3vqjn/2/

// Runs on init and resize
function parallaxInit() {

  var $container = $('.parallax'),
    container = $container[0];

  var testArray = [],
    $testLastElement;

  $('.parallax > .group').each(function() {

    var $group = $(this),
      group = $group[0],
      groupBounds = group.getBoundingClientRect(),
      $lastElement,
      lastElementBoundsBottom = 0;

    $group.find('> div').each(function() {

      var $div = $(this),
        div = $div[0],
        initTop = $div.attr('data-top');

      if (initTop == 0) {
        $div.css('top', '0');
      } else {
        $div.css('top', $(window).width() / 12 * initTop - 26 + 'px');
      };

      group.removeAttribute('style');
      $group.height(group.scrollHeight).attr('data-height', group.scrollHeight);

      var divBounds = div.getBoundingClientRect();
      testArray.push(divBounds.bottom);

    });

  });

  $('.parallax > .group > div').each(function() {

    var divBottomBounds = $(this)[0].getBoundingClientRect().bottom;

    if (divBottomBounds == Math.max.apply(Math, testArray)) {
      $testLastElement = $(this);
      $(this).addClass('is--last');
    }

    var letters = "0123456789ABCDEF";
    var color = '#';
    for (var i = 0; i < 6; i++) color += letters[(Math.floor(Math.random() * 16))];
    $(this).css('background-color', color);


  });

  $container[0].style.height = $testLastElement[0].getBoundingClientRect().bottom + 'px';

}
parallaxInit();
$(window).on('resize', parallaxInit);

// Runs on scroll
function parallax() {

  var $container = $('.parallax');

  var test = 0;
  var testArray = [],
    $testLastElement;

  $('.parallax > .group').each(function() {

    var $group = $(this),
      group = $group[0],
      groupHeight = $group.attr('data-height'),
      groupBounds = group.getBoundingClientRect();

    $group.find('> div').each(function() {

      var $this = $(this),
        speed = $this.attr('data-speed');

      if (speed < 0) {
        speed = Math.abs(speed);
        var yPos = window.pageYOffset / speed;
      } else {
        var yPos = window.pageYOffset * speed;
      }
      yPos = -yPos;

      $this[0].style.transform = "translate3d(0, " + yPos + "px, 0)";

      var divBounds = $this[0].getBoundingClientRect(),
        divRelativeBounds = {};

      testArray.push(divBounds.bottom);

    });

  });


  $('.parallax > .group > div').each(function() {

    var divBottomBounds = $(this)[0].getBoundingClientRect().bottom;
    $(this).removeClass('is--last');

    if (divBottomBounds == Math.max.apply(Math, testArray)) {
      $testLastElement = $(this);
      $(this).addClass('is--last');
    }

  });

  $container[0].style.height = $testLastElement[0].getBoundingClientRect().bottom + 'px';

}
$(window).bind('scroll', parallax);

UPDATED JSFiddle with container height calculation removed: https://jsfiddle.net/ejqhvz2c/

like image 688
John the Painter Avatar asked Jun 06 '19 15:06

John the Painter


1 Answers

I'm not 100% this is exactly what you are after but I have stopped the over-scroll and the scrolling into white space. First I changed the way the event is triggered. Since it is a parallax I made it bound to the wheel event rather than actual scrolling, which in doesn't actually matter when page actually has content on it. Then I used a global variable called paralaxYOffset which i decide was best to increment in +- 50 intervals for smoothness and to stop over-scroll i added a check to see if .is--last y position is <= 0 and then dont allow parallax scrolling any further.

Event changes:

if(e.originalEvent.deltaY  > 0){
        if($(".is--last")[0].getBoundingClientRect().y > 0){
            window.paralaxYOffset += 50;
      }
  }else{
    window.paralaxYOffset -= 50;
  }

  if(window.paralaxYOffset < 0)
    window.paralaxYOffset = 0;

To make sure everything looked right and to remove the overflow scroll bar, i made .parallax set to 100vh and overflow: hidden

I believe this achieves what you requested :)

https://jsfiddle.net/fynmtk9b/4/

like image 89
Deckerz Avatar answered Oct 18 '22 03:10

Deckerz