Parallax effect - calculate child offset to parent on scroll

情到浓时终转凉″ 提交于 2019-12-09 22:42:15

问题


I'm trying to create a parallax effect whereby an absolutely positioned child element should move at a rate slower than it's parent on scroll.

The child will always be 130% height of the parent but the parent can be any height:

HTML:

<div class="parallax-window lg">
  <div class="parallax-image image-1"></div>
  <div class="parallax-content">Hello World</div>
</div>

<div class="parallax-window">
  <div class="parallax-image image-2"></div>
  <div class="parallax-content">Hello World</div>
</div>

CSS:

.parallax-window {
  min-height: 300px;
  position: relative;
  overflow: hidden;
}

.parallax-window.lg {
  min-height: 600px;
}

.parallax-image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 130%;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  transform: translate3d(0, 0, 0);
  z-index: -1;
 }

.image-1 {
  background-image: url(https://i.ytimg.com/vi/TbC-vUPMR7k/maxresdefault.jpg);
}

.image-2 {
  background-image: url(https://i.ytimg.com/vi/xi5-YrAEChc/maxresdefault.jpg);
}

I have a formula to move the image but my maths is clearly way off:

var win = $(window),
        win_h = win.height(),
        parallaxers = $('.parallax-window');

    function scroll_events() {
        var win_top = win.scrollTop(),
            win_btm = win_top + win_h;
    
        parallaxers.each(function() {
            var cont = $(this),
                cont_top = cont.offset().top,
                cont_h = cont.height(),
                cont_btm = cont_top + cont_h,
                para = cont.find('.parallax-image'),
                para_h = Math.round(cont_h * 1.3);
            if (cont_btm > win_top && cont_top <= win_btm) {
                var diff = (win_h - cont_h) / (win_h - para_h),
                    value = -Math.round((win_top * diff));
                // para.css('transform', 'translate3d(0,' + value*-1 + 'px, 0)');
                para.css('top', value + 'px');
            }
        });
    }

The images move but not at the correct rate.

The image should be in line with the top of the parent when the element first comes into the viewport. Then after scrolling, the bottom of the image should be in line with the bottom of the parent when it reaches the top of the viewport.

Any help would be massively appreciated!

FIDDLE (https://jsfiddle.net/8dwLwgy7/1/)


回答1:


I figured this out.

For anyone stumbling on this in the future - the trick was to replace the window scrolled value with the remainder of the window scrolled amount minus the element's offset top, minus the height of the element.

Then calculate the speed by dividing the difference between the container height and the element height with largest of either the window and the container:

// Wrong:
// value = -Math.round((win_top * diff));

// Right:
var diff = elem_h - cont_h,
    max = Math.max(cont_h, win_h),
    speed = diff / max,
    cont_scrolled = win_top - cont_top - cont_h,
    value = Math.round(cont_scrolled * speed);

para.css('top', value + 'px');

Full working code:

(function() {
  var lastTime = 0;
  var vendors = ['ms', 'moz', 'webkit', 'o'];
  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
  }
  if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function(callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() {
          callback(currTime + timeToCall);
        },
        timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  if (!window.cancelAnimationFrame)
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
}());


(function($) {

  var win = $(window),
    win_h = win.height();
		parallaxers = $('.parallax-window'),
		parallax_objs = [],
    scroll_busy = false;

	function init_parallax() {
		win_h = win.height();
		parallax_objs = [];
		parallaxers.each(function() {
			var cont = $(this),
				elem = cont.find('.parallax-image'),
				cont_top = cont.offset().top,
				cont_h = cont.height(),
				elem_h = Math.round(cont_h * 1.3),
				diff = elem_h - cont_h,
				max = Math.max(cont_h, win_h),
				speed = diff / max,
				parallaxer = {
					cont_top: cont_top,
					cont_h: cont_h,
					elem: elem,
					speed: speed
				};
			parallax_objs.push(parallaxer);
		});
	}
  
  function on_scroll() {
    if (!scroll_busy) {
      scroll_busy = true;
      window.requestAnimationFrame(init_scroll);
    }
  }

  function init_scroll() {
    scroll_events()
    scroll_busy = false;
  }

  function scroll_events() {
    var win_top = win.scrollTop(),
      win_btm = win_top + win_h;

		$.each(parallax_objs, function(i, para) {
			cont_btm = para.cont_top + para.cont_h;
			if( cont_btm > win_top && para.cont_top <= win_btm ) {
				var cont_scrolled = win_top - para.cont_top - para.cont_h,
					value = Math.round(cont_scrolled * para.speed);
				para.elem.css('top', value + 'px');
			}
		});
  }

  $(document).ready(function() {
  	init_parallax();
    win.resize(init_parallax);
    scroll_events();
    win.scroll(on_scroll);
  });

})(jQuery);
.parallax-window {
  min-height: 300px;
  position: relative;
  overflow: hidden;
}

.parallax-window.lg {
  min-height: 600px;
}

.parallax-image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 130%;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  transform: translate3d(0, 0, 0);
  z-index: -1;
}

.image-1 {
  background-image: url(https://i.ytimg.com/vi/TbC-vUPMR7k/maxresdefault.jpg);
}

.image-2 {
  background-image: url(https://i.ytimg.com/vi/xi5-YrAEChc/maxresdefault.jpg);
}

.parallax-content {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  text-align: center;
  font-family: arial, sans-serif;
  font-size: 60px;
  color: #fff;
  transform: translateY(-50%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="parallax-window lg">
  <div class="parallax-image image-1"></div>
  <div class="parallax-content">Hello World</div>
</div>
<div class="parallax-window">
  <div class="parallax-image image-2"></div>
  <div class="parallax-content">Hello World</div>
</div>
<div class="parallax-window lg">
  <div class="parallax-image image-1"></div>
  <div class="parallax-content">Hello World</div>
</div>
<div class="parallax-window">
  <div class="parallax-image image-2"></div>
  <div class="parallax-content">Hello World</div>
</div>
<div class="parallax-window lg">
  <div class="parallax-image image-1"></div>
  <div class="parallax-content">Hello World</div>
</div>

Updated Fiddle: https://jsfiddle.net/8dwLwgy7/2/



来源:https://stackoverflow.com/questions/39528922/parallax-effect-calculate-child-offset-to-parent-on-scroll

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!