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