Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scroll function doesn't produce a smooth transition

I have 1 fixed DIV #scroll-up that is 100% height of the browser window. I have another DIV #next-prev that sits underneath it. As soon as #next-prev comes into view I want the #scroll-up DIV to begin scrolling up. My code blow does this, however as soon as #next-prev is in view #scroll-up just jumps up for no reason. The effect should be seamless.

How do I prevent the jump?

I have setup a demo of the problem on jsFiddle here: http://jsfiddle.net/sya0jr6p/1/


JS

Uses the jQuery Visible plugin to detect when #next-prev is in view.

$(window).scroll(function() {

    if ($('#next-prev').visible(true)) {
        console.log ( '#next-prev visible' );

        // Initiate scroll up of '#scroll-up' when '#next-prev' is in the viewport
        var $tagline = $('#scroll-up');
        var windowScroll;

        // Function
        function slidingTitle() {

            //Get scroll position of window
            windowScroll = $(this).scrollTop();

            $tagline.css({
                'top' : -(windowScroll/3)+"px"
            });

        }
        // Initiate
        slidingTitle();

    } else {
        console.log ( '#next-prev not visible' );
         // The element is NOT visible, do something else

    }

});

UPDATE

After further testing the jump ONLY exists when #nav-wrap isn't already on the screen. If it is on the screen then the jump doesn't occur and works as expected.

Another thing I noticed; When you first load the page AND #nav-wrap isn't already on the screen. Scroll down the page as normal until you see #scroll-up jump up and if you continue scrolling down you'll see what I want to happen. #scroll-up scrolling normally. BUT if after the jump has taken place you scroll back up the page to the very top, you'll notice the position of #scroll-up isn't the same as when you first loaded the page. Could the calculation wrong? or could the order of the code be wrong? Just a few ideas...

UPDATE

This is a animated GIF of how I want the scroll to work, the animate answers that have been given aren't intuitive:

enter image description here

like image 600
egr103 Avatar asked Sep 28 '22 02:09

egr103


1 Answers

The reason you see the jump is that you simply divide the current window scroll position once the #next-prev comes into view by 3 and set that as a top value for #scroll-up... the window scroll when you reach #next-prev will be larger than 0, hence the scroll divided by three will also be larger than zero, and #scroll-up will immediately get a (large) negative top value.

I don't know where you got the division by three from - that would create a slower scrolling of the element (aka. parallax effect) - which means that even if there was no jump and #scroll-up started to scroll at the right point, it would still cover two thirds of #next-prev once #next-prev was fully in view... So I don't think you actually want the parallax effect...

What I think you want is: The top value of #scroll-up needs to be zero until the first pixel of #next-prev scrolls into view, and then grow gradually as #next-prev comes "more" into view, making sure it stays exactly above #next-prev... ie. the top of #scroll-up needs to be 0 minus the number of pixels #next-prev's top is from the bottom of the viewport - and of course always zero if #next-prev is still completely below the viewport.

Here's a way to achieve that...

(function($) {
  $(window).scroll(function() {
    //Get the footer position relative to the document
    var footer_offset = $("#footer").position().top;
    //Get the window height (viewport height)
    var window_height = $(window).height();
    //Get the current scroll position
    var window_scroll = $(window).scrollTop();
    //Calculate the bottom of the window relative to the document
    var window_bottom_offset = window_scroll + window_height;
    //Calculate "how much" of the footer is above the bottom of the window
    var footer_visible_pixels = window_bottom_offset - footer_offset;
    //Default #semistuck's position to top: 0px
    var semistuck_top = 0;
    //But if the footer is above the bottom of the screen...
    if (footer_visible_pixels > 0) {
      //...move #semistuck up by the same amount of pixels
      semistuck_top = -footer_visible_pixels;
    }
    //Actually move it
    $("#semistuck").css('top', semistuck_top + 'px');
    //Just to see each step of the calculation
    console.log(footer_offset, window_height, window_scroll, window_bottom_offset, footer_visible_pixels, semistuck_top);
  });
}(jQuery));
body,
html {
  margin: 0;
}
#semistuck,
#loooooong,
#footer {
  margin: 0;
  padding: 4em 1em;
  text-align: center;
  box-sizing: border-box;
}
#semistuck {
  position: fixed;
  width: 50%;
  height: 100%;
  top: 0;
  left: 0;
  background: #afa;
}
#loooooong {
  height: 2000px;
  margin-left: 50%;
  width: 50%;
  background: repeating-linear-gradient(45deg, #606dbc, #606dbc 100px, #465298 100px, #465298 200px);
}
#footer {
  background: #faa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="semistuck">
  This is stuck until the<br>footer scrolls into view
