Search code examples
javascriptjqueryparallax

Parallax stop background image pos change if scroll exceeds bg image size


I have multiple sections showing different backgrounds, each section has a basic parallax background image. As the backgrounds vary in height, I cannot seem to work out how to stop the background image position once the image bottom is reached.

Background position change begins if the section offset().top is equal to or greater than $(window).scrollTop().

It would seem that the btmOffset is incorrect but I can't see why.

Any help greatly appreciated.

Live example http://demo.dwweb.co.uk

What I have so far

$window = $(window);
var winWid = $window.width();
$('.portfolioSection').each(function(){
        var $bgobj = $(this);
        var speed = 2.4;
        var bg = $(this).css('background-image').replace('url("','').replace('")','');
        var tmpImg = new Image();
        tmpImg.src = bg;
        var orgW = tmpImg.width;
        var orgH = tmpImg.height; 
        var imgResizedRatio = winWid/orgW;
        var resizedH = orgH * imgResizedRatio;
        var btmOffset =  (resizedH - $(this).height()) + $bgobj.offset().top;
        $(window).scroll(function() {
            if($(window).scrollTop() > $bgobj.offset().top && $(window).scrollTop() < btmOffset){
                var yPos = -(($window.scrollTop()-$bgobj.offset().top) / speed); 
                var coords = '0 '+ yPos + 'px';
                $bgobj.css({ backgroundPosition: coords });
            } else if($(window).scrollTop() < $bgobj.offset().top) {
                $bgobj.css({ backgroundPosition: '0 0' });
            } else {
                $bgobj.css({ backgroundPosition: '0 '+resizedH+'px' });
            }
        }); 

    });

Solution

  • orgH and orgW will be 0 on execution, since you are creating a new Image asynchronously, while executing your code synchronously:

    var tmpImg = new Image();
    tmpImg.src = bg;
    //...
    

    This means, you would have to use the onload event (and maybe cover the onerror event too), like this:

    tmpImg.onload = function(ev) {
        var orgW = tmpImg.width;
        var orgH = tmpImg.height; 
        //and the rest of your code...
    }
    

    This is very inefficient, since you are loading all these (big) images again. I would add data-attributes to each .portfolioSection, like data-bgmaxscroll="1000" (which is the height of the image).

    This would be a bit more hardcoded, but i think it's the easiest and the most performant way.