Параллакс - элемент смещения, привязанный к прокрутке

Удар моей головы, пытаясь разобраться в правильной логике для добавления простого поведения параллакса.

Я хотел бы иметь несколько элементов на странице, которые начинаются со своего верхнего смещения на определенное расстояние (например, 300 пикселей). Затем, когда вы прокрутите страницу вниз, как только появится верхняя часть элемента, она медленно сдвинется вверх (привязана к прокрутке) до тех пор, пока верхняя часть элемента не достигнет середины окна просмотра, в это время верхнее смещение будет 0, и оно останется на месте.

Я пробовал использовать сторонний script (Scroll Magic, Stellar и т.д.), но когда я не смог получить его прямо сейчас, я пробовал собственный код:

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

    }

  });

});

Ответы

Ответ 1

jsBin demo 1. (эффект маржирования на входе и выходе)
jsBin demo 2. (сохраните 0 полей после того, как они были затронуты)

Вместо того, чтобы таргетировать элементы section, (create and) нацелить свои первые дочерние элементы,
в противном случае вы создадите беспорядок concurrency, пытающийся получить верхнюю позицию, но одновременно изменив ее.

Кроме того, вы не можете полагаться на фиксированный маркер 300px (например: если высота окна меньше 500 пикселей, вам уже не хватает 100 пикселей). Это пространство может меняться, когда высота экрана очень мала, поэтому вам также нужно найти idealMarg значение.

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>

Я использую в HTML только a <div> логически, который должен быть единственным и единственным первым родителем section.
Вы можете настроить этот код, чтобы сделать его более эффективным.

Ответ 2

Эй, так вот мой поход на абрисе.

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

Суть его заключается в следующем.

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;
    });

});

Стили

body{
  height: 4000px;
}

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

Пример использования

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

Таким образом, проверяя, что его положение никогда не ниже 0, мы можем заблокировать его в верхней части экрана, когда он достигнет его.

Я получаю значение смещения тега данных в div.

Если вы хотите изменить скорость прокрутки в разных позициях, вы можете изменить коэффициент параллакса в определенном проценте высоты экрана.

Надеюсь, что это поможет.