0

I am trying to make a parallax effect that only involves backgrounds and header/footer images, leaving alone the main content markup (a regular wordpress loop).

The backgrounds are all in the div #page which takes the whole document, except for a top banner which is an img in a separate div at the top, .site-branding, and the fill background which is in body.

#page {
    background:url("http://i.imgur.com/jK3tbU8.png") no-repeat scroll bottom center, url("http://i.imgur.com/TSrLfq1.png") repeat-x scroll top center, url("http://i.imgur.com/Md0r0mw.png")repeat-x scroll bottom center;
    padding:0;
    margin:0;
    transition: 0s ease-in-out;
    transition-property: background-position;
}

body {
    background:url("http://i.imgur.com/910XVzz.jpg") repeat scroll top; 
    padding:0;
    margin:0;
    transition: 0s ease-in-out;
    transition-property: background-position;
}

.site-branding {
    margin-left:auto;
    margin-right:auto;
    min-width: 0;
    overflow: hidden;
    display: block;
}
.site-branding img {
    max-width:100%;
    margin-left:0;
}

The top banner scrolls up at a certain speed, the top background scrolls up at a slightly slower pace, the fill scrolls normally in the middle of the page...

So far so good (please refer to the JSFiddle linked below to follow all of this a little more conveniently).

I then want to make the two bottom backgrounds, jK3tbU8.png and Md0r0mw.png, to scroll up at their own speed (for example, slower than scroll), eventually stopping at the bottom of the page (jK3tbU8.png is a "mandala" footer banner, which is on top of jK3tbU8.png, the bottom fade background).

With the script below I can make them come up at their own speed: but I cannot make them stop exactly at the bottom.

I think this might happen mainly because scrollTop() and offset() are all based on top...?

You would think it is only a matter of subtracting values, but I find that the height of the viewport of course makes all the difference. If the viewport changes, the images end at a higher or lower position, although with very small differences.

I guess I need to factor the viewport in the calculation? but I don't know how!

Here's my whole code, I put a lot of comments in it, hopefully it will be not too difficult to understand:

jQuery(function ($) {
    "use strict"; 

    $(document).ready(function(){
    var $win = $(window);

    $('div#page').each(function(){

        //set an arbitrary "speed"
        var scroll_speed1 = 8;

        var viewportHeight = $(window).height();

        //set heights of backgrounds (any easy way to get this dynamically?)
        var topFade = 600;
        var bottomFade = 870;
        var mandalaBottom = 457;

        var $this = $(this);

        //set background-attachment here, so that in style.css top-fade can be "scroll"
        //needed in case the script breaks
        $this.css("background-attachment", "scroll, fixed, scroll");

        //set all the initial positions
        //the idea is that if the images are down below (= higher number)
        //they will scroll up faster, at a matching speed (= number dimishing faster)
        var bgP1 = ( ($this.outerHeight()*2) + mandalaBottom ); //mandala-bottom
        var bgP2 = $this.offset().top; //top-fade
        var bgP3 = ($this.outerHeight()+(bottomFade)); //bottom-fade
        var initialValue = "center " + bgP1 + "px," + "center " + bgP2 + "px," + "center " + bgP3 + "px";

        //put the images in the css
        $this.css("background-position", initialValue);

        //bind to the scroll event  
        $(window).scroll(function() {

            //percent between viewport and height of page
            var viewportDiff = percentChange($this.outerHeight(), viewportHeight);
            console.log("the percent between viewport and height of page is: " + viewportDiff+ "%");

            //percent between position of pic and height of page
            var perDiff = percentChange(bgP1, $this.outerHeight());
            console.log("the percent between position of pic and height of page is: " + perDiff);

       /*   1) $win.scrollTop() = position of the scroll (increasing number) 
            2) perDiff = the proportion between the page and the position of the pic (%)
            3) viewportDiff= the proportion between the page and the viewport (%)
            4) $this.outerHeight() = height of the page
            => scroll + the x% of scroll (given by the difference with the position of the pic) - the x% of page (given by the difference between viewport and page) + the height of the image   */             
            var numberToSubstract = ( $win.scrollTop() + percentOf($win.scrollTop(), perDiff) - percentOf(viewportDiff, $this.outerHeight() ) + (mandalaBottom/2) );
            console.log("the initial position of the pic was: " + bgP1);
            console.log("we have substracted this much: " + numberToSubstract);
            var bgNP1 = bgP1 - numberToSubstract - (mandalaBottom);
            console.log("the pic now is here: " + bgNP1);
            console.log("the height of the page is: " + $this.outerHeight());            


            //this calculates where to put the top-fade as the page is scrolled
            var bgNP2 = -(($win.scrollTop() - $this.offset().top)/ scroll_speed1);

            //this calculates where to put the bottom-fade as the page is scrolled
            var perDiff3 = percentDiff(bgP3, $this.outerHeight());
            var numberToSubstract3 = ($win.scrollTop() + percentOf($win.scrollTop(), perDiff3)) / scroll_speed1;
            var bgNP3 = bgP3 - numberToSubstract3 - bottomFade;
            //put the new values in the css
            var newValue = "center " + bgNP1 + "px," + "center " + bgNP2 + "px," + "center " + bgNP3 + "px";
            $this.css("background-position", newValue);

            }); //scroll
        }); //page

            //this takes care of mandala-top
            $('div.site-branding').each(function(){

                //set an arbitrary speed
                var scroll_speed = 1.5;

                    //bind to the scroll event (again?)  
                    $(window).scroll(function() {            

                        //this calculates where to put the top-fade as the page is scrolled
                        var bgNP2 = -( ($win.scrollTop() /*- $this.offset().top --not needed since it is 0*/) / scroll_speed );

                        //put the new values in the css
                        var newValue2 = bgNP2 + "px";
                        $('#mandalaTop').css("position", "relative");
                        $('#mandalaTop').css("top",  newValue2);
                        $('#mandalaTop').height(448 /*height of top img*/);

                    }); //scroll

        }); //div.site-branding   

    });//document ready

    if(navigator.userAgent.match(/Trident\/7\./)) {
        $('body').on("mousewheel", function () {
            event.preventDefault();
            var wd = event.wheelDelta;
            var csp = window.pageYOffset;
            window.scrollTo(0, csp - wd);
        });
    }

    //following this calculation: 
    //http://www.calculatorsoup.com/calculators/algebra/percent-difference-calculator.php
    function percentDiff(val1, val2) {
        var difference = ( (val1 - val2) / ((val1+val2) / 2) ) * 100;
        return difference;
    }

    function percentOf(percent, value) {        
        var n = percent/100;
        return n * value;
    }

    function percentChange(bigVal, smallVal) {
       var change = ( (smallVal-bigVal) / bigVal ) * 100;
       return 100 + change;
    }

});