</div>
<div id="loooooong"></div>
<div id="footer">
  When this gets into<br>view #semistuck will<br>get out of its way
</div>

Obviously the above code is way more verbose than needed, but I felt that very small increments in the calculation makes it clearer what happens....

Here's the same thing in a shorter form:

var footer_visible_pixels = $(window).scrollTop() + $(window).height() - $("#footer").position().top;
$("#semistuck").css('top', (footer_visible_pixels > 0 ? -footer_visible_pixels : 0) + 'px');

Hmm...

...come to think of it you may actually want the parallax effect, but just want the footer to be on top, so here's a slightly modified version which gives a somewhat cool effect:

(function($) {
  $(window).scroll(function() {
    //Get the footer position relative to the document
    var footer_offset = $("#footer").position().top;
    //Get the window height (viewport height)
    var window_height = $(window).height();
    //Get the current scroll position
    var window_scroll = $(window).scrollTop();
    //Calculate the bottom of the window relative to the document
    var window_bottom_offset = window_scroll + window_height;
    //Calculate "how much" of the footer is above the bottom of the window
    var footer_visible_pixels = window_bottom_offset - footer_offset;
    //Default #semistuck's position to top: 0px
    var semistuck_top = 0;
    //But if the footer is above the bottom of the screen...
    if (footer_visible_pixels > 0) {
      //...move #semistuck up by the same amount of pixels
      semistuck_top = -footer_visible_pixels/3;
    }
    //Actually move it
    $("#semistuck").css('top', semistuck_top + 'px');
    //Just to see each step of the calculation
    console.log(footer_offset, window_height, window_scroll, window_bottom_offset, footer_visible_pixels, semistuck_top);
  });
}(jQuery));
body,
html {
  margin: 0;
}
#semistuck,
#loooooong,
#footer {
  margin: 0;
  padding: 4em 1em;
  text-align: center;
  box-sizing: border-box;
}
#semistuck {
  position: fixed;
  width: 50%;
  height: 100%;
  top: 0;
  left: 0;
  background: #afa;
}
#loooooong {
  height: 2000px;
  margin-left: 50%;
  width: 50%;
  background: repeating-linear-gradient(45deg, #606dbc, #606dbc 100px, #465298 100px, #465298 200px);
}
#footer {
  background: #faa;
  position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="semistuck">
  This is stuck until the<br>footer scrolls into view
</div>
<div id="loooooong"></div>
<div id="footer">
  When this gets into<br>view #semistuck will<br>get out of its way
</div>

Only two things are changed compared to the original suggestion:

  1. I've added position: relative to the footer
  2. I divide the negative top of #semistuck by three

Note

None of this will work on iPhones, iPads, and probably most other touch devices. They simply don't fire any scroll events during scroll, but only after. So if support for mobile devices is a concern, you should detect those and provide a fallback - the alternative is a very jumpy effect that happens only after scrolling ends. To the best of my knowledge there is no workaround, and depending on the scenario the best way to handle it may be to:

  1. just degrade to something simpler
  2. use one of the animated solutions provided in the other answers - but only on devices that do not support continuous scroll events
like image 179
Mikk3lRo Avatar answered Oct 03 '22 03:10

Mikk3lRo