Parallax - Offset element(s), tied to scroll
Asked Answered
B

2

15

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 ?
      }

    }

  });

});
Baram answered 25/4, 2016 at 20:29 Comment(0)
N
6

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.

Neocolonialism answered 29/4, 2016 at 3:32 Comment(3)
Thanks for the help, good point about the idea of concurrency! Your demo looks good, but it should stop at it's end position once 50% into frame and stop moving at that point. So once it scrolls into place, they remain stuck together as you scroll down. Basically only reveal in, not do anything out.Baram
Sorry, thought it was "until the top of element reaches middle of viewport at which time it's top offset is 0 and it remains in place.". In any event, no rush, take your time and I appreciate the help. It's very close.Baram
@LouisW added a demo that preserves the 0 Y position once two element touch!Neocolonialism
L
2

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.

Lilililia answered 29/4, 2016 at 4:35 Comment(1)
Sorry, looks like your link doesn't work anymore. "This bin was created anonymously and its free preview time has expired"Baram

© 2022 - 2024 — McMap. All rights reserved.