To understand the above a little better, and see the simple html markup which contains all this, here's a JSFiddle.

It works as it should, which means, everything is fine at the top, but messed up at the bottom.

nonhocapito
  • 466
  • 5
  • 18

1 Answers1

0

A very simple fix is to change:

//put the new values in the css
var newValue = "center " + bgNP1 + "px," + "center " + bgNP2 + "px," + "center " + bgNP3 + "px";

To:

//put the new values in the css
var newValue = "center bottom," + "center " + bgNP2 + "px," + "center " + bgNP3 + "px";

Basically, not assigning a dynamic scroll offset to the bottom decorative element since you want it to stick to bottom.

If you still want to animate but the bottom element but stick it to the bottom, a simple conditional could do. In this case 457 is the image height. So, if the offset ever gets smaller than page height - image height, stick the image to bottom to avoid having a gap:

if(bgNP1 < $this.outerHeight() - 457)
{
    bgNP1 = $this.outerHeight() - 457;
}

//put the new values in the css
var newValue = "center " + bgNP1 + "px," + "center " + bgNP2 + "px," + "center " + bgNP3 + "px";
Serg Chernata
  • 12,280
  • 6
  • 32
  • 50
  • What you propose is simply to get rid of a parallax effect for the bottom image. But I don't want it exactly to stick to bottom: I want it to come up to a different pace (for example, slower than scroll) and _eventually stop at the bottom_. – nonhocapito Jan 08 '17 at 15:16
  • Sorry if I am slow in understanding, but what is that 3575 value? Anyway I added your modification to my fiddle, [here](https://jsfiddle.net/m89brz60/10/), and it seems that the bottom images just stick to the bottom, there being no parallax effect... I'd like to actually perceive the bottom images coming up slower or faster than scroll... – nonhocapito Jan 08 '17 at 16:12
  • I tried to apply your input in [this new fiddle](https://jsfiddle.net/m89brz60/11/), maybe this is what you meant. The script seems to work, but there are two problems: 1) I'd like the movement of the two bottom images to be more noticeable and 2) I'd like them to move independently from each other, at slightly different speeds. – nonhocapito Jan 08 '17 at 16:25
  • sorry, mistake in the link, I meant [this fiddle](https://jsfiddle.net/m89brz60/12/). – nonhocapito Jan 08 '17 at 16:26
  • It's actually working as it should, look at how the tip of the point overlaps the background blemish. http://g.recordit.co/GJr8pqAP1u.gif If they were moving at same speed they would never overlap. – Serg Chernata Jan 08 '17 at 16:29
  • I mean bgNP1 and bgNP3. While you are looking at the bgNP1 and the background fill in the middle. As I have described, there are actually 5 images involved: two at the top (mandala and blue fade), one in the middle (a body fill) and two at the bottom (mandala bgNP1 and red fade bgNP3) ... these last two actually move exactly at the same speed... the reason for this I think is that to neither I have applied scroll_speed, and arbitrary number which makes the images scroll at a particular speed. But if I divide for such a number (for example 4 or 8) the images end up slightly off the viewport. – nonhocapito Jan 08 '17 at 16:42
  • something else I noticed, is that your correction takes care of a case in which the pic at the bottom ends up higher than bottom, but it does not help if it ends up lower than bottom. If I use the `!==` operator instead of `<`, the parallax animation disappears completely. – nonhocapito Jan 08 '17 at 18:22