Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallax - Offset element(s), tied to scroll

Banging my head trying to sort out the correct logic for adding simple parallax behavior.

I would like to have a number of elements on a page which start out with their top offset a certain distance (e.g. 300px). Then as you scroll down the page, once the top of the element is revealed it will slowly shift upwards (tied to scroll) until the top of element reaches middle of viewport at which time it's top offset is 0 and it remains in place.

I tried using third party script (Scroll Magic, Stellar, etc), but when I couldn't get it right now I'm trying custom code:

https://jsfiddle.net/louiswalch/5bxz8fku/1/

var $Window = $(window);
var offset_amount = 400;
var window_height = $Window.height();
var window_half   = (window_height/2);
var sections      = $('SECTION.reveal');

sections.each(function() {

  var element = $(this);

  // Make sure we always start with the right offset
  element.css({top: offset_amount});

  $Window.bind('scroll', function() {

    var viewport_top    = $Window.scrollTop();
    var viewport_middle = viewport_top + (window_height/2)
    var viewport_bottom = viewport_top + window_height;
    var element_top     = element.offset().top;

    if (element_top > viewport_top &&  element_top <= viewport_bottom) {

      var distance_to_middle  = (element_top - viewport_middle);
      var amount_to_middle    = (distance_to_middle / window_half);

      console.log(amount_to_middle);

      if (amount_to_middle >= 0) {
        element.css({top: (offset_amount * amount_to_middle)+ 'px'});
      } else {
        // ? Lock to end position ?
      }

    }

  });

});
like image 695
Louis W Avatar asked Apr 25 '16 20:04

Louis W


Video Answer


2 Answers

jsBin demo 1. (margin space effect on both enter and exit)
jsBin demo 2. (preserve 0 margin once touched)

Instead of targeting the section elements, (create and) target their first child elements,
otherwise you'll create a concurrency mess trying to get the top position but simultaneously modifying it.

Also, you cannot rely on fixed 300px margin (i.e: if window height is less than 500px, you're already missing 100px). That space can vary when the screen height is really small, so you also need to find the idealMarg value.

var $win = $(window),
    $rev = $('.reveal'),
    winH2 = 0,
    winSt = 0;

function reveal() {

  winSt = $win.scrollTop();
  winH2 = $win.height()/2;

  $rev.each(function(i, el){
    var y = el.getBoundingClientRect().top,
        toMiddleMax = Math.max(0, y-winH2),
        idealMarg   = Math.min(300, toMiddleMax),
        margMin     = Math.min(idealMarg, idealMarg * (toMiddleMax/winH2));
    $(">div", this).css({transform: "translateY("+ margMin +"px)"});
  });

}

$win.on({"load resize scroll" : reveal});
*{box-sizing:border-box; -webkit-box-sizing:border-box;}
html, body{height:100%; margin:0;}

section > div{
  padding: 40px;
  min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<section>
  <div style="background-color:red">1</div>
</section>
<section class="reveal">
  <div style="background-color: yellow">2</div>
</section>
<section class="reveal">
  <div style="background-color: orange">3</div>
</section>
<section class="reveal">
  <div style="background-color: pink">4</div>
</section>

I've used in HTML just a <div> logically, that has to be the one and only first child of a section parent.
You're welcome to tweak the above code to make it more performant.

like image 198
Roko C. Buljan Avatar answered Oct 21 '22 03:10

Roko C. Buljan


Hey so here is my go at an awnser.

http://jsbin.com/wibiferili/edit?html,js,output

The jist of it is as follows.

JS

var $Window = $(window),
    parallaxFactor = 2;

$('.parallaxblock').each(function(a,b){
    var element = $(b);
    element.css("top",element.data("pOffset") + "px");

    $Window.bind('scroll', function() {
        var pos = 
            // Base Offset
            element.data("pOffset")
            // parallaxFactor
            - ($Window.scrollTop() / parallaxFactor);

        pos = pos < 0 ? 0 : pos;

        element.animate({"top": pos + "px"},10);

        return;
    });

});

Styles

body{
  height: 4000px;
}

.parallaxblock{
  position:fixed;
  background:#999;
  opacity:.5;
}

Example Usage

<div class="parallaxblock" data-p-offset=100>Im A Block</div>
<div class="parallaxblock" data-p-offset=200>Im Also Block</div>
<div class="parallaxblock" data-p-offset=1500>Im Another Block</div>

So by checking the offest its never lower then 0 we can lock it at the top of the screen once it reaches it.

I get the offset amount of the data tag on the div.

If you wanted to change the rate of scroll in different posistions you could change the parallax factor at a certain percentage of screen height.

Hope this helps.

like image 45
Spaceman Avatar answered Oct 21 '22 02:10

Spaceman